Skip to content

Commit 9b9f3f2

Browse files
authored
Improve distinct rec groups error message (#8522)
Refactor ErrorReason to be a std::variant that can carry extra information beyond an error code. Use this expanded capability to report the missing features for the error about distinct rec groups being identical after binary writing. Fixes #8519.
1 parent 7cd76de commit 9b9f3f2

7 files changed

Lines changed: 112 additions & 56 deletions

File tree

src/binaryen-c.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6541,19 +6541,19 @@ ExpressionRunnerRunAndDispose(ExpressionRunnerRef runner,
65416541

65426542
TypeBuilderErrorReason TypeBuilderErrorReasonSelfSupertype() {
65436543
return static_cast<TypeBuilderErrorReason>(
6544-
TypeBuilder::ErrorReason::SelfSupertype);
6544+
TypeBuilder::ErrorReasonKind::SelfSupertype);
65456545
}
65466546
TypeBuilderErrorReason TypeBuilderErrorReasonInvalidSupertype() {
65476547
return static_cast<TypeBuilderErrorReason>(
6548-
TypeBuilder::ErrorReason::InvalidSupertype);
6548+
TypeBuilder::ErrorReasonKind::InvalidSupertype);
65496549
}
65506550
TypeBuilderErrorReason TypeBuilderErrorReasonForwardSupertypeReference() {
65516551
return static_cast<TypeBuilderErrorReason>(
6552-
TypeBuilder::ErrorReason::ForwardSupertypeReference);
6552+
TypeBuilder::ErrorReasonKind::ForwardSupertypeReference);
65536553
}
65546554
TypeBuilderErrorReason TypeBuilderErrorReasonForwardChildReference() {
65556555
return static_cast<TypeBuilderErrorReason>(
6556-
TypeBuilder::ErrorReason::ForwardChildReference);
6556+
TypeBuilder::ErrorReasonKind::ForwardChildReference);
65576557
}
65586558

65596559
TypeBuilderRef TypeBuilderCreate(BinaryenIndex size) {
@@ -6652,7 +6652,7 @@ bool TypeBuilderBuildAndDispose(TypeBuilderRef builder,
66526652
*errorIndex = err->index;
66536653
}
66546654
if (errorReason) {
6655-
*errorReason = static_cast<TypeBuilderErrorReason>(err->reason);
6655+
*errorReason = static_cast<TypeBuilderErrorReason>(err->reason.getKind());
66566656
}
66576657
delete B;
66586658
return false;

src/wasm-type.h

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -894,7 +894,7 @@ struct TypeBuilder {
894894
void setOpen(size_t i, bool open = true);
895895
void setShared(size_t i, Shareability share = Shared);
896896

897-
enum class ErrorReason {
897+
enum class ErrorReasonKind {
898898
// There is a cycle in the supertype relation.
899899
SelfSupertype,
900900
// The declared supertype of a type is invalid.
@@ -934,6 +934,27 @@ struct TypeBuilder {
934934
RecGroupCollision,
935935
};
936936

937+
struct RecGroupCollision {
938+
FeatureSet missingFeatures;
939+
bool operator==(const RecGroupCollision& other) const {
940+
return missingFeatures == other.missingFeatures;
941+
}
942+
};
943+
944+
struct ErrorReason : std::variant<ErrorReasonKind, RecGroupCollision> {
945+
using variant::variant;
946+
947+
ErrorReasonKind getKind() const {
948+
if (auto* kind = std::get_if<ErrorReasonKind>(this)) {
949+
return *kind;
950+
}
951+
return ErrorReasonKind::RecGroupCollision;
952+
}
953+
954+
bool operator==(ErrorReasonKind kind) const { return getKind() == kind; }
955+
bool operator!=(ErrorReasonKind kind) const { return getKind() != kind; }
956+
};
957+
937958
struct Error {
938959
// The index of the type causing the failure.
939960
size_t index;
@@ -1082,7 +1103,7 @@ std::ostream& operator<<(std::ostream&, Continuation);
10821103
std::ostream& operator<<(std::ostream&, Field);
10831104
std::ostream& operator<<(std::ostream&, Struct);
10841105
std::ostream& operator<<(std::ostream&, Array);
1085-
std::ostream& operator<<(std::ostream&, TypeBuilder::ErrorReason);
1106+
std::ostream& operator<<(std::ostream&, const TypeBuilder::ErrorReason&);
10861107

10871108
// Inline some nontrivial methods here for performance reasons.
10881109

src/wasm/wasm-type.cpp

Lines changed: 72 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,49 +1503,64 @@ std::ostream& operator<<(std::ostream& os, Struct struct_) {
15031503
std::ostream& operator<<(std::ostream& os, Array array) {
15041504
return TypePrinter(os).print(array);
15051505
}
1506-
std::ostream& operator<<(std::ostream& os, TypeBuilder::ErrorReason reason) {
1506+
std::ostream& operator<<(std::ostream& os,
1507+
TypeBuilder::ErrorReasonKind reason) {
15071508
switch (reason) {
1508-
case TypeBuilder::ErrorReason::SelfSupertype:
1509+
case TypeBuilder::ErrorReasonKind::SelfSupertype:
15091510
return os << "Heap type is a supertype of itself";
1510-
case TypeBuilder::ErrorReason::InvalidSupertype:
1511+
case TypeBuilder::ErrorReasonKind::InvalidSupertype:
15111512
return os << "Heap type has an invalid supertype";
1512-
case TypeBuilder::ErrorReason::ForwardSupertypeReference:
1513+
case TypeBuilder::ErrorReasonKind::ForwardSupertypeReference:
15131514
return os << "Heap type has an undeclared supertype";
1514-
case TypeBuilder::ErrorReason::ForwardChildReference:
1515+
case TypeBuilder::ErrorReasonKind::ForwardChildReference:
15151516
return os << "Heap type has an undeclared child";
1516-
case TypeBuilder::ErrorReason::InvalidFuncType:
1517+
case TypeBuilder::ErrorReasonKind::InvalidFuncType:
15171518
return os << "Continuation has invalid function type";
1518-
case TypeBuilder::ErrorReason::InvalidSharedType:
1519+
case TypeBuilder::ErrorReasonKind::InvalidSharedType:
15191520
return os << "Shared types require shared-everything";
1520-
case TypeBuilder::ErrorReason::InvalidWaitQueue:
1521+
case TypeBuilder::ErrorReasonKind::InvalidWaitQueue:
15211522
return os << "Waitqueues require shared-everything";
1522-
case TypeBuilder::ErrorReason::InvalidStringType:
1523+
case TypeBuilder::ErrorReasonKind::InvalidStringType:
15231524
return os << "String types require strings feature";
1524-
case TypeBuilder::ErrorReason::InvalidUnsharedField:
1525+
case TypeBuilder::ErrorReasonKind::InvalidUnsharedField:
15251526
return os << "Heap type has an invalid unshared field";
1526-
case TypeBuilder::ErrorReason::NonStructDescribes:
1527+
case TypeBuilder::ErrorReasonKind::NonStructDescribes:
15271528
return os << "Describes clause on a non-struct type";
1528-
case TypeBuilder::ErrorReason::ForwardDescribesReference:
1529+
case TypeBuilder::ErrorReasonKind::ForwardDescribesReference:
15291530
return os << "Describes clause is a forward reference";
1530-
case TypeBuilder::ErrorReason::MismatchedDescribes:
1531+
case TypeBuilder::ErrorReasonKind::MismatchedDescribes:
15311532
return os << "Described type is not a matching descriptor";
1532-
case TypeBuilder::ErrorReason::NonStructDescriptor:
1533+
case TypeBuilder::ErrorReasonKind::NonStructDescriptor:
15331534
return os << "Descriptor clause on a non-struct type";
1534-
case TypeBuilder::ErrorReason::MismatchedDescriptor:
1535+
case TypeBuilder::ErrorReasonKind::MismatchedDescriptor:
15351536
return os << "Descriptor type does not describe heap type";
1536-
case TypeBuilder::ErrorReason::InvalidUnsharedDescriptor:
1537+
case TypeBuilder::ErrorReasonKind::InvalidUnsharedDescriptor:
15371538
return os << "Heap type has an invalid unshared descriptor";
1538-
case TypeBuilder::ErrorReason::InvalidUnsharedDescribes:
1539+
case TypeBuilder::ErrorReasonKind::InvalidUnsharedDescribes:
15391540
return os << "Heap type describes an invalid unshared type";
1540-
case TypeBuilder::ErrorReason::RequiresCustomDescriptors:
1541+
case TypeBuilder::ErrorReasonKind::RequiresCustomDescriptors:
15411542
return os << "custom descriptors required but not enabled";
1542-
case TypeBuilder::ErrorReason::RecGroupCollision:
1543+
case TypeBuilder::ErrorReasonKind::RecGroupCollision:
15431544
return os
15441545
<< "distinct rec groups would be identical after binary writing";
15451546
}
15461547
WASM_UNREACHABLE("Unexpected error reason");
15471548
}
15481549

1550+
std::ostream& operator<<(std::ostream& os,
1551+
const TypeBuilder::ErrorReason& reason) {
1552+
os << reason.getKind();
1553+
if (auto* collision = std::get_if<TypeBuilder::RecGroupCollision>(&reason)) {
1554+
if (collision->missingFeatures != FeatureSet(FeatureSet::None)) {
1555+
os << " (to resolve this, use";
1556+
collision->missingFeatures.iterFeatures(
1557+
[&](auto feat) { os << " --enable-" << FeatureSet::toString(feat); });
1558+
os << ")";
1559+
}
1560+
}
1561+
return os;
1562+
}
1563+
15491564
unsigned Field::getByteSize() const {
15501565
if (type != Type::i32) {
15511566
return type.getByteSize();
@@ -2413,13 +2428,13 @@ validateType(Type type, FeatureSet feats, bool isShared) {
24132428
if (type.isRef()) {
24142429
auto heapType = type.getHeapType();
24152430
if (isShared && !heapType.isShared()) {
2416-
return TypeBuilder::ErrorReason::InvalidUnsharedField;
2431+
return TypeBuilder::ErrorReasonKind::InvalidUnsharedField;
24172432
}
24182433
if (heapType.isShared() && !feats.hasSharedEverything()) {
2419-
return TypeBuilder::ErrorReason::InvalidSharedType;
2434+
return TypeBuilder::ErrorReasonKind::InvalidSharedType;
24202435
}
24212436
if (heapType.isString() && !feats.hasStrings()) {
2422-
return TypeBuilder::ErrorReason::InvalidStringType;
2437+
return TypeBuilder::ErrorReasonKind::InvalidStringType;
24232438
}
24242439
}
24252440
return std::nullopt;
@@ -2433,7 +2448,7 @@ validateStruct(const Struct& struct_, FeatureSet feats, bool isShared) {
24332448
}
24342449
if (field.packedType == Field::PackedType::WaitQueue &&
24352450
!feats.hasSharedEverything()) {
2436-
return TypeBuilder::ErrorReason::InvalidWaitQueue;
2451+
return TypeBuilder::ErrorReasonKind::InvalidWaitQueue;
24372452
}
24382453
}
24392454
return std::nullopt;
@@ -2464,10 +2479,10 @@ validateSignature(Signature sig, FeatureSet feats, bool isShared) {
24642479
std::optional<TypeBuilder::ErrorReason>
24652480
validateContinuation(Continuation cont, FeatureSet feats, bool isShared) {
24662481
if (!cont.type.isSignature()) {
2467-
return TypeBuilder::ErrorReason::InvalidFuncType;
2482+
return TypeBuilder::ErrorReasonKind::InvalidFuncType;
24682483
}
24692484
if (isShared != cont.type.isShared()) {
2470-
return TypeBuilder::ErrorReason::InvalidFuncType;
2485+
return TypeBuilder::ErrorReasonKind::InvalidFuncType;
24712486
}
24722487
return std::nullopt;
24732488
}
@@ -2480,48 +2495,48 @@ validateTypeInfo(HeapTypeInfo& info,
24802495
// The supertype must be canonical (i.e. defined in a previous rec group)
24812496
// or have already been defined in this rec group.
24822497
if (super->isTemp && !seenTypes.count(HeapType(uintptr_t(super)))) {
2483-
return TypeBuilder::ErrorReason::ForwardSupertypeReference;
2498+
return TypeBuilder::ErrorReasonKind::ForwardSupertypeReference;
24842499
}
24852500
// The supertype must have a valid structure.
24862501
if (!isValidSupertype(info, *super)) {
2487-
return TypeBuilder::ErrorReason::InvalidSupertype;
2502+
return TypeBuilder::ErrorReasonKind::InvalidSupertype;
24882503
}
24892504
}
24902505
if (auto* desc = info.described) {
24912506
if (!features.hasCustomDescriptors()) {
2492-
return TypeBuilder::ErrorReason::RequiresCustomDescriptors;
2507+
return TypeBuilder::ErrorReasonKind::RequiresCustomDescriptors;
24932508
}
24942509
if (info.kind != HeapTypeKind::Struct) {
2495-
return TypeBuilder::ErrorReason::NonStructDescribes;
2510+
return TypeBuilder::ErrorReasonKind::NonStructDescribes;
24962511
}
24972512
assert(desc->isTemp && "unexpected canonical described type");
24982513
if (!seenTypes.count(HeapType(uintptr_t(desc)))) {
2499-
return TypeBuilder::ErrorReason::ForwardDescribesReference;
2514+
return TypeBuilder::ErrorReasonKind::ForwardDescribesReference;
25002515
}
25012516
if (desc->descriptor != &info) {
2502-
return TypeBuilder::ErrorReason::MismatchedDescribes;
2517+
return TypeBuilder::ErrorReasonKind::MismatchedDescribes;
25032518
}
25042519
}
25052520
if (auto* desc = info.descriptor) {
25062521
if (!features.hasCustomDescriptors()) {
2507-
return TypeBuilder::ErrorReason::RequiresCustomDescriptors;
2522+
return TypeBuilder::ErrorReasonKind::RequiresCustomDescriptors;
25082523
}
25092524
if (info.kind != HeapTypeKind::Struct) {
2510-
return TypeBuilder::ErrorReason::NonStructDescriptor;
2525+
return TypeBuilder::ErrorReasonKind::NonStructDescriptor;
25112526
}
25122527
if (desc->described != &info) {
2513-
return TypeBuilder::ErrorReason::MismatchedDescriptor;
2528+
return TypeBuilder::ErrorReasonKind::MismatchedDescriptor;
25142529
}
25152530
}
25162531
if (info.share == Shared) {
25172532
if (!features.hasSharedEverything()) {
2518-
return TypeBuilder::ErrorReason::InvalidSharedType;
2533+
return TypeBuilder::ErrorReasonKind::InvalidSharedType;
25192534
}
25202535
if (info.described && info.described->share != Shared) {
2521-
return TypeBuilder::ErrorReason::InvalidUnsharedDescribes;
2536+
return TypeBuilder::ErrorReasonKind::InvalidUnsharedDescribes;
25222537
}
25232538
if (info.descriptor && info.descriptor->share != Shared) {
2524-
return TypeBuilder::ErrorReason::InvalidUnsharedDescriptor;
2539+
return TypeBuilder::ErrorReasonKind::InvalidUnsharedDescriptor;
25252540
}
25262541
}
25272542
bool isShared = info.share == Shared;
@@ -2636,7 +2651,7 @@ buildRecGroup(std::unique_ptr<RecGroupInfo>&& groupInfo,
26362651
for (auto child : type.getHeapTypeChildren()) {
26372652
if (isTemp(child) && !seenTypes.count(child)) {
26382653
return {TypeBuilder::Error{
2639-
i, TypeBuilder::ErrorReason::ForwardChildReference}};
2654+
i, TypeBuilder::ErrorReasonKind::ForwardChildReference}};
26402655
}
26412656
}
26422657
}
@@ -2746,8 +2761,26 @@ TypeBuilder::BuildResult TypeBuilder::build() {
27462761
auto group = (*built)[0].getRecGroup();
27472762
auto uniqueGroup = impl->unique.insertOrGet(group);
27482763
if (group != uniqueGroup) {
2764+
// There is a conflict. Find the set of missing featuers that would
2765+
// resolve the conflict if enabled.
2766+
FeatureSet missingFeatures = FeatureSet::None;
2767+
FeatureSet potential = FeatureSet::GC | FeatureSet::CustomDescriptors;
2768+
std::vector<HeapType> builtTypes = *built;
2769+
std::vector<HeapType> otherTypes(uniqueGroup.begin(),
2770+
uniqueGroup.end());
2771+
for (uint32_t x = 1; x & FeatureSet::All; x <<= 1) {
2772+
FeatureSet f(x);
2773+
if ((f & potential) && !impl->features.has(f)) {
2774+
// We have a potential missing feature. Check whether enabling it
2775+
// allows us to differentiate the rec groups.
2776+
if (RecGroupShape(builtTypes, impl->features | f) !=
2777+
RecGroupShape(otherTypes, impl->features | f)) {
2778+
missingFeatures |= f;
2779+
}
2780+
}
2781+
}
27492782
return {TypeBuilder::Error{
2750-
groupStart, TypeBuilder::ErrorReason::RecGroupCollision}};
2783+
groupStart, TypeBuilder::RecGroupCollision{missingFeatures}}};
27512784
}
27522785
}
27532786

test/gtest/type-builder.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ TEST_F(TypeTest, DirectSelfSupertype) {
215215

216216
const auto* error = result.getError();
217217
ASSERT_TRUE(error);
218-
EXPECT_EQ(error->reason, TypeBuilder::ErrorReason::ForwardSupertypeReference);
218+
EXPECT_EQ(error->reason,
219+
TypeBuilder::ErrorReasonKind::ForwardSupertypeReference);
219220
EXPECT_EQ(error->index, 0u);
220221
}
221222

@@ -233,7 +234,8 @@ TEST_F(TypeTest, IndirectSelfSupertype) {
233234

234235
const auto* error = result.getError();
235236
ASSERT_TRUE(error);
236-
EXPECT_EQ(error->reason, TypeBuilder::ErrorReason::ForwardSupertypeReference);
237+
EXPECT_EQ(error->reason,
238+
TypeBuilder::ErrorReasonKind::ForwardSupertypeReference);
237239
EXPECT_EQ(error->index, 0u);
238240
}
239241

@@ -249,7 +251,7 @@ TEST_F(TypeTest, InvalidSupertype) {
249251

250252
const auto* error = result.getError();
251253
ASSERT_TRUE(error);
252-
EXPECT_EQ(error->reason, TypeBuilder::ErrorReason::InvalidSupertype);
254+
EXPECT_EQ(error->reason, TypeBuilder::ErrorReasonKind::InvalidSupertype);
253255
EXPECT_EQ(error->index, 1u);
254256
}
255257

@@ -265,7 +267,7 @@ TEST_F(TypeTest, InvalidFinalSupertype) {
265267

266268
const auto* error = result.getError();
267269
ASSERT_TRUE(error);
268-
EXPECT_EQ(error->reason, TypeBuilder::ErrorReason::InvalidSupertype);
270+
EXPECT_EQ(error->reason, TypeBuilder::ErrorReasonKind::InvalidSupertype);
269271
EXPECT_EQ(error->index, 1u);
270272
}
271273

@@ -282,7 +284,7 @@ TEST_F(TypeTest, InvalidSharedSupertype) {
282284

283285
const auto* error = result.getError();
284286
ASSERT_TRUE(error);
285-
EXPECT_EQ(error->reason, TypeBuilder::ErrorReason::InvalidSupertype);
287+
EXPECT_EQ(error->reason, TypeBuilder::ErrorReasonKind::InvalidSupertype);
286288
EXPECT_EQ(error->index, 1u);
287289
}
288290

@@ -299,7 +301,7 @@ TEST_F(TypeTest, InvalidUnsharedSupertype) {
299301

300302
const auto* error = result.getError();
301303
ASSERT_TRUE(error);
302-
EXPECT_EQ(error->reason, TypeBuilder::ErrorReason::InvalidSupertype);
304+
EXPECT_EQ(error->reason, TypeBuilder::ErrorReasonKind::InvalidSupertype);
303305
EXPECT_EQ(error->index, 1u);
304306
}
305307

@@ -319,7 +321,7 @@ TEST_F(TypeTest, ForwardReferencedChild) {
319321

320322
const auto* error = result.getError();
321323
ASSERT_TRUE(error);
322-
EXPECT_EQ(error->reason, TypeBuilder::ErrorReason::ForwardChildReference);
324+
EXPECT_EQ(error->reason, TypeBuilder::ErrorReasonKind::ForwardChildReference);
323325
EXPECT_EQ(error->index, 1u);
324326
}
325327

test/lit/validation/type-collision-exact.wast

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
;; RUN: not wasm-opt %s -all --disable-custom-descriptors 2>&1 | filecheck %s
22

3-
;; CHECK: error: invalid type: distinct rec groups would be identical after binary writing
3+
;; CHECK: error: invalid type: distinct rec groups would be identical after binary writing (to resolve this, use --enable-custom-descriptors)
44

55
(module
66
(type $foo (struct))

test/lit/validation/type-collision-funcref.wast

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
;; RUN: not wasm-opt %s -all --disable-gc 2>&1 | filecheck %s
22

3-
;; CHECK: error: invalid type: distinct rec groups would be identical after binary writing
3+
;; CHECK: error: invalid type: distinct rec groups would be identical after binary writing (to resolve this, use --enable-gc)
44

55
(module
66
(type $foo (func))

test/lit/validation/type-collision-null.wast

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
;; RUN: not wasm-opt %s -all --disable-gc 2>&1 | filecheck %s
22

3-
;; CHECK: error: invalid type: distinct rec groups would be identical after binary writing
3+
;; CHECK: error: invalid type: distinct rec groups would be identical after binary writing (to resolve this, use --enable-gc)
44

55
(module
66
(type $A (func (param externref)))

0 commit comments

Comments
 (0)