Skip to content
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

Fix reference type processing #2571

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

zherczeg
Copy link
Contributor

Parse (ref index) form, not just (ref $name) form
Resolve references in types and locals
Display location when a named reference is not found

@zherczeg
Copy link
Contributor Author

This is another bugfix patch extracted from #2562. The list of changes are above, these constructs should work even in the main branch.

@@ -973,11 +1008,16 @@ Result WastParser::ParseValueTypeList(
CHECK_RESULT(ParseValueType(&type));

if (type.is_index()) {
out_type_list->push_back(Type(type.index()));
// TODO: Temporary fix.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The full fix is in #2562. It is unlikely that close to UINT32_MAX number of types are defined, so this is good as a temporary change.

func->GetNumParams(),
decl->sig.param_type_names, errors);

func->local_types.Set(func->local_type_list);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a delayed processing of locals. The reasion is that otherwise we cannot tell that (ref $t) and (ref 0) refers to the same or different locals, so they can be merged or not.

src/ir.cc Outdated
@@ -181,7 +181,7 @@ void LocalTypes::Set(const TypeVector& types) {
Type type = types[0];
Index count = 1;
for (Index i = 1; i < types.size(); ++i) {
if (types[i] != type) {
if (!types[i].IsSame(type)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The name might not be the best. Anyway, the purpose is that == only compares the enum, while the index is also important for references.

Copy link
Member

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

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

Thanks for working on this.

Mostly LGTM, just trying to page in how this stuff all works.

@@ -259,8 +259,8 @@ struct FuncSignature {
// So to use this type we need to translate its name into
// a proper index from the module type section.
// This is the mapping from parameter/result index to its name.
Copy link
Member

Choose a reason for hiding this comment

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

Comment needs updating maybe?

@@ -79,6 +79,11 @@ class Type {
return IsRef();
}

// The == comparison only compares the enum_ member.
bool IsSame(const Type& rhs) const {
Copy link
Member

Choose a reason for hiding this comment

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

Should we instead update operator==?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately I get errors such as error: ambiguous overload for ‘operator==’ (operand types are ‘const wabt::Type’ and ‘wabt::Type::Enum’)

The reason is the Type has a constexpr operator Enum() const { return enum_; }, and this confuses the compiler.

I am open to any ideas.

Copy link
Collaborator

Choose a reason for hiding this comment

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

how much would break if you made that explicit...

Copy link
Member

Choose a reason for hiding this comment

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

This seems to work for me locally:

   constexpr operator Enum() const { return enum_; }
 
+  // TODO(sbc) Use =default here once we have C++20
+  bool operator==(const Type& other) const {
+    return enum_ == other.enum_ && type_index_ == other.type_index_;
+  }
+
+  bool operator==(const Enum& other) const { return enum_ == other; }

I tried the explicit route too and that seems to break a bunch of places.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I change it. Thank you

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Btw it might fix another bug. Currently if we have two types which are the same except the ref part:

(type $t0 (func (param (ref $t0))))
(type $t1 (func (param (ref $t1))))

(func $f (param (ref $t1)) )

$f gets type 0, instead of type 1. The index is simply ignored.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seems it does not work :(

if (types[i] != type) { still uses the enum auto conversion.
if (!types[i].operator==(type)) { works but does not look nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What shall I do?

Copy link
Member

Choose a reason for hiding this comment

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

I guess removing the implicit cast is the way to go.. although as you say it might require a bunch of downstream changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried to move the operator== before the Enum casting, and the auto casting still takes precedence. I will never understand c++.

Removing the casting might have many side effects which is too big risk for this patch, so I move back to the original code for now, and we can try it later.

@@ -137,7 +137,8 @@ class WastParser {
Result ParseValueType(Var* out_type);
Result ParseValueTypeList(
TypeVector* out_type_list,
std::unordered_map<uint32_t, std::string>* type_names);
std::unordered_map<uint32_t, Var>* type_names,
Copy link
Member

Choose a reason for hiding this comment

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

Maybe should be renamed? type_vars?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All ...type_names are renamed to ...type_vars.

out/test/typecheck/missing-ref.txt:5:24: error: Unknown reference: $first_type
(func $f (param (ref $first_type))
^^^^^^^^^^^
out/test/typecheck/missing-ref.txt:6:25: error: Unknown reference: $second_type
Copy link
Member

Choose a reason for hiding this comment

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

Looking at the errors that come from resolve-names.cc It looks like this should be undefined reference type variable \"%s\"".. or something like that?

Copy link
Contributor Author

@zherczeg zherczeg Mar 26, 2025

Choose a reason for hiding this comment

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

I changed it to undefined reference type name %s

@@ -457,7 +474,7 @@ class ResolveFuncTypesExprVisitorDelegate : public ExprVisitor::DelegateNop {
: module_(module), errors_(errors) {}

void ResolveBlockDeclaration(const Location& loc, BlockDeclaration* decl) {
ResolveTypeNames(*module_, decl);
ResolveTypeNames(*module_, decl->sig, errors_);
Copy link
Member

Choose a reason for hiding this comment

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

How is this different to the code in src/wast-parser.cc that does name resolving?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure I understand this question. ResolveTypeNames is reworked to support error throwing.

@zherczeg zherczeg force-pushed the ref_fix branch 2 times, most recently from 1207345 to a4e230c Compare March 26, 2025 18:43
out/test/typecheck/missing-ref.txt:5:24: error: undefined reference type name $first_type
(func $f (param (ref $first_type))
^^^^^^^^^^^
out/test/typecheck/missing-ref.txt:6:25: error: undefined reference type name $second_type
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just a note: the error is thrown twice. The reason is that the references are resolved as func type and as a function signature. I don't think it is a problem, but we could fix it (could be later) by removing the items of param_type_vars / result_type_vars, if somebody does not like it.

@zherczeg zherczeg force-pushed the ref_fix branch 3 times, most recently from 946e527 to 74078be Compare March 27, 2025 05:12
@zherczeg
Copy link
Contributor Author

Did some code improvements. The unordered_map is replaced by a vector, because it is a simpler structure. Furthermore, when a reference is resolved, its index is set to kInvalidIndex to avoid duplicated resolves.

@zherczeg zherczeg force-pushed the ref_fix branch 3 times, most recently from d403868 to 3fe420e Compare March 27, 2025 05:25
Parse (ref index) form, not just (ref $name) form
Resolve references in types and locals
Display location when a named reference is not found
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants