From: Philip Herron <[email protected]>

This is a daft feature so instead of using the turbo fish to supply the
const generics like a sensible language rustc uses this attribute to support
that the order/index of call arguments are the const generic arguments in
order for the param list. So we walk them then grab the associated subst
param mapping and unify it.

Fixes Rust-GCC/gccrs#3868

gcc/rust/ChangeLog:

        * backend/rust-compile-expr.cc (CompileExpr::visit): check for const 
args
        * hir/tree/rust-hir-expr.cc (CallExpr::CallExpr): add new helper to 
parse attribute
        (CallExpr::operator=): new member
        * hir/tree/rust-hir-expr.h (class CallExpr): new helper
        * hir/tree/rust-hir-item.cc 
(Function::get_legacy_const_generic_indexes): likewise
        * hir/tree/rust-hir-item.h: likewise
        * typecheck/rust-tyty-call.cc (TypeCheckCallExpr::visit): handle const 
args
        * typecheck/rust-tyty-subst.cc 
(SubstitutionRef::resolve_const_argument): refactor out
        * typecheck/rust-tyty-subst.h: likewise

gcc/testsuite/ChangeLog:

        * rust/compile/issues/issue-3868.rs: New test.

Signed-off-by: Philip Herron <[email protected]>

Tested on x86_64-pc-linux-gnu.

---
 gcc/rust/backend/rust-compile-expr.cc         |  15 +-
 gcc/rust/hir/tree/rust-hir-expr.cc            |   3 +-
 gcc/rust/hir/tree/rust-hir-expr.h             |  17 +++
 gcc/rust/hir/tree/rust-hir-item.cc            |  72 +++++++++
 gcc/rust/hir/tree/rust-hir-item.h             |   2 +
 gcc/rust/typecheck/rust-tyty-call.cc          | 101 ++++++++++--
 gcc/rust/typecheck/rust-tyty-subst.cc         | 144 +++++++++---------
 gcc/rust/typecheck/rust-tyty-subst.h          |   3 +
 .../rust/compile/issues/issue-3868.rs         |  15 ++
 9 files changed, 282 insertions(+), 90 deletions(-)
 create mode 100644 gcc/testsuite/rust/compile/issues/issue-3868.rs

diff --git a/gcc/rust/backend/rust-compile-expr.cc 
b/gcc/rust/backend/rust-compile-expr.cc
index 815f53cb087..e2e5644938e 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -1760,12 +1760,18 @@ CompileExpr::visit (HIR::CallExpr &expr)
     }
 
   std::vector<tree> args;
-  for (size_t i = 0; i < expr.get_arguments ().size (); i++)
+  for (size_t source_argument_index = 0;
+       source_argument_index < expr.get_arguments ().size ();
+       source_argument_index++)
     {
-      auto &argument = expr.get_arguments ().at (i);
+      if (expr.is_const_argument (source_argument_index))
+       continue;
+
+      auto &argument = expr.get_arguments ().at (source_argument_index);
+      size_t runtime_argument_index = args.size ();
       auto rvalue = CompileExpr::Compile (*argument, ctx);
 
-      if (is_variadic && i >= required_num_args)
+      if (is_variadic && runtime_argument_index >= required_num_args)
        {
          args.push_back (rvalue);
          continue;
@@ -1775,7 +1781,8 @@ CompileExpr::visit (HIR::CallExpr &expr)
       // necessary
       bool ok;
       TyTy::BaseType *expected = nullptr;
-      ok = get_parameter_tyty_at_index (tyty, i, &expected);
+      ok
+       = get_parameter_tyty_at_index (tyty, runtime_argument_index, &expected);
       rust_assert (ok);
 
       TyTy::BaseType *actual = nullptr;
diff --git a/gcc/rust/hir/tree/rust-hir-expr.cc 
b/gcc/rust/hir/tree/rust-hir-expr.cc
index c4ccad1497b..080ac4da6a4 100644
--- a/gcc/rust/hir/tree/rust-hir-expr.cc
+++ b/gcc/rust/hir/tree/rust-hir-expr.cc
@@ -581,7 +581,7 @@ CallExpr::CallExpr (Analysis::NodeMapping mappings,
 
 CallExpr::CallExpr (CallExpr const &other)
   : ExprWithoutBlock (other), function (other.function->clone_expr ()),
-    locus (other.locus)
+    const_argument_indexes (other.const_argument_indexes), locus (other.locus)
 /*, params(other.params),*/ {
   params.reserve (other.params.size ());
   for (const auto &e : other.params)
@@ -593,6 +593,7 @@ CallExpr::operator= (CallExpr const &other)
 {
   ExprWithoutBlock::operator= (other);
   function = other.function->clone_expr ();
+  const_argument_indexes = other.const_argument_indexes;
   locus = other.locus;
 
   params.reserve (other.params.size ());
diff --git a/gcc/rust/hir/tree/rust-hir-expr.h 
b/gcc/rust/hir/tree/rust-hir-expr.h
index 02c6f253810..14e29e0853e 100644
--- a/gcc/rust/hir/tree/rust-hir-expr.h
+++ b/gcc/rust/hir/tree/rust-hir-expr.h
@@ -1413,6 +1413,7 @@ class CallExpr : public ExprWithoutBlock
 {
   std::unique_ptr<Expr> function;
   std::vector<std::unique_ptr<Expr>> params;
+  std::vector<size_t> const_argument_indexes;
   location_t locus;
 
 public:
@@ -1440,6 +1441,22 @@ public:
   void accept_vis (HIRFullVisitor &vis) override;
   void accept_vis (HIRExpressionVisitor &vis) override;
 
+  void set_const_argument_indexes (const std::vector<size_t> &indexes)
+  {
+    const_argument_indexes = indexes;
+  }
+
+  bool is_const_argument (size_t index) const
+  {
+    for (auto const_index : const_argument_indexes)
+      {
+       if (index == const_index)
+         return true;
+      }
+
+    return false;
+  }
+
   bool has_fnexpr () const { return function != nullptr; }
   Expr &get_fnexpr () { return *function; }
 
diff --git a/gcc/rust/hir/tree/rust-hir-item.cc 
b/gcc/rust/hir/tree/rust-hir-item.cc
index d5c9ad154c8..8273ab51631 100644
--- a/gcc/rust/hir/tree/rust-hir-item.cc
+++ b/gcc/rust/hir/tree/rust-hir-item.cc
@@ -18,6 +18,8 @@
 
 #include "rust-hir-item.h"
 #include "optional.h"
+#include "rust-attribute-values.h"
+#include "rust-attributes.h"
 
 namespace Rust {
 namespace HIR {
@@ -261,6 +263,76 @@ Module::operator= (Module const &other)
   return *this;
 }
 
+tl::optional<std::vector<size_t>>
+Function::get_legacy_const_generic_indexes () const
+{
+  std::vector<size_t> legacy_const_generic_indexes;
+  const auto &attrs = get_outer_attrs ();
+  for (const auto &attr : attrs)
+    {
+      const auto possibly_builtin = Analysis::lookup_builtin (attr);
+      if (!possibly_builtin.has_value ())
+       continue;
+
+      const auto &mapping = possibly_builtin.value ();
+      if (mapping.name != Values::Attributes::RUSTC_ARGS_REQUIRED_CONST)
+       continue;
+
+      if (!attr.has_attr_input ())
+       {
+         rust_error_at (attr.get_locus (), "malformed %qs attribute",
+                        mapping.name.c_str ());
+         return tl::nullopt;
+       }
+
+      auto parsed_attr = attr;
+      parsed_attr.parse_attr_to_meta_item ();
+      if (!parsed_attr.is_parsed_to_meta_item ())
+       {
+         rust_error_at (attr.get_locus (), "malformed %qs attribute",
+                        mapping.name.c_str ());
+         return tl::nullopt;
+       }
+
+      const auto &container
+       = static_cast<const AST::AttrInputMetaItemContainer &> (
+         parsed_attr.get_attr_input ());
+      const auto &items = container.get_items ();
+      for (const auto &item : items)
+       {
+         if (item->get_kind () != AST::MetaItemInner::Kind::LitExpr)
+           {
+             rust_error_at (item->get_locus (),
+                            "expected an integer argument index");
+             return tl::nullopt;
+           }
+         const auto &literal
+           = static_cast<AST::MetaItemLitExpr &> (*item).get_literal ();
+         if (literal.get_lit_type () != AST::Literal::INT)
+           {
+             rust_error_at (item->get_locus (),
+                            "expected an integer argument index");
+             return tl::nullopt;
+           }
+
+         mpz_t value;
+         if (mpz_init_set_str (value, literal.as_string ().c_str (), 10) != 0)
+           {
+             mpz_clear (value);
+             rust_error_at (item->get_locus (), "failed load argument index");
+             return tl::nullopt;
+           }
+
+         size_t index = 0;
+         mpz_export (&index, nullptr, 1, sizeof (index), 0, 0, value);
+         mpz_clear (value);
+         legacy_const_generic_indexes.push_back (index);
+       }
+    }
+
+  return legacy_const_generic_indexes;
+}
+
 Function::Function (Analysis::NodeMapping mappings, Identifier function_name,
                    FunctionQualifiers qualifiers,
                    std::vector<std::unique_ptr<GenericParam>> generic_params,
diff --git a/gcc/rust/hir/tree/rust-hir-item.h 
b/gcc/rust/hir/tree/rust-hir-item.h
index 9a572bf92c6..a07334ee979 100644
--- a/gcc/rust/hir/tree/rust-hir-item.h
+++ b/gcc/rust/hir/tree/rust-hir-item.h
@@ -966,6 +966,8 @@ class Function : public VisItem, public ImplItem
 public:
   std::string to_string () const override;
 
+  tl::optional<std::vector<size_t>> get_legacy_const_generic_indexes () const;
+
   // Returns whether function has generic parameters.
   bool has_generics () const { return !generic_params.empty (); }
 
diff --git a/gcc/rust/typecheck/rust-tyty-call.cc 
b/gcc/rust/typecheck/rust-tyty-call.cc
index f87b160f848..3dd72a5fd23 100644
--- a/gcc/rust/typecheck/rust-tyty-call.cc
+++ b/gcc/rust/typecheck/rust-tyty-call.cc
@@ -17,10 +17,13 @@
 // <http://www.gnu.org/licenses/>.
 
 #include "rust-tyty-call.h"
+#include "optional.h"
+#include "rust-hir-item.h"
 #include "rust-hir-type-check-expr.h"
 #include "rust-hir-type-check.h"
 #include "rust-type-util.h"
 #include "rust-hir-trait-reference.h"
+#include "rust-tyty.h"
 
 namespace Rust {
 namespace TyTy {
@@ -153,26 +156,91 @@ TypeCheckCallExpr::visit (ADTType &type)
 void
 TypeCheckCallExpr::visit (FnType &type)
 {
-  if (call.num_params () != type.num_params ())
+  std::vector<size_t> indexes;
+  const auto lookup = mappings.lookup_defid (type.get_id ());
+  if (lookup.has_value ())
     {
-      if (type.is_variadic ())
+      const auto *item = lookup.value ();
+      rust_assert (item->get_item_kind () == HIR::Item::ItemKind::Function);
+
+      const auto &fn = *static_cast<const HIR::Function *> (item);
+      auto legacy_const_generic_indexes
+       = fn.get_legacy_const_generic_indexes ();
+
+      bool malformed_const_generic_indexes
+       = !legacy_const_generic_indexes.has_value ();
+      if (malformed_const_generic_indexes)
+       return;
+
+      indexes = legacy_const_generic_indexes.value ();
+    }
+
+  size_t expected_args = type.num_params () + indexes.size ();
+  if (call.num_params () < expected_args
+      || (!type.is_variadic () && call.num_params () != expected_args))
+    {
+      emit_unexpected_argument_error (call.get_locus (), call.num_params (),
+                                     expected_args);
+      return;
+    }
+
+  if (!indexes.empty ())
+    {
+      std::set<size_t> seen;
+      for (size_t i = 0; i < indexes.size (); i++)
        {
-         if (call.num_params () < type.num_params ())
+         size_t index = indexes[i];
+         seen.insert (index);
+
+         bool duplicate_index = seen.size () != i + 1;
+         if (index >= call.num_params () || duplicate_index)
            {
-             emit_unexpected_argument_error (
-               call.get_locus (), (unsigned long) call.num_params (),
-               (unsigned long) type.num_params ());
+             rust_error_at (call.get_locus (), "invalid const argument index");
              return;
            }
        }
-      else
+
+      std::vector<SubstitutionParamMapping *> const_params;
+      for (auto &sub : type.get_substs ())
        {
-         emit_unexpected_argument_error (call.get_locus (),
-                                         (unsigned long) call.num_params (),
-                                         (unsigned long) type.num_params ());
+         if (sub.get_generic_param ().get_kind ()
+             == HIR::GenericParam::GenericKind::CONST)
+           const_params.push_back (&sub);
+       }
+
+      if (const_params.size () != indexes.size ())
+       {
+         rust_error_at (
+           call.get_locus (),
+           "const argument count does not match const parameters");
+
          return;
        }
+
+      auto &subst_mappings = type.get_substitution_arguments ();
+      for (size_t i = 0; i < indexes.size (); i++)
+       {
+         size_t cidx = indexes[i];
+         auto &argument = *call.get_arguments ().at (cidx);
+
+         auto *param = const_params[i]->get_param_ty ();
+         auto const_ty = param->as_const_type ()->get_specified_type ();
+         auto *value
+           = SubstitutionRef::resolve_const_argument (argument, const_ty);
+
+         if (value->is<ErrorType> ())
+           return;
+
+         SubstitutionArg existing = SubstitutionArg::error ();
+         bool ok = subst_mappings.get_argument_for_symbol (param, &existing);
+         rust_assert (ok);
+
+         Resolver::unify_site (argument.get_mappings ().get_hirid (),
+                               TyWithLocation (existing.get_tyty ()),
+                               TyWithLocation (value), argument.get_locus ());
+       }
     }
+  call.set_const_argument_indexes (indexes);
 
   // if the surrounding context has pushed an expected type, try unifying it
   // with the fn's return type before checking arguments. This lets the callee
@@ -193,9 +261,13 @@ TypeCheckCallExpr::visit (FnType &type)
                                true /*implicit_infer_vars*/, true /*cleanup*/);
     }
 
-  size_t i = 0;
-  for (auto &argument : call.get_arguments ())
+  size_t i;
+  for (i = 0; i < call.get_arguments ().size (); i++)
     {
+      auto &argument = call.get_arguments ().at (i);
+      if (call.is_const_argument (i))
+       continue;
+
       location_t arg_locus = argument->get_locus ();
 
       TyTy::BaseType *param_ty = nullptr;
@@ -204,6 +276,7 @@ TypeCheckCallExpr::visit (FnType &type)
 
       if (param_ty != nullptr)
        ctx->push_expected_type (param_ty);
+
       auto argument_expr_tyty = Resolver::TypeCheckExpr::Resolve (*argument);
       if (param_ty != nullptr)
        ctx->pop_expected_type ();
@@ -309,11 +382,9 @@ TypeCheckCallExpr::visit (FnType &type)
              break;
            }
        }
-
-      i++;
     }
 
-  if (i < call.num_params ())
+  if (i + indexes.size () < call.num_params ())
     {
       emit_unexpected_argument_error (call.get_locus (), (unsigned long) i,
                                      (unsigned long) call.num_params ());
diff --git a/gcc/rust/typecheck/rust-tyty-subst.cc 
b/gcc/rust/typecheck/rust-tyty-subst.cc
index 73e4479fe27..d589b25473d 100644
--- a/gcc/rust/typecheck/rust-tyty-subst.cc
+++ b/gcc/rust/typecheck/rust-tyty-subst.cc
@@ -823,11 +823,6 @@ SubstitutionRef::get_mappings_from_generic_args (
   for (auto &arg : args.get_const_args ())
     {
       auto &expr = *arg.get_expression ().get ();
-      BaseType *expr_type = Resolver::TypeCheckExpr::Resolve (expr);
-      if (expr_type == nullptr || expr_type->is<ErrorType> ())
-       return SubstitutionArgumentMappings::error ();
-
-      // validate this param is really a const generic
       const auto &param_mapping = substitutions.at (offs);
       const auto &generic = param_mapping.get_generic_param ();
       if (generic.get_kind () != HIR::GenericParam::GenericKind::CONST)
@@ -838,73 +833,13 @@ SubstitutionRef::get_mappings_from_generic_args (
          return SubstitutionArgumentMappings::error ();
        }
 
-      // get the const generic specified type
-      const auto base_generic = param_mapping.get_param_ty ();
-      rust_assert (base_generic->get_kind () == TyTy::TypeKind::CONST);
-      const auto const_param
-       = static_cast<const TyTy::ConstParamType *> (base_generic);
-      auto specified_type = const_param->get_specified_type ();
-
-      // validate this const generic is of the correct type
-      TyTy::BaseType *coereced_type = nullptr;
-      if (expr_type->get_kind () == TyTy::TypeKind::CONST)
-       {
-         auto const_expr_type = expr_type->as_const_type ();
-         auto const_value_type = const_expr_type->get_specified_type ();
-         coereced_type
-           = Resolver::coercion_site (expr.get_mappings ().get_hirid (),
-                                      TyTy::TyWithLocation (specified_type),
-                                      TyTy::TyWithLocation (const_value_type,
-                                                            expr.get_locus ()),
-                                      arg.get_locus ());
-       }
-      else
-       {
-         coereced_type
-           = Resolver::coercion_site (expr.get_mappings ().get_hirid (),
-                                      TyTy::TyWithLocation (specified_type),
-                                      TyTy::TyWithLocation (expr_type,
-                                                            expr.get_locus ()),
-                                      arg.get_locus ());
-       }
-
-      if (coereced_type == nullptr || coereced_type->is<ErrorType> ())
+      auto specified_type = param_mapping.get_param_ty ()
+                             ->as_const_type ()
+                             ->get_specified_type ();
+      auto const_value_ty = resolve_const_argument (expr, specified_type);
+      if (const_value_ty->is<ErrorType> ())
        return SubstitutionArgumentMappings::error ();
 
-      TyTy::BaseType *const_value_ty = nullptr;
-      if (expr_type->get_kind () == TyTy::TypeKind::CONST)
-       const_value_ty = expr_type;
-      else
-       {
-         // const fold it if available
-         auto ctx = Compile::Context::get ();
-         tree folded
-           = Compile::HIRCompileBase::query_compile_const_expr (ctx,
-                                                                coereced_type,
-                                                                expr);
-
-         if (folded == error_mark_node)
-           {
-             rich_location r (line_table, arg.get_locus ());
-             r.add_range (expr.get_locus ());
-             rust_error_at (r, "failed to resolve const expression");
-             return SubstitutionArgumentMappings::error ();
-           }
-
-         // Use a fresh HirId to avoid conflicts with the expr's type
-         auto &global_mappings = Analysis::Mappings::get ();
-         HirId const_value_id = global_mappings.get_next_hir_id ();
-         const_value_ty
-           = new TyTy::ConstValueType (folded, coereced_type, const_value_id,
-                                       const_value_id, {});
-
-         // Insert the ConstValueType into the context so it can be looked up
-         auto context = Resolver::TypeCheckContext::get ();
-         context->insert_type (
-           Analysis::NodeMapping (0, 0, const_value_ty->get_ref (), 0),
-           const_value_ty);
-       }
-
       mappings.emplace_back (&param_mapping, const_value_ty);
       offs++;
     }
@@ -950,6 +885,75 @@ SubstitutionRef::get_mappings_from_generic_args (
          constraint_arguments};
 }
 
+BaseType *
+SubstitutionRef::resolve_const_argument (HIR::Expr &expr,
+                                        BaseType *specified_type)
+{
+  BaseType *expr_type = Resolver::TypeCheckExpr::Resolve (expr);
+  if (expr_type == nullptr || expr_type->is<ErrorType> ())
+    return new ErrorType (expr.get_mappings ().get_hirid ());
+
+  TyTy::BaseType *coerced_type = nullptr;
+  if (expr_type->get_kind () == TyTy::TypeKind::CONST)
+    {
+      auto const_expr_type = expr_type->as_const_type ();
+      auto const_value_type = const_expr_type->get_specified_type ();
+      coerced_type
+       = Resolver::coercion_site (expr.get_mappings ().get_hirid (),
+                                  TyTy::TyWithLocation (specified_type),
+                                  TyTy::TyWithLocation (const_value_type,
+                                                        expr.get_locus ()),
+                                  expr.get_locus ());
+    }
+  else
+    {
+      coerced_type
+       = Resolver::coercion_site (expr.get_mappings ().get_hirid (),
+                                  TyTy::TyWithLocation (specified_type),
+                                  TyTy::TyWithLocation (expr_type,
+                                                        expr.get_locus ()),
+                                  expr.get_locus ());
+    }
+
+  if (coerced_type == nullptr || coerced_type->is<ErrorType> ())
+    return new ErrorType (expr.get_mappings ().get_hirid ());
+
+  TyTy::BaseType *const_value_ty = nullptr;
+  if (expr_type->get_kind () == TyTy::TypeKind::CONST)
+    const_value_ty = expr_type;
+  else
+    {
+      auto ctx = Compile::Context::get ();
+      tree folded
+       = Compile::HIRCompileBase::query_compile_const_expr (ctx, coerced_type,
+                                                            expr);
+
+      if (folded == error_mark_node)
+       {
+         rich_location r (line_table, expr.get_locus ());
+         r.add_range (expr.get_locus ());
+         rust_error_at (r, "failed to resolve const expression");
+         return new ErrorType (expr.get_mappings ().get_hirid ());
+       }
+
+      // Use a fresh HirId to avoid conflicts with the expr's type
+      auto &global_mappings = Analysis::Mappings::get ();
+      HirId const_value_id = global_mappings.get_next_hir_id ();
+      const_value_ty
+       = new TyTy::ConstValueType (folded, coerced_type, const_value_id,
+                                   const_value_id, {});
+
+      // Insert the ConstValueType into the context so it can be looked up
+      auto context = Resolver::TypeCheckContext::get ();
+      context->insert_type (Analysis::NodeMapping (0, 0,
+                                                  const_value_ty->get_ref (),
+                                                  0),
+                           const_value_ty);
+    }
+
+  return const_value_ty;
+}
+
 BaseType *
 SubstitutionRef::infer_substitions (location_t locus)
 {
diff --git a/gcc/rust/typecheck/rust-tyty-subst.h 
b/gcc/rust/typecheck/rust-tyty-subst.h
index 623593b9acc..bfe462b119d 100644
--- a/gcc/rust/typecheck/rust-tyty-subst.h
+++ b/gcc/rust/typecheck/rust-tyty-subst.h
@@ -315,6 +315,9 @@ public:
   get_mappings_from_generic_args (HIR::GenericArgs &args,
                                  const std::vector<Region> &regions);
 
+  static BaseType *resolve_const_argument (HIR::Expr &expr,
+                                          BaseType *specified_type);
+
   // Recursive substitutions
   // Foo <A,B> { a:A, b: B}; Bar <X,Y,Z>{a:X, b: Foo<Y,Z>}
   //
diff --git a/gcc/testsuite/rust/compile/issues/issue-3868.rs 
b/gcc/testsuite/rust/compile/issues/issue-3868.rs
new file mode 100644
index 00000000000..7f8a9ce9c6b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issues/issue-3868.rs
@@ -0,0 +1,15 @@
+#![feature(no_core, intrinsics, lang_items, staged_api)]
+#![feature(rustc_attrs)]
+#![no_core]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[rustc_args_required_const(1)]
+fn foo<const N: usize>(x: i32) -> i32 {
+    x + N as i32
+}
+
+fn main() {
+    let _a = foo(10, 3);
+}
-- 
2.55.0

Reply via email to