Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
18 changes: 12 additions & 6 deletions include/wabt/type.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class Type {
I32U = 7, // Not actually specified, but used internally with load/store
};

// Used by FuncRef / ExternRef
// Used by FuncRef / ExternRef / ExnRef
enum GenericReferenceType : uint32_t {
ReferenceOrNull = 0,
ReferenceNonNull = 1,
Expand Down Expand Up @@ -105,14 +105,18 @@ class Type {
}

bool IsNullableRef() const {
return enum_ == Type::Reference || enum_ == Type::ExnRef ||
return enum_ == Type::Reference ||
enum_ == Type::RefNull ||
((enum_ == Type::ExternRef || enum_ == Type::FuncRef) && type_index_ == ReferenceOrNull);
((enum_ == Type::ExternRef || enum_ == Type::FuncRef ||
enum_ == Type::ExnRef) &&
type_index_ == ReferenceOrNull);
}

bool IsNonNullableRef() const {
return enum_ == Type::Ref ||
((enum_ == Type::ExternRef || enum_ == Type::FuncRef) && type_index_ != ReferenceOrNull);
((enum_ == Type::ExternRef || enum_ == Type::FuncRef ||
enum_ == Type::ExnRef) &&
type_index_ != ReferenceOrNull);
}

bool IsReferenceWithIndex() const { return EnumIsReferenceWithIndex(enum_); }
Expand All @@ -135,7 +139,8 @@ class Type {
case Type::V128: return "v128";
case Type::I8: return "i8";
case Type::I16: return "i16";
case Type::ExnRef: return "exnref";
case Type::ExnRef:
return type_index_ == ReferenceOrNull ? "exnref" : "(ref exn)";
case Type::Func: return "func";
case Type::Void: return "void";
case Type::Any: return "any";
Expand Down Expand Up @@ -218,7 +223,8 @@ class Type {
}

static bool EnumIsNonTypedRef(Enum value) {
return value == Type::ExternRef || value == Type::FuncRef;
return value == Type::ExternRef || value == Type::FuncRef ||
value == Type::ExnRef;
}

private:
Expand Down
9 changes: 6 additions & 3 deletions src/binary-reader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,12 @@ Result BinaryReader::ReadType(Type* out_value, const char* desc) {
if (static_cast<int64_t>(heap_type) < 0 ||
static_cast<int64_t>(heap_type) >= kInvalidIndex) {
Type::Enum heap_type_code = static_cast<Type::Enum>(heap_type);
ERROR_UNLESS(
heap_type_code == Type::FuncRef || heap_type_code == Type::ExternRef,
"Reference type is limited to func and extern: %s", desc);
ERROR_UNLESS(heap_type_code == Type::FuncRef ||
heap_type_code == Type::ExternRef ||
(heap_type_code == Type::ExnRef &&
options_.features.exceptions_enabled()),
"Reference type is limited to func, extern, and exn: %s",
desc);
type = (static_cast<Type::Enum>(type) == Type::Ref)
? Type::ReferenceNonNull
: Type::ReferenceOrNull;
Expand Down
4 changes: 3 additions & 1 deletion src/type-checker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ Result TypeChecker::CheckType(Type actual, Type expected) {
switch (actual_type) {
case Type::ExternRef:
case Type::FuncRef:
case Type::ExnRef:
return (expected.IsNullableNonTypedRef() ||
!actual.IsNullableNonTypedRef())
? Result::Ok
Expand Down Expand Up @@ -617,7 +618,8 @@ Result TypeChecker::OnBrIf(Index depth) {
}

static Type convertRefNullToRef(Type type) {
if (type == Type::ExternRef || type == Type::FuncRef) {
if (type == Type::ExternRef || type == Type::FuncRef ||
type == Type::ExnRef) {
return Type(type, Type::ReferenceNonNull);
}

Expand Down
1 change: 1 addition & 0 deletions src/validator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ static Result CheckType(Type actual, Type expected) {
switch (actual_type) {
case Type::ExternRef:
case Type::FuncRef:
case Type::ExnRef:
return (expected.IsNullableNonTypedRef() ||
!actual.IsNullableNonTypedRef())
? Result::Ok
Expand Down
43 changes: 32 additions & 11 deletions src/wast-parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -945,22 +945,43 @@ Result WastParser::ParseRefDeclaration(Var* out_type) {
EXPECT(Lpar);
EXPECT(Ref);

Type::Enum opt_type = Type::Reference;
bool saw_null = false;
if (options_->features.function_references_enabled() ||
options_->features.exceptions_enabled()) {
saw_null = Match(TokenType::Null);
}

Type::Enum opt_type = Type::Reference;
if (options_->features.function_references_enabled()) {
opt_type = Type::Ref;
opt_type = saw_null ? Type::RefNull : Type::Ref;
} else if (saw_null) {
opt_type = Type::RefNull;
}

if (Match(TokenType::Null)) {
opt_type = Type::RefNull;
if (PeekMatch(TokenType::Func) || PeekMatch(TokenType::Extern) ||
PeekMatch(TokenType::Exn)) {
Token token = Consume();
TokenType token_type = token.token_type();
Type::Enum ref_enum = Type::FuncRef;
if (token_type == TokenType::Extern) {
ref_enum = Type::ExternRef;
} else if (token_type == TokenType::Exn) {
ref_enum = Type::ExnRef;
}

if (ref_enum == Type::ExnRef && !options_->features.exceptions_enabled()) {
Error(token.loc, "value type not allowed: %s",
Type(ref_enum).GetName().c_str());
return Result::Error;
}
}

if (PeekMatch(TokenType::Func) || PeekMatch(TokenType::Extern)) {
TokenType token = Consume().token_type();
out_type->set_opt_type(token == TokenType::Func ? Type::FuncRef
: Type::ExternRef);
out_type->set_index(opt_type == Type::Ref ? Type::ReferenceNonNull
: Type::ReferenceOrNull);
out_type->set_opt_type(ref_enum);
// (ref exn) is non-null even without function-references; (ref func) /
// (ref extern) only become non-null when that proposal is enabled.
bool non_null =
(ref_enum == Type::ExnRef) ? !saw_null : (opt_type == Type::Ref);
out_type->set_index(non_null ? Type::ReferenceNonNull
: Type::ReferenceOrNull);
} else {
CHECK_RESULT(ParseVar(out_type));
out_type->set_opt_type(opt_type);
Expand Down
11 changes: 11 additions & 0 deletions test/parse/expr/bad-exn-ref-nullable.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
;;; TOOL: wat2wasm
;;; ARGS: --enable-exceptions
;;; ERROR: 1
(module
(func (param exnref) (result (ref exn))
(local.get 0)))
(;; STDERR ;;;
out/test/parse/expr/bad-exn-ref-nullable.txt:6:6: error: type mismatch in implicit return, expected [(ref exn)] but got [exnref]
(local.get 0)))
^^^^^^^^^
;;; STDERR ;;)
6 changes: 6 additions & 0 deletions test/parse/expr/exn-ref-as-non-null.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
;;; TOOL: wat2wasm
;;; ARGS: --enable-exceptions --enable-function-references
(module
(func (result (ref exn))
(ref.as_non_null (ref.null exn)))
)
12 changes: 12 additions & 0 deletions test/parse/expr/exn-ref.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
;;; TOOL: wat2wasm
;;; ARGS: --enable-exceptions
(module
(func (param (ref exn))
(throw_ref (local.get 0)))
(func (param (ref null exn))
(throw_ref (local.get 0)))
(func (param exnref)
(throw_ref (local.get 0)))
(func (result (ref exn))
(unreachable))
)
24 changes: 24 additions & 0 deletions test/roundtrip/exn-ref.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
;;; TOOL: run-roundtrip
;;; ARGS: --stdout --enable-exceptions
(module
(func $nonnull (param (ref exn))
(throw_ref (local.get 0)))
(func $nullable (param (ref null exn))
(throw_ref (local.get 0)))
(func $abbrev (param exnref)
(throw_ref (local.get 0)))
)
(;; STDOUT ;;;
(module
(type (;0;) (func (param (ref exn))))
(type (;1;) (func (param exnref)))
(func (;0;) (type 0) (param (ref exn))
local.get 0
throw_ref)
(func (;1;) (type 1) (param exnref)
local.get 0
throw_ref)
(func (;2;) (type 1) (param exnref)
local.get 0
throw_ref))
;;; STDOUT ;;)
Loading