Skip to content

nr2.0: Fix multiple bindings #3702

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 8 commits into from
Apr 24, 2025
Merged
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
12 changes: 10 additions & 2 deletions gcc/rust/ast/rust-ast-visitor.cc
Original file line number Diff line number Diff line change
@@ -831,6 +831,13 @@ DefaultASTVisitor::visit (AST::UseDeclaration &use_decl)
visit (use_decl.get_tree ());
}

void
DefaultASTVisitor::visit_function_params (AST::Function &function)
{
for (auto &param : function.get_function_params ())
visit (param);
}

void
DefaultASTVisitor::visit (AST::Function &function)
{
@@ -839,8 +846,9 @@ DefaultASTVisitor::visit (AST::Function &function)
visit (function.get_qualifiers ());
for (auto &generic : function.get_generic_params ())
visit (generic);
for (auto &param : function.get_function_params ())
visit (param);

visit_function_params (function);

if (function.has_return_type ())
visit (function.get_return_type ());
if (function.has_where_clause ())
2 changes: 2 additions & 0 deletions gcc/rust/ast/rust-ast-visitor.h
Original file line number Diff line number Diff line change
@@ -242,6 +242,8 @@ class ASTVisitor
class DefaultASTVisitor : public ASTVisitor
{
public:
virtual void visit_function_params (AST::Function &function);

virtual void visit (AST::Crate &crate);

virtual void visit (AST::Token &tok) override;
18 changes: 18 additions & 0 deletions gcc/rust/ast/rust-ast.h
Original file line number Diff line number Diff line change
@@ -57,6 +57,11 @@ class Identifier

bool empty () const { return ident.empty (); }

bool operator== (const Identifier &other) const
{
return ident == other.ident;
}

private:
std::string ident;
location_t loc;
@@ -2097,6 +2102,19 @@ template <> struct less<Rust::Identifier>
return lhs.as_string () < rhs.as_string ();
}
};

template <> struct hash<Rust::Identifier>
{
std::size_t operator() (const Rust::Identifier &k) const
{
using std::hash;
using std::size_t;
using std::string;

return hash<string> () (k.as_string ()) ^ (hash<int> () (k.get_locus ()));
}
};

} // namespace std

#endif
135 changes: 130 additions & 5 deletions gcc/rust/resolve/rust-late-name-resolver-2.0.cc
Original file line number Diff line number Diff line change
@@ -128,6 +128,54 @@ Late::new_label (Identifier name, NodeId id)
rust_assert (ok);
}

void
Late::visit (AST::ForLoopExpr &expr)
{
visit_outer_attrs (expr);

ctx.bindings.enter (BindingSource::For);

visit (expr.get_pattern ());

ctx.bindings.exit ();

visit (expr.get_iterator_expr ());
visit (expr.get_loop_label ());
visit (expr.get_loop_block ());
}

void
Late::visit (AST::IfLetExpr &expr)
{
visit_outer_attrs (expr);

ctx.bindings.enter (BindingSource::Let);

for (auto &pattern : expr.get_patterns ())
visit (pattern);

ctx.bindings.exit ();

visit (expr.get_value_expr ());
visit (expr.get_if_block ());
}

void
Late::visit (AST::MatchArm &arm)
{
visit_outer_attrs (arm);

ctx.bindings.enter (BindingSource::Match);

for (auto &pattern : arm.get_patterns ())
visit (pattern);

ctx.bindings.exit ();

if (arm.has_match_arm_guard ())
visit (arm.get_guard_expr ());
}

void
Late::visit (AST::LetStmt &let)
{
@@ -138,8 +186,13 @@ Late::visit (AST::LetStmt &let)
// this makes variable shadowing work properly
if (let.has_init_expr ())
visit (let.get_init_expr ());

ctx.bindings.enter (BindingSource::Let);

visit (let.get_pattern ());

ctx.bindings.exit ();

if (let.has_else_expr ())
visit (let.get_init_expr ());

@@ -167,9 +220,60 @@ Late::visit (AST::IdentifierPattern &identifier)
// but values does not allow shadowing... since functions cannot shadow
// do we insert functions in labels as well?

// We do want to ignore duplicated data because some situations rely on it.
std::ignore = ctx.values.insert_shadowable (identifier.get_ident (),
identifier.get_node_id ());
if (ctx.bindings.peek ().is_and_bound (identifier.get_ident ()))
{
if (ctx.bindings.peek ().get_source () == BindingSource::Param)
rust_error_at (
identifier.get_locus (), ErrorCode::E0415,
"identifier %qs is bound more than once in the same parameter list",
identifier.as_string ().c_str ());
else
rust_error_at (
identifier.get_locus (), ErrorCode::E0416,
"identifier %qs is bound more than once in the same pattern",
identifier.as_string ().c_str ());
return;
}

ctx.bindings.peek ().insert_ident (identifier.get_ident ());

if (ctx.bindings.peek ().is_or_bound (identifier.get_ident ()))
{
// FIXME: map usage instead
std::ignore = ctx.values.insert_shadowable (identifier.get_ident (),
identifier.get_node_id ());
}
else
{
// We do want to ignore duplicated data because some situations rely on
// it.
std::ignore = ctx.values.insert_shadowable (identifier.get_ident (),
identifier.get_node_id ());
}
}

void
Late::visit (AST::AltPattern &pattern)
{
ctx.bindings.peek ().push (Binding::Kind::Or);
for (auto &alt : pattern.get_alts ())
{
ctx.bindings.peek ().push (Binding::Kind::Product);
visit (alt);
ctx.bindings.peek ().merge ();
}
ctx.bindings.peek ().merge ();
}

void
Late::visit_function_params (AST::Function &function)
{
ctx.bindings.enter (BindingSource::Param);

for (auto &param : function.get_function_params ())
visit (param);

ctx.bindings.exit ();
}

void
@@ -525,14 +629,35 @@ void
Late::visit (AST::ClosureExprInner &closure)
{
add_captures (closure, ctx);
DefaultResolver::visit (closure);

visit_outer_attrs (closure);

ctx.bindings.enter (BindingSource::Param);

for (auto &param : closure.get_params ())
visit (param);

ctx.bindings.exit ();

visit (closure.get_definition_expr ());
}

void
Late::visit (AST::ClosureExprInnerTyped &closure)
{
add_captures (closure, ctx);
DefaultResolver::visit (closure);

visit_outer_attrs (closure);

ctx.bindings.enter (BindingSource::Param);

for (auto &param : closure.get_params ())
visit (param);

ctx.bindings.exit ();

visit (closure.get_return_type ());
visit (closure.get_definition_block ());
}

} // namespace Resolver2_0
7 changes: 7 additions & 0 deletions gcc/rust/resolve/rust-late-name-resolver-2.0.h
Original file line number Diff line number Diff line change
@@ -37,13 +37,20 @@ class Late : public DefaultResolver

void new_label (Identifier name, NodeId id);

// Specialized visit bits
void visit_function_params (AST::Function &function) override;

// some more label declarations
void visit (AST::LetStmt &) override;
// TODO: Do we need this?
// void visit (AST::Method &) override;
void visit (AST::IdentifierPattern &) override;
void visit (AST::StructPatternFieldIdent &) override;
void visit (AST::AltPattern &) override;
void visit (AST::SelfParam &) override;
void visit (AST::MatchArm &) override;
void visit (AST::ForLoopExpr &) override;
void visit (AST::IfLetExpr &) override;

// resolutions
void visit (AST::IdentifierExpr &) override;
59 changes: 59 additions & 0 deletions gcc/rust/resolve/rust-name-resolution-context.cc
Original file line number Diff line number Diff line change
@@ -23,6 +23,65 @@
namespace Rust {
namespace Resolver2_0 {

BindingLayer::BindingLayer (BindingSource source) : source (source)
{
push (Binding::Kind::Product);
}

bool
BindingLayer::bind_test (Identifier ident, Binding::Kind kind)
{
for (auto &bind : bindings)
{
if (bind.set.find (ident) != bind.set.cend () && bind.kind == kind)
{
return true;
}
}
return false;
}

void
BindingLayer::push (Binding::Kind kind)
{
bindings.push_back (Binding (kind));
}

bool
BindingLayer::is_and_bound (Identifier ident)
{
return bind_test (ident, Binding::Kind::Product);
}

bool
BindingLayer::is_or_bound (Identifier ident)
{
return bind_test (ident, Binding::Kind::Or);
}

void
BindingLayer::insert_ident (Identifier ident)
{
bindings.back ().set.insert (ident);
}

void
BindingLayer::merge ()
{
auto last_binding = bindings.back ();
bindings.pop_back ();
for (auto &value : last_binding.set)
{
bindings.back ().set.insert (value);
}
}

BindingSource
BindingLayer::get_source () const
{
return source;
}

NameResolutionContext::NameResolutionContext ()
: mappings (Analysis::Mappings::get ())
{}
Loading