Skip to content

Commit

Permalink
Fix ?? inference and precedence (#11252)
Browse files Browse the repository at this point in the history
  • Loading branch information
RblSb authored and kLabz committed Sep 1, 2023
1 parent 0f45df6 commit 39960a1
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 21 deletions.
13 changes: 7 additions & 6 deletions src/syntax/parser.ml
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,13 @@ let precedence op =
| OpAdd | OpSub -> 3, left
| OpShl | OpShr | OpUShr -> 4, left
| OpOr | OpAnd | OpXor -> 5, left
| OpEq | OpNotEq | OpGt | OpLt | OpGte | OpLte -> 6, left
| OpInterval -> 7, left
| OpBoolAnd -> 8, left
| OpBoolOr | OpNullCoal -> 9, left
| OpArrow -> 10, right
| OpAssign | OpAssignOp _ -> 11, right
| OpNullCoal -> 6, left
| OpEq | OpNotEq | OpGt | OpLt | OpGte | OpLte -> 7, left
| OpInterval -> 8, left
| OpBoolAnd -> 9, left
| OpBoolOr -> 10, left
| OpArrow -> 11, right
| OpAssign | OpAssignOp _ -> 12, right

let is_higher_than_ternary = function
| OpAssign | OpAssignOp _ | OpArrow -> false
Expand Down
15 changes: 10 additions & 5 deletions src/typing/typer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1892,19 +1892,24 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) =
let vr = new value_reference ctx in
let e1 = type_expr ctx (Expr.ensure_block e1) with_type in
let e2 = type_expr ctx (Expr.ensure_block e2) (WithType.with_type e1.etype) in
let e1 = vr#as_var "tmp" {e1 with etype = ctx.t.tnull e1.etype} in
let tmin = unify_min ctx [e1; e2] in
let e1 = vr#as_var "tmp" {e1 with etype = ctx.t.tnull tmin} in
let e_null = Builder.make_null e1.etype e1.epos in
let e_cond = mk (TBinop(OpNotEq,e1,e_null)) ctx.t.tbool e1.epos in

let follow_null_once t =
let rec follow_null t =
match t with
| TAbstract({a_path = [],"Null"},[t]) -> t
| TAbstract({a_path = [],"Null"},[t]) -> follow_null t
| _ -> t
in
let iftype = if DeadEnd.has_dead_end e2 then
WithType.with_type (follow_null_once e1.etype)
WithType.with_type (follow_null e1.etype)
else
WithType.WithType(e2.etype,None)
let t = match e2.etype with
| TAbstract({a_path = [],"Null"},[t]) -> tmin
| _ -> follow_null tmin
in
WithType.with_type t
in
let e_if = make_if_then_else ctx e_cond e1 e2 iftype p in
vr#to_texpr e_if
Expand Down
4 changes: 3 additions & 1 deletion tests/unit/src/unit/TestMain.hx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ function main() {
new TestCasts(),
new TestSyntaxModule(),
new TestNull(),
new TestNullCoalescing(),
new TestNumericCasts(),
new TestHashMap(),
new TestRest(),
Expand Down Expand Up @@ -107,7 +108,8 @@ function main() {
new TestOverloadsForEveryone(),
new TestInterface(),
new TestNaN(),
#if ((dce == "full") && !interp) new TestDCE(),
#if ((dce == "full") && !interp)
new TestDCE(),
#end
new TestMapComprehension(),
new TestMacro(),
Expand Down
40 changes: 31 additions & 9 deletions tests/unit/src/unit/TestNullCoalescing.hx
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
package unit;

private class A {}
private class B extends A {}
private class C extends A {}

@:nullSafety(StrictThreaded)
class TestNullCoalescing extends Test {
final nullInt:Null<Int> = null;
final nullFloat:Null<Float> = null;
final nullBool:Null<Bool> = null;
final nullString:Null<String> = null;

var count = 0;

function call() {
count++;
return "_";
}

function test() {
eq(true, 0 != 1 ?? 2);
var a = call() ?? "default";
eq(count, 1);

Expand All @@ -30,9 +37,10 @@ class TestNullCoalescing extends Test {
final s:Int = if (nullInt == null) 2; else nullInt;
final s = nullInt ?? 2;

// $type(testBool); // Bool
// $type(testNullBool); // Null<Bool>
// $type(s); // Int
f(HelperMacros.isNullable(testBool));
t(HelperMacros.isNullable(testNullBool));
f(HelperMacros.isNullable(s));
// nullsafety filter test:
final shouldBeBool:Bool = testBool;
if (testNullBool == null) {}
final shouldBeInt:Int = s;
Expand All @@ -54,10 +62,7 @@ class TestNullCoalescing extends Test {
eq(arr[1], 1);
eq(arr[2], 1);

final arr = [
nullInt ?? 2,
2
];
final arr = [nullInt ?? 2, 2];
eq(arr[0], arr[1]);

var a = [0 => nullInt ?? 0 + 100];
Expand Down Expand Up @@ -110,7 +115,8 @@ class TestNullCoalescing extends Test {
}
eq(item(1) ?? item(2) ?? item(3), 1);
eq(arr.length, 1);
for (i => v in [1]) eq(arr[i], v);
for (i => v in [1])
eq(arr[i], v);

final arr = [];
function item(n) {
Expand All @@ -119,6 +125,22 @@ class TestNullCoalescing extends Test {
}
eq(item(1) ?? item(2) ?? item(3), null);
eq(arr.length, 3);
for (i => v in [1, 2, 3]) eq(arr[i], v);
for (i => v in [1, 2, 3])
eq(arr[i], v);

var b:B = cast null;
var c:C = cast null;
var a = if (b != null) b else c;
var a = b ?? c;
eq("unit._TestNullCoalescing.A", HelperMacros.typeString(a));

var nullF = false ? nullFloat : 0;
var nullF2 = nullFloat ?? nullInt;
var notNullF = nullFloat ?? 0;
var notNullF2 = (1 : Null<Float>) ?? throw "";
t(HelperMacros.isNullable(nullF));
t(HelperMacros.isNullable(nullF2));
f(HelperMacros.isNullable(notNullF));
f(HelperMacros.isNullable(notNullF2));
}
}

0 comments on commit 39960a1

Please sign in to comment.