-
-
Notifications
You must be signed in to change notification settings - Fork 661
Breaking changes in Haxe 4.0.0
function set_some(value) {
var old = some;
some = value;
rollbacks.push(function() some = old); // <-- bypasses setter now as it should
}
Use -D haxe3compat
to get warnings pointing to places that need to be fixed.
final
is a new keyword and can no longer be used as an identifier.
The Null<T>
type is now @:coreType abstract
instead of a typedef
to T
.
It still supports implicit conversion from and to T
, as well as forwarding
any field access to T
, so its basic usage shouldn't be affected at all.
However, this change affects macros. Because it's not a typedef
now and it won't be matched by the TType
constructor. Instead it's now represented as a TAbstract
instance, so if a macro
is handling Null
specifically, it needs to be changed to match TAbstract
instead of
TType
. Also if the macro is handling TAbstract
, e.g. by recursing into its underlying
type, an additional match for the Null
case might be needed, unless it's already handling
abstracts with the @:coreType
metadata.
The reason for this change is mainly to cleanup the inconsistency and confusion about Null
within compiler and macro source. That is, on most static platforms, the nullable type is
often different than a non-nullable type, for e.g. basic types (Int,Bool,etc), so having Null<T>
represented as a mere type alias (which is what typedef
is) leads to bugs and general confusion.
Moreover, having an abstract Null<T>
type sets the ground for possible future exploration regarding null-safety features, because with abstract types we can control implicit casting and variance easier.
Related PR: https://github.com/HaxeFoundation/haxe/pull/6380
A new constructor were added to the haxe.macro.Type.TypedExprDef
enum: TEnumIndex(e1:TypedExpr)
. It represents access to a enum index value and is generated by the pattern matcher.
Before this node was introduced, the pattern matcher was generating a (potentially inlined) Type.enumIndex
call that was hard to distinguish later in the optimization filters and generators.
Related PR: https://github.com/HaxeFoundation/haxe/pull/6364
Identifiers that are not bound to a known field or variable are now represented by a special haxe.macro.Type.TypedExprDef
constructor: TIdent(s:String)
. These identifiers are most often
introduced by the use of untyped
(e.g. untyped __js__
).
Before this node, such identifiers were represented by a TLocal
node pointing a fake local variable with @:unbound
metadata. This solution wasn't perfect, because compiler/macro developer would expect TLocal
to point an actually declared variable, so having to check whether a given local variable is unbound every time is error-prone.
Related PR: https://github.com/HaxeFoundation/haxe/pull/6424
The interpreter used for running scripts and executing macros has been rewritten for better performance and maintainability. There are two things to consider regarding backward compatibility in this change:
First, the target name for the interpreter is now eval
, not neko
, so if the code contains #if neko
conditional compilation implying that it also affects interpreter, that has to be changed to #if eval
or #if (neko || eval)
. The macro
define is of course still present when running macros.
Second, because of the new target now has a JIT-compilation step, it's a bit more strict about compiling code that was meant for another target. For example, if a module with your macro function also contains a method with code like untyped __js__
, the previous interpreter would not complain about it unless you actually execute that method. The new interpreter will fail at JIT-compilation stage. To fix this, the code that is not meant to run on eval should be skipped, for example by fencing it with #if <target-define>
or #if !macro
, or by moving it into a separate module that won't be loaded in the interpreter context.
Related PR: https://github.com/HaxeFoundation/haxe/pull/6319
Related blog post: http://haxe.org/blog/eval/
Haxe now expects the source files (.hx
) to be UTF-8 encoded and positions reported and expected by the compiler now contain character offsets instead of byte offsets.
This is an important change for IDE support maintainers: all display query offsets and all reported positions must be treated as character-based rather than byte-based.
This makes haxe report proper positions for source files containing multi-byte characters, such as Cyrillic, CJK and others. This also should make it easier for editors to integrate with haxe ide services, since they mostly work with character offsets as well. If the editor has the handling position conversion by e.g. encoding/decoding the file on-request, this should be disabled for Haxe 4.
Related issue: https://github.com/HaxeFoundation/haxe/issues/5163
Related PR: https://github.com/HaxeFoundation/haxe/pull/6172
If your IDE have Haxe 3.x support, you can add -D old-error-format
to keep compiler completion working using previous behavior. Other advanced completion services will not work with bytes position.
Haxe now reports both line and column number starting from 1. This is more consistent and is what a lot of editors expect when processing build output for capturing error messages.
If you're parsing the position string (e.g. main.hx:3: characters 17-25
) for further processing, please note that characters
numbers are now 1-based. This can be disabled with -D old-error-format
compiler argument.
Related PR: https://github.com/HaxeFoundation/haxe/pull/6391
Haxe 4 now supports short arrow function syntax in the form of (a, b) -> a + b
which is equivalent to function(a, b) return a + b
.
While it's an obvious feature for language users, IDE maintainers will need to add support for this new syntax. In short:
// no args
() -> expr
// with args
(arg1, arg2) -> expr
// args with type hints
(arg1:Type1, arg2:Type2) -> expr
// return type hint is not supported for arrow functions due to syntax ambiguity,
// one can use type-check syntax in the function expression
(arg1:Type) -> (expr:ReturnType)
// single-arg-no-type-hint convenience special case (no parenthesis required)
arg -> expr
Related proposal: https://github.com/HaxeFoundation/haxe-evolution/blob/master/proposals/0002-arrow-functions.md
In addition to the old Int->Void
function type syntax, Haxe 4 now has new syntax for specifying function types with support for argument names.
// no args
() -> Void
// unnamed args
(Int, String) -> Bool
// named args
(name:String) -> Void
// named, unnamed and optional args mixed
(arg1:Type1, ?arg2:Type2, Type3) -> Ret
Related proposal: https://github.com/HaxeFoundation/haxe-evolution/blob/master/proposals/0003-new-function-type.md
The classes in haxe.unit have been removed. They are still available from the hx3compat library. For more elaborate unit testing frameworks, consider using utest or munit.
The classes in haxe.remoting have been removed. They are still available from the hx3compat library.
Some occurrences of List
in the standard library have been replaced with Array
. This might require some small changes in user code.
While usage remains the same, it is no longer possible to use haxe.xml.Fast as a class (e.g. extending it or using reflection on it).
The syntax T:(A, B)
is no longer supported and has been replaced by T : A & B
. We decided to break this because there was a syntactic conflict.
See https://github.com/HaxeFoundation/haxe/pull/7127
- Static extensions no longer resolve on an implicit
this
type. This was a little-known feature which could affect resolution order in some instances (related: https://github.com/HaxeFoundation/haxe/issues/6036) - Static extensions no longer apply abstract field-casts. (https://github.com/HaxeFoundation/haxe/issues/5924)
The function
expression no longer checks if it is used as an operand in cases such as function() {} + 1
. This change was made to avoid syntactic ambiguities an some other issues, such as function() {} (expression)
being treated as a call. If a function is meant to be used this way, it can be wrapped in parentheses: (function() {}) + 1
Signature of EFunction
constructor of haxe.macro.ExprDef
enum has been changed.
In Haxe 3 it was EFunction(name:Null<String>, f:Function)
.
New signature is EFunction(kind:Null<FunctionKind>, f:Function)
, where FunctionKind
is the following enum:
enum FunctionKind {
/** Anonymous function */
FAnonymous;
/** Named local function */
FNamed(name:String, ?inlined:Bool);
/** Arrow function */
FArrow;
}
In Haxe 3 using macros it was possible to generate any invalid identifier for a type, field or variable.
Since Haxe 4 macro-generated identifiers are checked by the compiler to be valid identifiers.
For a full definition of valid identifiers refer to https://haxe.org/manual/expression.html#define-identifier
In Haxe 3 string literals were represented in abstract syntax tree as EConst(CString(str:String))
.
Since Haxe 4 CString
constructor got second argument: CString(str:String, ?kind:StringLiteralKind)
where StringLiteralKind
allows to distinguish single-quoted and double-quoted strings:
enum StringLiteralKind {
/** Strings enclosed in double quotes (e.g. "hello world") */
DoubleQuotes;
/** Strings enclosed in single quotes (e.g. 'hello world') */
SingleQuotes;
}
Classes can be passed somewhere as a value of Class<T>
. E.g. var stringClass:Class<String> = String
.
In Haxe 3.4 it was possible to pass abstract types as values too. E.g. var v:Dynamic = MyAbstractType
.
However that was a regression since 3.3.0-rc.1 because abstract types don't exist at runtime. Haxe 3.2.1 did not allow to use abstracts as values. A compile-time error was being emitted in such cases: "Cannot use abstract as value".
In Haxe 4 that error is restored.
Since Haxe 4 it's impossible to pass the result of a Void function somewhere. E.g. this was possible in Haxe 3:
[1, 2, 3].map(function(i):Void {
trace(i);
});
Since Haxe 4 such code causes an error: "Cannot use Void as value".
Use for-loops instead:
for(i in [1, 2, 3]) {
trace(i);
}
The Flash property support was reworked in (pull request).
It's now required to specify a proper Haxe property and its accessor functions in the extern class as well as mark it with the @:flash.property
metadata:
extern class FlashClass {
@:flash.property var enabled(get,set):Bool;
private function get_enabled():Bool;
private function set_enabled(v:Bool):Bool;
}
These accessor functions can be overriden just like normal Haxe functions:
class HaxeClass extends FlashClass {
override function get_enabled():Bool {
var result = super.get_enabled();
return result;
}
}
@:setter(propertyName)
and @:getter(propertyName)
are not needed anymore.
Since Haxe 4 this
is not implicitly promoted to the enum abstract type for pattern matching inside of member methods of enum abstracts.
enum abstract Volume(String) {
var Loud = "loud";
var Quiet = "quiet";
public function beep()
switch this {
case Loud: // Error: Volume should be String
default:
};
}
As a workaround use untyped cast and checktype syntax:
enum abstract Volume(String) {
var Loud = "loud";
var Quiet = "quiet";
public function beep()
switch (cast this:Volume) {
case Loud: // Works
default:
};
}
operator
and overload
are new keywords reserved for future features and can no longer be used as identifiers.
Learn Haxe: Introduction | Manual | API | Try Haxe | Haxe Library Manager | Code Cookbook
Connect: GitHub | Stack Overflow | Forum | Discord | Gitter | Twitter | Facebook