https://gcc.gnu.org/g:46161005e7947b5acdb463b425edb55f5cae60bf
commit r17-5131-g46161005e7947b5acdb463b425edb55f5cae60bf Author: Philip Herron <[email protected]> Date: Fri Sep 11 16:23:50 2026 +0100 gccrs: Support silly legacy rustc const generics for stdarch 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]> Diff: --- 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 + gcc/testsuite/rust/compile/issues/issue-3868.rs | 15 +++ 9 files changed, 282 insertions(+), 90 deletions(-) diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc index 815f53cb0878..e2e5644938e7 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 c4ccad1497bf..080ac4da6a40 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 02c6f253810a..14e29e0853ef 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 d5c9ad154c8d..8273ab516310 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 9a572bf92c65..a07334ee9799 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 f87b160f8485..3dd72a5fd238 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 ()) + { + 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 ()) { - if (type.is_variadic ()) + 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 ()) + { + if (sub.get_generic_param ().get_kind () + == HIR::GenericParam::GenericKind::CONST) + const_params.push_back (&sub); + } + + if (const_params.size () != indexes.size ()) { - emit_unexpected_argument_error (call.get_locus (), - (unsigned long) call.num_params (), - (unsigned long) type.num_params ()); + 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 73e4479fe273..d589b25473d1 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 ¶m_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 (¶m_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 623593b9acc5..bfe462b119d6 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> ®ions); + 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 000000000000..7f8a9ce9c6b8 --- /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); +}
