Skip to content

fix: Fix non-nullable assertion for externref-s #2337

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
ExpressionRef,
UnaryOp,
BinaryOp,
RefIsOp,
TypeRef,
FunctionRef,
ExpressionId,
Expand Down Expand Up @@ -10331,11 +10330,11 @@ export class Compiler extends DiagnosticEmitter {
case TypeKind.EXTERNREF:
case TypeKind.ANYREF:
case TypeKind.EQREF:
case TypeKind.DATAREF:
case TypeKind.I31REF: {
case TypeKind.I31REF:
case TypeKind.DATAREF: {
// Needs to be true (i.e. not zero) when the ref is _not_ null,
// which means `ref.is_null` returns false (i.e. zero).
return module.unary(UnaryOp.EqzI32, module.ref_is(RefIsOp.RefIsNull, expr));
return module.unary(UnaryOp.EqzI32, module.ref_is_null(expr));

}
default: {
Expand Down Expand Up @@ -10585,11 +10584,23 @@ export class Compiler extends DiagnosticEmitter {
var temp = flow.getTempLocal(type);
if (!flow.canOverflow(expr, type)) flow.setLocalFlag(temp.index, LocalFlags.WRAPPED);
flow.setLocalFlag(temp.index, LocalFlags.NONNULL);
expr = module.if(
module.local_tee(temp.index, expr, type.isManaged),
module.local_get(temp.index, type.toRef()),
this.makeStaticAbort(this.ensureStaticString("unexpected null"), reportNode) // TODO: throw
);
if (type.kind >= TypeKind.FUNCREF && type.kind <= TypeKind.DATAREF) {
let nonNullExpr = module.local_get(temp.index, type.toRef());
if (this.options.hasFeature(Feature.GC)) {
nonNullExpr = module.ref_as_nonnull(nonNullExpr);
}
expr = module.if(
module.ref_is_null(module.local_tee(temp.index, expr, false)),
this.makeStaticAbort(this.ensureStaticString("unexpected null"), reportNode), // TODO: throw
nonNullExpr
);
} else {
expr = module.if(
module.local_tee(temp.index, expr, type.isManaged),
module.local_get(temp.index, type.toRef()),
this.makeStaticAbort(this.ensureStaticString("unexpected null"), reportNode) // TODO: throw
);
}
flow.freeTempLocal(temp);
this.currentType = type.nonNullableType;
return expr;
Expand Down
12 changes: 12 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1774,13 +1774,25 @@ export class Module {
return binaryen._BinaryenRefIs(this.ref, op, expr);
}

ref_is_null(
expr: ExpressionRef
): ExpressionRef {
return binaryen._BinaryenRefIs(this.ref, RefIsOp.RefIsNull, expr);
}

ref_as(
op: RefAsOp,
expr: ExpressionRef
): ExpressionRef {
return binaryen._BinaryenRefAs(this.ref, op, expr);
}

ref_as_nonnull(
expr: ExpressionRef
): ExpressionRef {
return binaryen._BinaryenRefAs(this.ref, RefAsOp.RefAsNonNull, expr);
}

ref_func(
name: string,
type: TypeRef
Expand Down
3 changes: 2 additions & 1 deletion src/passes/pass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import {
_BinaryenMemoryFillGetValue,
_BinaryenMemoryFillGetSize,
_BinaryenRefIsGetValue,
_BinaryenRefAsGetValue,
_BinaryenTryGetBody,
_BinaryenTryGetNumCatchBodies,
_BinaryenTryGetCatchBodyAt,
Expand Down Expand Up @@ -989,7 +990,7 @@ export abstract class Visitor {
}
case ExpressionId.RefAs: {
this.stack.push(expr);
assert(false); // TODO
this.visit(_BinaryenRefAsGetValue(expr));
assert(this.stack.pop() == expr);
this.visitRefAs(expr);
break;
Expand Down
66 changes: 49 additions & 17 deletions tests/compiler/features/reference-types.debug.wat
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@
(global $features/reference-types/otherFuncGlobal (mut funcref) (ref.func $features/reference-types/someFunc))
(global $features/reference-types/a anyref (ref.null any))
(global $features/reference-types/b funcref (ref.null func))
(global $~lib/memory/__data_end i32 (i32.const 92))
(global $~lib/memory/__stack_pointer (mut i32) (i32.const 16476))
(global $~lib/memory/__heap_base i32 (i32.const 16476))
(global $features/reference-types/nonNullFunc (mut funcref) (ref.null func))
(global $features/reference-types/nonNullReal (mut anyref) (ref.null any))
(global $~lib/memory/__data_end i32 (i32.const 156))
(global $~lib/memory/__stack_pointer (mut i32) (i32.const 16540))
(global $~lib/memory/__heap_base i32 (i32.const 16540))
(memory $0 1)
(data (i32.const 12) "L\00\00\00\00\00\00\00\00\00\00\00\01\00\00\006\00\00\00f\00e\00a\00t\00u\00r\00e\00s\00/\00r\00e\00f\00e\00r\00e\00n\00c\00e\00-\00t\00y\00p\00e\00s\00.\00t\00s\00\00\00\00\00\00\00")
(data (i32.const 92) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00u\00n\00e\00x\00p\00e\00c\00t\00e\00d\00 \00n\00u\00l\00l\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")
(table $0 1 1 funcref)
(elem $0 (i32.const 1))
(elem declare func $features/reference-types/someFunc)
Expand All @@ -28,6 +31,8 @@
(export "internal" (func $features/reference-types/internal))
(export "a" (global $features/reference-types/a))
(export "b" (global $features/reference-types/b))
(export "nonNullFunc" (global $features/reference-types/nonNullFunc))
(export "nonNullReal" (global $features/reference-types/nonNullReal))
(export "memory" (memory $0))
(start $~start)
(func $features/reference-types/testLocal<funcref>
Expand Down Expand Up @@ -77,7 +82,7 @@
unreachable
end
)
(func $features/reference-types/testLocal<anyref>
(func $features/reference-types/testLocal<externref>
(local $0 anyref)
(local $1 anyref)
local.get $0
Expand Down Expand Up @@ -129,6 +134,7 @@
)
(func $start:features/reference-types
(local $0 funcref)
(local $1 anyref)
call $features/reference-types/somethingReal
ref.is_null
i32.eqz
Expand Down Expand Up @@ -278,7 +284,7 @@
unreachable
end
call $features/reference-types/testLocal<funcref>
call $features/reference-types/testLocal<anyref>
call $features/reference-types/testLocal<externref>
ref.func $features/reference-types/someFunc
global.set $features/reference-types/funcGlobal
global.get $features/reference-types/funcGlobal
Expand All @@ -301,20 +307,46 @@
call $~lib/builtins/abort
unreachable
end
block
ref.func $features/reference-types/someFunc
local.set $0
ref.func $features/reference-types/someFunc
local.set $0
local.get $0
ref.is_null
if
i32.const 0
i32.const 32
i32.const 88
i32.const 3
call $~lib/builtins/abort
unreachable
end
global.get $features/reference-types/otherFuncGlobal
local.tee $0
ref.is_null
if (result funcref)
i32.const 112
i32.const 32
i32.const 97
i32.const 28
call $~lib/builtins/abort
unreachable
else
local.get $0
ref.is_null
if
i32.const 0
i32.const 32
i32.const 88
i32.const 3
call $~lib/builtins/abort
unreachable
end
end
global.set $features/reference-types/nonNullFunc
call $features/reference-types/somethingReal
local.tee $1
ref.is_null
if (result anyref)
i32.const 112
i32.const 32
i32.const 98
i32.const 28
call $~lib/builtins/abort
unreachable
else
local.get $1
end
global.set $features/reference-types/nonNullReal
)
(func $features/reference-types/internal (param $0 anyref) (result anyref)
(local $1 anyref)
Expand Down
3 changes: 1 addition & 2 deletions tests/compiler/features/reference-types.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"features": [
"reference-types",
"gc"
"reference-types"
],
"asc_flags": [
]
Expand Down
22 changes: 22 additions & 0 deletions tests/compiler/features/reference-types.release.wat
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,22 @@
(global $features/reference-types/funcGlobal (mut funcref) (ref.null func))
(global $features/reference-types/a anyref (ref.null any))
(global $features/reference-types/b funcref (ref.null func))
(global $features/reference-types/nonNullFunc (mut funcref) (ref.null func))
(global $features/reference-types/nonNullReal (mut anyref) (ref.null any))
(memory $0 1)
(data (i32.const 1036) "L")
(data (i32.const 1048) "\01\00\00\006\00\00\00f\00e\00a\00t\00u\00r\00e\00s\00/\00r\00e\00f\00e\00r\00e\00n\00c\00e\00-\00t\00y\00p\00e\00s\00.\00t\00s")
(data (i32.const 1116) "<")
(data (i32.const 1128) "\01\00\00\00\1e\00\00\00u\00n\00e\00x\00p\00e\00c\00t\00e\00d\00 \00n\00u\00l\00l")
(elem declare func $features/reference-types/someFunc)
(export "external" (func $features/reference-types/external))
(export "somethingReal" (func $features/reference-types/somethingReal))
(export "somethingNull" (func $features/reference-types/somethingNull))
(export "internal" (func $features/reference-types/internal))
(export "a" (global $features/reference-types/a))
(export "b" (global $features/reference-types/b))
(export "nonNullFunc" (global $features/reference-types/nonNullFunc))
(export "nonNullReal" (global $features/reference-types/nonNullReal))
(export "memory" (memory $0))
(start $~start)
(func $features/reference-types/someFunc
Expand All @@ -32,6 +38,7 @@
call $features/reference-types/external
)
(func $~start
(local $0 anyref)
call $features/reference-types/somethingReal
ref.is_null
if
Expand Down Expand Up @@ -89,5 +96,20 @@
global.set $features/reference-types/funcGlobal
ref.func $features/reference-types/someFunc
global.set $features/reference-types/funcGlobal
ref.func $features/reference-types/someFunc
global.set $features/reference-types/nonNullFunc
call $features/reference-types/somethingReal
local.tee $0
ref.is_null
if
i32.const 1136
i32.const 1056
i32.const 98
i32.const 28
call $~lib/builtins/abort
unreachable
end
local.get $0
global.set $features/reference-types/nonNullReal
)
)
28 changes: 16 additions & 12 deletions tests/compiler/features/reference-types.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
// can use anyref as a parameter or return type

export declare function external(a: anyref): anyref;
export declare function somethingReal(): anyref;
export declare function somethingNull(): anyref;
export declare function external(a: externref): externref;
export declare function somethingReal(): externref;
export declare function somethingNull(): externref;

export function internal(a: anyref): anyref {
export function internal(a: externref): externref {
const b = external(a);
let c = external(b);
var d = external(c);
return d;
}

// Truthiness conversion
if(!somethingReal()) {
if (!somethingReal()) {
assert(false);
}
if(!somethingNull()) {
if (!somethingNull()) {
// nop
} else {
assert(false);
}
if(somethingReal()) {
if (somethingReal()) {
// nop
} else {
assert(false);
}
if(somethingNull()) {
if (somethingNull()) {
assert(false);
}

Expand Down Expand Up @@ -58,11 +58,11 @@ assert(!funcGlobal);
var funcGlobalInit: funcref = null;
assert(!funcGlobalInit);

var anyGlobal: anyref;
var anyGlobal: externref;
assert(!anyGlobal);
anyGlobal = null;
assert(!anyGlobal);
var anyGlobalInit: anyref = null;
var anyGlobalInit: externref = null;
assert(!anyGlobalInit);

function testLocal<T>(): void {
Expand All @@ -74,7 +74,7 @@ function testLocal<T>(): void {
assert(!localInit);
}
testLocal<funcref>();
testLocal<anyref>();
testLocal<externref>();

// funcref can represent function references

Expand All @@ -90,5 +90,9 @@ assert(otherFuncGlobal);

// constant globals

export const a: anyref = null;
export const a: externref = null;
export const b: funcref = null;

// non-null assertions
export const nonNullFunc = otherFuncGlobal!;
export const nonNullReal = somethingReal()!;