]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: resolve: Handle multiple bindings to the same identifier
authorMahmoud Mohamed <mahadelr19@gmail.com>
Mon, 13 Mar 2023 14:18:11 +0000 (17:18 +0300)
committerArthur Cohen <arthur.cohen@embecosm.com>
Tue, 16 Jan 2024 17:21:10 +0000 (18:21 +0100)
https://github.com/rust-lang/rust/blob/master/compiler/rustc_resolve/src/late.rs#L3168
This commit follows rustc's implementation of handling multiple bindings
to the same identifier in parameters.

gcc/rust/ChangeLog:

* resolve/rust-ast-resolve-expr.cc (ResolveExpr::visit) :declare and
pass bindings to PatternDeclaration::go.
(ResolveExpr::resolve_closure_param): Likewise.
* resolve/rust-ast-resolve-expr.h: Likewise.
* resolve/rust-ast-resolve-item.cc (ResolveTraitItems::visit): Likewise.
(ResolveItem::visit): Likewise.
* resolve/rust-ast-resolve-pattern.cc (PatternDeclaration::go): Likewise.
(PatternDeclaration::visit): check for previous identifier bindings
before inserting the new one.
* resolve/rust-ast-resolve-pattern.h (enum PatternBoundCtx): New enum.
* resolve/rust-ast-resolve-stmt.h: pass bindings to PatterDeclaration::go.

gcc/testsuite/ChangeLog:

* rust/compile/multiple_bindings1.rs: New test.

Signed-off-by: Mahmoud Mohamed <mahadelr19@gmail.com>
gcc/rust/resolve/rust-ast-resolve-expr.cc
gcc/rust/resolve/rust-ast-resolve-expr.h
gcc/rust/resolve/rust-ast-resolve-item.cc
gcc/rust/resolve/rust-ast-resolve-pattern.cc
gcc/rust/resolve/rust-ast-resolve-pattern.h
gcc/rust/resolve/rust-ast-resolve-stmt.h
gcc/testsuite/rust/compile/multiple_bindings1.rs [new file with mode: 0644]

index b2815dce550e5a7395a99ded086fd221ff32bd7d..6b7782ce391eef564ea93fac41b8b00ab96f0dde 100644 (file)
@@ -207,9 +207,14 @@ ResolveExpr::visit (AST::IfLetExpr &expr)
   resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
   resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
 
+  // FIXME: this declaration should be removed after refactoring
+  // parse_match_arm_patterns output into an AltPattern
+  std::vector<PatternBinding> bindings
+    = {PatternBinding (PatternBoundCtx::Or, std::set<Identifier> ())};
+
   for (auto &pattern : expr.get_patterns ())
     {
-      PatternDeclaration::go (pattern.get (), Rib::ItemType::Var);
+      PatternDeclaration::go (pattern.get (), Rib::ItemType::Var, bindings);
     }
 
   ResolveExpr::go (expr.get_if_block ().get (), prefix, canonical_prefix);
@@ -517,10 +522,15 @@ ResolveExpr::visit (AST::MatchExpr &expr)
        ResolveExpr::go (arm.get_guard_expr ().get (), prefix,
                         canonical_prefix);
 
+      // FIXME: this declaration should be removed after refactoring
+      // parse_match_arms_patterns output into a single AltPattern
+      std::vector<PatternBinding> bindings
+       = {PatternBinding (PatternBoundCtx::Or, std::set<Identifier> ())};
+
       // insert any possible new patterns
       for (auto &pattern : arm.get_patterns ())
        {
-         PatternDeclaration::go (pattern.get (), Rib::ItemType::Var);
+         PatternDeclaration::go (pattern.get (), Rib::ItemType::Var, bindings);
        }
 
       // resolve the body
@@ -576,9 +586,12 @@ ResolveExpr::visit (AST::ClosureExprInner &expr)
   resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
   resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
 
+  std::vector<PatternBinding> bindings
+    = {PatternBinding (PatternBoundCtx::Product, std::set<Identifier> ())};
+
   for (auto &p : expr.get_params ())
     {
-      resolve_closure_param (p);
+      resolve_closure_param (p, bindings);
     }
 
   resolver->push_closure_context (expr.get_node_id ());
@@ -604,9 +617,12 @@ ResolveExpr::visit (AST::ClosureExprInnerTyped &expr)
   resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
   resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
 
+  std::vector<PatternBinding> bindings
+    = {PatternBinding (PatternBoundCtx::Product, std::set<Identifier> ())};
+
   for (auto &p : expr.get_params ())
     {
-      resolve_closure_param (p);
+      resolve_closure_param (p, bindings);
     }
 
   ResolveType::go (expr.get_return_type ().get ());
@@ -624,9 +640,11 @@ ResolveExpr::visit (AST::ClosureExprInnerTyped &expr)
 }
 
 void
-ResolveExpr::resolve_closure_param (AST::ClosureParam &param)
+ResolveExpr::resolve_closure_param (AST::ClosureParam &param,
+                                   std::vector<PatternBinding> &bindings)
 {
-  PatternDeclaration::go (param.get_pattern ().get (), Rib::ItemType::Param);
+  PatternDeclaration::go (param.get_pattern ().get (), Rib::ItemType::Param,
+                         bindings);
 
   if (param.has_type_given ())
     ResolveType::go (param.get_type ().get ());
index 1c9b140ce98eb02db050fc6f7d302271d7f028b2..b3d9e0d799c66f4bf7e6f26811701c7324cafdbc 100644 (file)
@@ -20,6 +20,7 @@
 #define RUST_AST_RESOLVE_EXPR_H
 
 #include "rust-ast-resolve-base.h"
+#include "rust-ast-resolve-pattern.h"
 #include "rust-ast-full.h"
 
 namespace Rust {
@@ -79,7 +80,8 @@ public:
   void visit (AST::ClosureExprInnerTyped &expr) override;
 
 protected:
-  void resolve_closure_param (AST::ClosureParam &param);
+  void resolve_closure_param (AST::ClosureParam &param,
+                             std::vector<PatternBinding> &bindings);
 
 private:
   ResolveExpr (const CanonicalPath &prefix,
index c9485842b40a64988afdd8e60790e149a70b1772..d1ed210323070ab30ea1f61becebbc6700872c03 100644 (file)
@@ -77,13 +77,16 @@ ResolveTraitItems::visit (AST::TraitItemFunc &func)
   if (function.has_return_type ())
     ResolveType::go (function.get_return_type ().get ());
 
+  std::vector<PatternBinding> bindings
+    = {PatternBinding (PatternBoundCtx::Product, std::set<Identifier> ())};
+
   // we make a new scope so the names of parameters are resolved and shadowed
   // correctly
   for (auto &param : function.get_function_params ())
     {
       ResolveType::go (param.get_type ().get ());
-      PatternDeclaration::go (param.get_pattern ().get (),
-                             Rib::ItemType::Param);
+      PatternDeclaration::go (param.get_pattern ().get (), Rib::ItemType::Param,
+                             bindings);
     }
 
   if (function.has_where_clause ())
@@ -141,13 +144,16 @@ ResolveTraitItems::visit (AST::TraitItemMethod &func)
   ResolveType::go (&self_type_path);
   PatternDeclaration::go (&self_pattern, Rib::ItemType::Param);
 
+  std::vector<PatternBinding> bindings
+    = {PatternBinding (PatternBoundCtx::Product, std::set<Identifier> ())};
+
   // we make a new scope so the names of parameters are resolved and shadowed
   // correctly
   for (auto &param : function.get_function_params ())
     {
       ResolveType::go (param.get_type ().get ());
-      PatternDeclaration::go (param.get_pattern ().get (),
-                             Rib::ItemType::Param);
+      PatternDeclaration::go (param.get_pattern ().get (), Rib::ItemType::Param,
+                             bindings);
     }
 
   if (function.has_where_clause ())
@@ -496,13 +502,16 @@ ResolveItem::visit (AST::Function &function)
   if (function.has_return_type ())
     ResolveType::go (function.get_return_type ().get ());
 
+  std::vector<PatternBinding> bindings
+    = {PatternBinding (PatternBoundCtx::Product, std::set<Identifier> ())};
+
   // we make a new scope so the names of parameters are resolved and shadowed
   // correctly
   for (auto &param : function.get_function_params ())
     {
       ResolveType::go (param.get_type ().get ());
-      PatternDeclaration::go (param.get_pattern ().get (),
-                             Rib::ItemType::Param);
+      PatternDeclaration::go (param.get_pattern ().get (), Rib::ItemType::Param,
+                             bindings);
     }
 
   // resolve the function body
@@ -633,13 +642,16 @@ ResolveItem::visit (AST::Method &method)
   ResolveType::go (&self_type_path);
   PatternDeclaration::go (&self_pattern, Rib::ItemType::Param);
 
+  std::vector<PatternBinding> bindings
+    = {PatternBinding (PatternBoundCtx::Product, std::set<Identifier> ())};
+
   // we make a new scope so the names of parameters are resolved and shadowed
   // correctly
   for (auto &param : method.get_function_params ())
     {
       ResolveType::go (param.get_type ().get ());
-      PatternDeclaration::go (param.get_pattern ().get (),
-                             Rib::ItemType::Param);
+      PatternDeclaration::go (param.get_pattern ().get (), Rib::ItemType::Param,
+                             bindings);
     }
 
   // resolve any where clause items
index 30eb6c9547be5cc6fca42c6aceb6c5dfbdba303e..9d9cafb49ad4a45a6800c25ee7f36805633258b3 100644 (file)
 namespace Rust {
 namespace Resolver {
 
+void
+PatternDeclaration::go (AST::Pattern *pattern, Rib::ItemType type)
+{
+  std::vector<PatternBinding> bindings
+    = {PatternBinding (PatternBoundCtx::Product, std::set<Identifier> ())};
+  PatternDeclaration resolver (bindings, type);
+  pattern->accept_vis (resolver);
+};
+
+void
+PatternDeclaration::go (AST::Pattern *pattern, Rib::ItemType type,
+                       std::vector<PatternBinding> &bindings)
+{
+  PatternDeclaration resolver (bindings, type);
+  pattern->accept_vis (resolver);
+}
+
+void
+PatternDeclaration::visit (AST::IdentifierPattern &pattern)
+{
+  bool has_binding_ctx = bindings.size () > 0;
+  rust_assert (has_binding_ctx);
+
+  auto &binding_idents = bindings.back ().idents;
+
+  bool current_ctx_is_product
+    = bindings.back ().ctx == PatternBoundCtx::Product;
+  bool identifier_is_product_bound
+    = current_ctx_is_product
+      && binding_idents.find (pattern.get_ident ()) != binding_idents.end ();
+
+  if (identifier_is_product_bound)
+    {
+      if (type == Rib::ItemType::Param)
+       {
+         rust_error_at (pattern.get_locus (), ErrorCode ("E0415"),
+                        "identifier '%s' is bound more than once in the "
+                        "same parameter list",
+                        pattern.get_ident ().c_str ());
+       }
+      else
+       {
+         rust_error_at (
+           pattern.get_locus (), ErrorCode ("E0416"),
+           "identifier '%s' is bound more than once in the same pattern",
+           pattern.get_ident ().c_str ());
+       }
+
+      return;
+    }
+
+  // if we have a duplicate id this then allows for shadowing correctly
+  // as new refs to this decl will match back here so it is ok to overwrite
+  resolver->get_name_scope ().insert (
+    CanonicalPath::new_seg (pattern.get_node_id (), pattern.get_ident ()),
+    pattern.get_node_id (), pattern.get_locus (), type);
+
+  binding_idents.insert (pattern.get_ident ());
+}
+
+void
+PatternDeclaration::visit (AST::GroupedPattern &pattern)
+{
+  pattern.get_pattern_in_parens ()->accept_vis (*this);
+}
+
+void
+PatternDeclaration::visit (AST::ReferencePattern &pattern)
+{
+  pattern.get_referenced_pattern ()->accept_vis (*this);
+}
+
 void
 PatternDeclaration::visit (AST::PathInExpression &pattern)
 {
@@ -49,7 +121,7 @@ PatternDeclaration::visit (AST::TupleStructPattern &pattern)
 
        for (auto &inner_pattern : items_no_range.get_patterns ())
          {
-           PatternDeclaration::go (inner_pattern.get (), type);
+           inner_pattern.get ()->accept_vis (*this);
          }
       }
       break;
index 0811a031386edd717483647c8d34ee284b2e55ea..9f0e8b5db898467b0d496c3948dcb8f2d232cbbf 100644 (file)
 namespace Rust {
 namespace Resolver {
 
+// Specifies whether the set of already bound patterns are related by 'Or' or
+// 'Product'. Used to check for multiple bindings to the same identifier.
+enum PatternBoundCtx
+{
+  // A product pattern context (e.g. struct and tuple patterns)
+  Product,
+  // An or-pattern context (e.g. p_0 | p_1 | ...)
+  Or,
+};
+
+struct PatternBinding
+{
+  PatternBoundCtx ctx;
+  std::set<Identifier> idents;
+
+  PatternBinding (PatternBoundCtx ctx, std::set<Identifier> idents)
+    : ctx (ctx), idents (idents)
+  {}
+};
+
 class ResolvePattern : public ResolverBase
 {
   using Rust::Resolver::ResolverBase::visit;
@@ -55,45 +75,25 @@ class PatternDeclaration : public ResolverBase
   using Rust::Resolver::ResolverBase::visit;
 
 public:
-  static void go (AST::Pattern *pattern, Rib::ItemType type)
-  {
-    PatternDeclaration resolver (type);
-    pattern->accept_vis (resolver);
-  };
+  static void go (AST::Pattern *pattern, Rib::ItemType type);
+  static void go (AST::Pattern *pattern, Rib::ItemType type,
+                 std::vector<PatternBinding> &bindings);
 
-  void visit (AST::IdentifierPattern &pattern) override
-  {
-    // if we have a duplicate id this then allows for shadowing correctly
-    // as new refs to this decl will match back here so it is ok to overwrite
-    resolver->get_name_scope ().insert (
-      CanonicalPath::new_seg (pattern.get_node_id (), pattern.get_ident ()),
-      pattern.get_node_id (), pattern.get_locus (), type);
-  }
-
-  void visit (AST::GroupedPattern &pattern) override
-  {
-    pattern.get_pattern_in_parens ()->accept_vis (*this);
-  }
-
-  void visit (AST::ReferencePattern &pattern) override
-  {
-    pattern.get_referenced_pattern ()->accept_vis (*this);
-  }
-
-  // cases in a match expression
+  void visit (AST::IdentifierPattern &pattern) override;
+  void visit (AST::GroupedPattern &pattern) override;
+  void visit (AST::ReferencePattern &pattern) override;
   void visit (AST::PathInExpression &pattern) override;
-
   void visit (AST::StructPattern &pattern) override;
-
   void visit (AST::TupleStructPattern &pattern) override;
-
   void visit (AST::TuplePattern &pattern) override;
-
   void visit (AST::RangePattern &pattern) override;
 
 private:
-  PatternDeclaration (Rib::ItemType type) : ResolverBase (), type (type) {}
+  PatternDeclaration (std::vector<PatternBinding> &bindings, Rib::ItemType type)
+    : ResolverBase (), bindings (bindings), type (type)
+  {}
 
+  std::vector<PatternBinding> &bindings;
   Rib::ItemType type;
 };
 
index 10c8605dd062ef4256fd29a0bb6c896035e93003..184ea3a64df024fd9677afaac712e75d603be989 100644 (file)
@@ -344,13 +344,16 @@ public:
     if (function.has_return_type ())
       ResolveType::go (function.get_return_type ().get ());
 
+    std::vector<PatternBinding> bindings
+      = {PatternBinding (PatternBoundCtx::Product, std::set<Identifier> ())};
+
     // we make a new scope so the names of parameters are resolved and shadowed
     // correctly
     for (auto &param : function.get_function_params ())
       {
        ResolveType::go (param.get_type ().get ());
        PatternDeclaration::go (param.get_pattern ().get (),
-                               Rib::ItemType::Param);
+                               Rib::ItemType::Param, bindings);
       }
 
     // resolve the function body
diff --git a/gcc/testsuite/rust/compile/multiple_bindings1.rs b/gcc/testsuite/rust/compile/multiple_bindings1.rs
new file mode 100644 (file)
index 0000000..e73dc2a
--- /dev/null
@@ -0,0 +1,29 @@
+fn f1(i: i32, i: i32) {}
+// { dg-error "identifier .i. is bound more than once in the same parameter list .E0415." "" { target *-*-* } .-1 }
+
+trait Foo {
+  fn f2(i: i32, i: i32) {}
+  // { dg-error "identifier .i. is bound more than once in the same parameter list .E0415." "" { target *-*-* } .-1 }
+}
+
+trait Bar {
+  fn f3(i: i32, j: i32) {}
+}
+
+struct S;
+
+impl S {
+  fn f4(i: i32, i: i32) {}
+  // { dg-error "identifier .i. is bound more than once in the same parameter list .E0415." "" { target *-*-* } .-1 }
+}
+
+impl Bar for S {
+  fn f3(i: i32, i: i32) {}
+  // { dg-error "identifier .i. is bound more than once in the same parameter list .E0415." "" { target *-*-* } .-1 }
+}
+
+fn main() {
+  let _ = |i, i| {};
+  // { dg-error "identifier .i. is bound more than once in the same parameter list .E0415." "" { target *-*-* } .-1 }
+}
+