Skip to content
Merged
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
2 changes: 2 additions & 0 deletions src/wasm-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,8 @@ struct TypeBuilder {
ForwardChildReference,
// A continuation reference that does not refer to a function type.
InvalidFuncType,
// A non-shared field of a shared heap type.
InvalidUnsharedField,
};

struct Error {
Expand Down
69 changes: 51 additions & 18 deletions src/wasm/wasm-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1697,6 +1697,8 @@ std::ostream& operator<<(std::ostream& os, TypeBuilder::ErrorReason reason) {
return os << "Heap type has an undeclared child";
case TypeBuilder::ErrorReason::InvalidFuncType:
return os << "Continuation has invalid function type";
case TypeBuilder::ErrorReason::InvalidUnsharedField:
return os << "Heap type has an invalid unshared field";
}
WASM_UNREACHABLE("Unexpected error reason");
}
Expand Down Expand Up @@ -2628,6 +2630,53 @@ bool isValidSupertype(const HeapTypeInfo& sub, const HeapTypeInfo& super) {
WASM_UNREACHABLE("unknown kind");
}

std::optional<TypeBuilder::ErrorReason>
validateType(HeapTypeInfo& info, std::unordered_set<HeapType>& seenTypes) {
if (auto* super = info.supertype) {
// The supertype must be canonical (i.e. defined in a previous rec group)
// or have already been defined in this rec group.
if (super->isTemp && !seenTypes.count(HeapType(uintptr_t(super)))) {
return TypeBuilder::ErrorReason::ForwardSupertypeReference;
}
// The supertype must have a valid structure.
if (!isValidSupertype(info, *super)) {
return TypeBuilder::ErrorReason::InvalidSupertype;
}
}
if (info.isContinuation()) {
if (!info.continuation.type.isSignature()) {
return TypeBuilder::ErrorReason::InvalidFuncType;
}
}
if (info.share == Shared) {
switch (info.kind) {
case HeapTypeInfo::SignatureKind:
// TODO: Figure out and enforce shared function rules.
break;
case HeapTypeInfo::ContinuationKind:
if (!info.continuation.type.isShared()) {
return TypeBuilder::ErrorReason::InvalidFuncType;
}
break;
case HeapTypeInfo::StructKind:
for (auto& field : info.struct_.fields) {
if (field.type.isRef() && !field.type.getHeapType().isShared()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems surprising this only applies to references, when we have shared Memories for example, so we do have shared i32s etc. But I guess they must be referred to indirectly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we don't have separate shared i32 as a type. i32 and other non-reference types validate just fine in both shared and unshared contexts.

return TypeBuilder::ErrorReason::InvalidUnsharedField;
}
}
break;
case HeapTypeInfo::ArrayKind: {
auto elem = info.array.element.type;
if (elem.isRef() && !elem.getHeapType().isShared()) {
return TypeBuilder::ErrorReason::InvalidUnsharedField;
}
break;
}
}
}
return std::nullopt;
}

void updateReferencedHeapTypes(
std::unique_ptr<HeapTypeInfo>& info,
const std::unordered_map<HeapType, HeapType>& canonicalized) {
Expand Down Expand Up @@ -2695,24 +2744,8 @@ buildRecGroup(std::unique_ptr<RecGroupInfo>&& groupInfo,
std::unordered_set<HeapType> seenTypes;
for (size_t i = 0; i < typeInfos.size(); ++i) {
auto& info = typeInfos[i];
if (auto* super = info->supertype) {
// The supertype must be canonical (i.e. defined in a previous rec group)
// or have already been defined in this rec group.
if (super->isTemp && !seenTypes.count(HeapType(uintptr_t(super)))) {
return {TypeBuilder::Error{
i, TypeBuilder::ErrorReason::ForwardSupertypeReference}};
}
// The supertype must have a valid structure.
if (!isValidSupertype(*info, *super)) {
return {
TypeBuilder::Error{i, TypeBuilder::ErrorReason::InvalidSupertype}};
}
}
if (info->isContinuation()) {
if (!info->continuation.type.isSignature()) {
return {
TypeBuilder::Error{i, TypeBuilder::ErrorReason::InvalidFuncType}};
}
if (auto err = validateType(*info, seenTypes)) {
return {TypeBuilder::Error{i, *err}};
}
seenTypes.insert(asHeapType(info));
}
Expand Down
8 changes: 4 additions & 4 deletions test/lit/passes/type-merging-shared.wast
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@
(module
;; Shared and unshared basic heap types similarly cannot be merged.
;; CHECK: (rec
;; CHECK-NEXT: (type $A' (shared (struct (field anyref))))
;; CHECK-NEXT: (type $A' (struct (field anyref)))

;; CHECK: (type $A (shared (struct (field (ref null (shared any))))))
(type $A (shared (struct (ref null (shared any)))))
(type $A' (shared (struct (ref null any))))
;; CHECK: (type $A (struct (field (ref null (shared any)))))
(type $A (struct (ref null (shared any))))
(type $A' (struct (ref null any)))

;; CHECK: (type $2 (func))

Expand Down
69 changes: 69 additions & 0 deletions test/spec/shared-array.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
;; Shared array declaration syntax
(module
(type (shared (array i8)))
(type (sub final (shared (array i8))))
(rec
(type (sub final (shared (array i8))))
)

(global (ref 0) (array.new_default 1 (i32.const 1)))
(global (ref 1) (array.new_default 2 (i32.const 1)))
(global (ref 2) (array.new_default 0 (i32.const 1)))
)

;; Shared arrays are distinct from non-shared arrays
(assert_invalid
(module
(type (shared (array i8)))
(type (array i8))

(global (ref 0) (array.new_default 1 (i32.const 1)))
)
"not a subtype"
)

(assert_invalid
(module
(type (shared (array i8)))
(type (array i8))

(global (ref 1) (array.new 0))
)
"not a subtype"
)

;; Shared arrays may not be subtypes of non-shared arrays
(assert_invalid
(module
(type (sub (array i8)))
(type (sub 0 (shared (array i8))))
)
"invalid supertype"
)

;; Non-shared arrays may not be subtypes of shared arrays
(assert_invalid
(module
(type (sub (shared (array i8))))
(type (sub 0 (array i8)))
)
"invalid supertype"
)

;; Shared arrays may not contain non-shared references
(assert_invalid
(module
(type (shared (array anyref)))
)
"invalid field"
)

;; But they may contain shared references
(module
(type (shared (array (ref null (shared any)))))
)

;; Non-shared arrays may contain shared references
(module
(type (array (ref null (shared any))))
)
69 changes: 69 additions & 0 deletions test/spec/shared-struct.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
;; Shared struct declaration syntax
(module
(type (shared (struct)))
(type (sub final (shared (struct))))
(rec
(type (sub final (shared (struct))))
)

(global (ref 0) (struct.new 1))
(global (ref 1) (struct.new 2))
(global (ref 2) (struct.new 0))
)

;; Shared structs are distinct from non-shared structs
(assert_invalid
(module
(type (shared (struct)))
(type (struct))

(global (ref 0) (struct.new 1))
)
"not a subtype"
)

(assert_invalid
(module
(type (shared (struct)))
(type (struct))

(global (ref 1) (struct.new 0))
)
"not a subtype"
)

;; Shared structs may not be subtypes of non-shared structs
(assert_invalid
(module
(type (sub (struct)))
(type (sub 0 (shared (struct))))
)
"invalid supertype"
)

;; Non-shared structs may not be subtypes of shared structs
(assert_invalid
(module
(type (sub (shared (struct))))
(type (sub 0 (struct)))
)
"invalid supertype"
)

;; Shared structs may not contain non-shared references
(assert_invalid
(module
(type (shared (struct anyref)))
)
"invalid field"
)

;; But they may contain shared references
(module
(type (shared (struct (ref null (shared any)))))
)

;; Non-shared structs may contain shared references
(module
(type (struct (ref null (shared any))))
)