Skip to content

Commit e7deae3

Browse files
committed
gccrs: avoid ICE on generic const expressions in path resolution
Generic const expressions such as `{ N + 1 }` are symbolic and cannot be evaluated to a concrete value immediately during type checking. Previously, the compiler lacked a representation for these symbolic expressions in the type system, causing it to treat them as concrete values. This led to assertion failures and Internal Compiler Errors (ICE) in the backend and path resolution logic when the compiler attempted to access the raw value. This patch introduces `TyTy::ConstExprType`, a new type variant designed to hold symbolic expressions that have not yet been evaluated. It updates the core type system, substitution mapper, unification rules, call checkers, and backend compilation visitors to handle this new type gracefully. Key changes include: 1. Validating const generic arguments in path resolution to detect dependency using the name resolver. 2. Bridging the substitution mapper to clone symbolic expressions. 3. Updating backend type compilation (`TyTyResolveCompile`) to compile the underlying expression tree for symbolic types instead of crashing. 4. Handling non-value constants in path resolution by emitting a specific diagnostic ("cannot evaluate constant expression") before returning failure. 5. Updating existing tests to match the new `<const_expr>` string representation. Fixes Rust-GCC#4341 gcc/rust/ChangeLog: * backend/rust-compile-resolve-path.cc (ResolvePathRef::resolve_with_node_id): Emit diagnostic and return error_mark_node for non-value constants. * backend/rust-compile-type.cc (TyTyResolveCompile::visit): Compile the underlying expression for ConstExprType. * backend/rust-compile-type.h: Declare visit method. * typecheck/rust-hir-type-check-path.cc (is_const_dependent): New helper. (TypeCheckExpr::resolve_root_path): Skip immediate resolution for dependent const arguments. (TypeCheckExpr::resolve_segments): Likewise. * typecheck/rust-hir-type-check-pattern.cc (TypeCheckPattern::visit): Handle ConstKind::Expr in switch to prevent fallthrough. * typecheck/rust-substitution-mapper.cc (SubstMapperInternal::visit): Implement cloning for ConstExprType. * typecheck/rust-substitution-mapper.h: Add visit declarations. * typecheck/rust-tyty-call.cc (TypeCheckCallExpr::visit): Implement stub for ConstExprType. * typecheck/rust-tyty-call.h: Declare visit method. * typecheck/rust-tyty-variance-analysis-private.h (VisitorBase::visit): Add stub implementation. * typecheck/rust-tyty-visitor.h: Add virtual visit methods. * typecheck/rust-tyty.cc (ConstExprType::const_kind): Implement. (ConstExprType::accept_vis): Implement. (ConstExprType::as_string): Implement. (ConstExprType::clone): Implement. (ConstExprType::is_equal): Implement. (ConstExprType::get_expr): Implement. * typecheck/rust-tyty.h (enum ConstKind): Add Expr variant. (class ConstExprType): New class definition. * typecheck/rust-unify.cc (UnifyRules::expect_const): Add unification logic for ConstExprType. gcc/testsuite/ChangeLog: * rust/compile/const_generics_10.rs: Update expected error messages to match <const_expr> representation. * rust/compile/issue-4302.rs: New test. Signed-off-by: Jayant Chauhan <[email protected]>
1 parent 32622b7 commit e7deae3

16 files changed

+286
-14
lines changed

gcc/rust/backend/rust-compile-resolve-path.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,12 @@ ResolvePathRef::resolve_with_node_id (
193193
auto d = lookup->destructure ();
194194
rust_assert (d->get_kind () == TyTy::TypeKind::CONST);
195195
auto c = d->as_const_type ();
196-
rust_assert (c->const_kind () == TyTy::BaseConstType::ConstKind::Value);
196+
if (c->const_kind () != TyTy::BaseConstType::ConstKind::Value)
197+
{
198+
// Emit diagnostic so the test passes instead of silently failing
199+
rust_error_at (expr_locus, "cannot evaluate constant expression");
200+
return error_mark_node;
201+
}
197202
auto val = static_cast<TyTy::ConstValueType *> (c);
198203
return val->get_value ();
199204
}

gcc/rust/backend/rust-compile-type.cc

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "rust-compile-type.h"
2020
#include "rust-constexpr.h"
2121
#include "rust-compile-base.h"
22+
#include "rust-compile-expr.h"
2223

2324
#include "tree.h"
2425
#include "fold-const.h"
@@ -165,6 +166,12 @@ TyTyResolveCompile::visit (const TyTy::ConstErrorType &type)
165166
translated = error_mark_node;
166167
}
167168

169+
void
170+
TyTyResolveCompile::visit (const TyTy::ConstExprType &type)
171+
{
172+
translated = CompileExpr::Compile (*type.get_expr (), ctx);
173+
}
174+
168175
void
169176
TyTyResolveCompile::visit (const TyTy::ProjectionType &type)
170177
{
@@ -488,9 +495,11 @@ TyTyResolveCompile::visit (const TyTy::ArrayType &type)
488495
{
489496
tree element_type
490497
= TyTyResolveCompile::compile (ctx, type.get_element_type ());
498+
499+
// 1. Get capacity
491500
auto const_capacity = type.get_capacity ();
492501

493-
// Check if capacity is a const type
502+
// 2. CHECK if it is valid BEFORE using it
494503
if (const_capacity->get_kind () != TyTy::TypeKind::CONST)
495504
{
496505
rust_error_at (type.get_locus (), "array capacity is not a const type");
@@ -499,14 +508,23 @@ TyTyResolveCompile::visit (const TyTy::ArrayType &type)
499508
}
500509

501510
auto *capacity_const = const_capacity->as_const_type ();
511+
tree folded_capacity_expr = error_mark_node;
512+
if (capacity_const->const_kind () == TyTy::BaseConstType::ConstKind::Expr)
513+
{
514+
auto &expr_type
515+
= *static_cast<const TyTy::ConstExprType *> (capacity_const);
502516

503-
rust_assert (capacity_const->const_kind ()
504-
== TyTy::BaseConstType::ConstKind::Value);
505-
auto &capacity_value = *static_cast<TyTy::ConstValueType *> (capacity_const);
506-
auto folded_capacity_expr = capacity_value.get_value ();
517+
folded_capacity_expr = CompileExpr::Compile (*expr_type.get_expr (), ctx);
518+
}
519+
else
520+
{
521+
rust_assert (capacity_const->const_kind ()
522+
== TyTy::BaseConstType::ConstKind::Value);
523+
auto &capacity_value
524+
= *static_cast<const TyTy::ConstValueType *> (capacity_const);
525+
folded_capacity_expr = capacity_value.get_value ();
526+
}
507527

508-
// build_index_type takes the maximum index, which is one less than
509-
// the length.
510528
tree index_type_tree = build_index_type (
511529
fold_build2 (MINUS_EXPR, sizetype, folded_capacity_expr, size_one_node));
512530

gcc/rust/backend/rust-compile-type.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class TyTyResolveCompile : protected TyTy::TyConstVisitor
5454
void visit (const TyTy::ConstValueType &) override;
5555
void visit (const TyTy::ConstInferType &) override;
5656
void visit (const TyTy::ConstErrorType &) override;
57+
void visit (const TyTy::ConstExprType &) override;
5758
void visit (const TyTy::StrType &) override;
5859
void visit (const TyTy::NeverType &) override;
5960
void visit (const TyTy::PlaceholderType &) override;

gcc/rust/typecheck/rust-hir-type-check-path.cc

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include "rust-hir-item.h"
3131
#include "rust-session-manager.h"
3232
#include "rust-immutable-name-resolution-context.h"
33+
#include "rust-name-resolver.h"
34+
#include "rust-hir-expr.h"
3335

3436
namespace Rust {
3537
namespace Resolver {
@@ -241,6 +243,66 @@ TypeCheckExpr::visit (HIR::PathInExpression &expr)
241243
}
242244
}
243245

246+
/* Helper to check if a const generic expression is dependent (symbolic).
247+
Returns true if the expression contains paths or identifiers (e.g., { N + 1
248+
}). Returns false if the expression is purely literal/concrete (e.g., { 1 + 1
249+
}). */
250+
static bool
251+
is_const_dependent (HIR::Expr &expr)
252+
{
253+
switch (expr.get_expression_type ())
254+
{
255+
case HIR::Expr::ExprType::Path:
256+
{
257+
// A path is only dependent if it resolves to a generic parameter.
258+
// We use Resolver2_0 to find the definition ID.
259+
auto &nr_ctx
260+
= Resolver2_0::ImmutableNameResolutionContext::get ().resolver ();
261+
auto resolved = nr_ctx.lookup (expr.get_mappings ().get_nodeid ());
262+
263+
if (!resolved)
264+
return false;
265+
266+
return Analysis::Mappings::get ().lookup_hir_generic_param (*resolved)
267+
!= nullptr;
268+
}
269+
270+
case HIR::Expr::ExprType::Lit:
271+
return false;
272+
273+
case HIR::Expr::ExprType::Block:
274+
{
275+
auto &block = static_cast<HIR::BlockExpr &> (expr);
276+
if (block.has_expr ())
277+
278+
return is_const_dependent (block.get_final_expr ());
279+
280+
if (!block.get_statements ().empty ())
281+
return true;
282+
283+
return false;
284+
}
285+
286+
case HIR::Expr::ExprType::Grouped:
287+
{
288+
auto &group = static_cast<HIR::GroupedExpr &> (expr);
289+
290+
return is_const_dependent (group.get_expr_in_parens ());
291+
}
292+
293+
case HIR::Expr::ExprType::Operator:
294+
{
295+
auto &arith = static_cast<HIR::ArithmeticOrLogicalExpr &> (expr);
296+
297+
return is_const_dependent (arith.get_lhs ())
298+
|| is_const_dependent (arith.get_rhs ());
299+
}
300+
301+
default:
302+
return true;
303+
}
304+
}
305+
244306
TyTy::BaseType *
245307
TypeCheckExpr::resolve_root_path (HIR::PathInExpression &expr, size_t *offset,
246308
NodeId *root_resolved_node_id)
@@ -366,6 +428,25 @@ TypeCheckExpr::resolve_root_path (HIR::PathInExpression &expr, size_t *offset,
366428
// turbo-fish segment path::<ty>
367429
if (seg.has_generic_args ())
368430
{
431+
// Check for dependent const expressions (like { N + 1 })
432+
bool is_dependent = false;
433+
for (auto &arg : seg.get_generic_args ().get_const_args ())
434+
{
435+
if (is_const_dependent (*arg.get_expression ()))
436+
{
437+
is_dependent = true;
438+
break;
439+
}
440+
}
441+
442+
if (is_dependent)
443+
{
444+
*root_resolved_node_id = ref_node_id;
445+
*offset = *offset + 1;
446+
root_tyty = lookup;
447+
continue;
448+
}
449+
369450
lookup = SubstMapper::Resolve (lookup, expr.get_locus (),
370451
&seg.get_generic_args (),
371452
context->regions_from_generic_args (
@@ -524,6 +605,21 @@ TypeCheckExpr::resolve_segments (NodeId root_resolved_node_id,
524605

525606
if (seg.has_generic_args ())
526607
{
608+
// Check for dependent const expressions (like { N + 1 })
609+
bool is_dependent = false;
610+
for (auto &arg : seg.get_generic_args ().get_const_args ())
611+
{
612+
if (is_const_dependent (*arg.get_expression ()))
613+
{
614+
is_dependent = true;
615+
break;
616+
}
617+
}
618+
if (is_dependent)
619+
{
620+
continue;
621+
}
622+
527623
rust_debug_loc (seg.get_locus (), "applying segment generics: %s",
528624
tyseg->as_string ().c_str ());
529625
tyseg

gcc/rust/typecheck/rust-hir-type-check-pattern.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,7 @@ TypeCheckPattern::visit (HIR::SlicePattern &pattern)
823823
case TyTy::BaseConstType::ConstKind::Decl:
824824
case TyTy::BaseConstType::ConstKind::Infer:
825825
case TyTy::BaseConstType::ConstKind::Error:
826+
case TyTy::BaseConstType::ConstKind::Expr:
826827
cap = error_mark_node;
827828
break;
828829
}

gcc/rust/typecheck/rust-substitution-mapper.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,12 @@ SubstMapperInternal::visit (TyTy::ConstErrorType &type)
291291
resolved = type.clone ();
292292
}
293293

294+
void
295+
SubstMapperInternal::visit (TyTy::ConstExprType &type)
296+
{
297+
resolved = type.clone ();
298+
}
299+
294300
void
295301
SubstMapperInternal::visit (TyTy::PlaceholderType &type)
296302
{

gcc/rust/typecheck/rust-substitution-mapper.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class SubstMapper : public TyTy::TyVisitor
6565
void visit (TyTy::ConstValueType &) override { rust_unreachable (); }
6666
void visit (TyTy::ConstInferType &) override { rust_unreachable (); }
6767
void visit (TyTy::ConstErrorType &) override { rust_unreachable (); }
68+
void visit (TyTy::ConstExprType &) override { rust_unreachable (); }
6869
void visit (TyTy::StrType &) override { rust_unreachable (); }
6970
void visit (TyTy::NeverType &) override { rust_unreachable (); }
7071
void visit (TyTy::DynamicObjectType &) override { rust_unreachable (); }
@@ -100,6 +101,7 @@ class SubstMapperInternal : public TyTy::TyVisitor
100101
void visit (TyTy::ConstValueType &type) override;
101102
void visit (TyTy::ConstInferType &type) override;
102103
void visit (TyTy::ConstErrorType &type) override;
104+
void visit (TyTy::ConstExprType &type) override;
103105
void visit (TyTy::PlaceholderType &type) override;
104106
void visit (TyTy::ProjectionType &type) override;
105107
void visit (TyTy::ClosureType &type) override;
@@ -157,6 +159,7 @@ class SubstMapperFromExisting : public TyTy::TyVisitor
157159
void visit (TyTy::ConstValueType &) override { rust_unreachable (); }
158160
void visit (TyTy::ConstInferType &) override { rust_unreachable (); }
159161
void visit (TyTy::ConstErrorType &) override { rust_unreachable (); }
162+
void visit (TyTy::ConstExprType &) override { rust_unreachable (); }
160163
void visit (TyTy::StrType &) override { rust_unreachable (); }
161164
void visit (TyTy::NeverType &) override { rust_unreachable (); }
162165
void visit (TyTy::PlaceholderType &) override { rust_unreachable (); }
@@ -201,6 +204,7 @@ class GetUsedSubstArgs : public TyTy::TyConstVisitor
201204
void visit (const TyTy::ConstValueType &) override {}
202205
void visit (const TyTy::ConstInferType &) override {}
203206
void visit (const TyTy::ConstErrorType &) override {}
207+
void visit (const TyTy::ConstExprType &) override {}
204208
void visit (const TyTy::StrType &) override {}
205209
void visit (const TyTy::NeverType &) override {}
206210
void visit (const TyTy::PlaceholderType &) override {}

gcc/rust/typecheck/rust-tyty-call.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,13 @@ TypeCheckCallExpr::visit (FnPtr &type)
300300
resolved = type.get_return_type ()->monomorphized_clone ();
301301
}
302302

303+
void
304+
TypeCheckCallExpr::visit (ConstExprType &type)
305+
{
306+
// A constant expression is not a callable function.
307+
// We do nothing, which leaves 'resolved' as nullptr and fails gracefully.
308+
}
309+
303310
// method call checker
304311

305312
TypeCheckMethodCallExpr::TypeCheckMethodCallExpr (

gcc/rust/typecheck/rust-tyty-call.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ class TypeCheckCallExpr : private TyVisitor
6767
void visit (ConstInferType &) override { rust_unreachable (); }
6868
void visit (ConstErrorType &) override { rust_unreachable (); }
6969

70+
void visit (ConstExprType &type) override;
71+
7072
// tuple-structs
7173
void visit (ADTType &type) override;
7274

gcc/rust/typecheck/rust-tyty-variance-analysis-private.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,11 @@ template <typename VARIANCE> class VisitorBase final : public TyVisitor
165165

166166
void visit (ErrorType &type) override {}
167167

168+
void visit (TyTy::ConstExprType &type) override
169+
{
170+
// TODO: Traverse expression for variance
171+
}
172+
168173
void visit (PlaceholderType &type) override { rust_unreachable (); }
169174
void visit (InferType &type) override { rust_unreachable (); }
170175

0 commit comments

Comments
 (0)