Skip to content

Commit 91c9be0

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 #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. * rust/compile/const_generics_12.rs: Update expected error messages to match <const_expr> representation. Signed-off-by: Jayant Chauhan <[email protected]>
1 parent 32622b7 commit 91c9be0

17 files changed

+309
-15
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: 45 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"
@@ -27,6 +28,34 @@
2728
namespace Rust {
2829
namespace Compile {
2930

31+
// Universal helper to convert any Rust Constant into a GCC Tree
32+
static tree
33+
compile_const_to_tree (const TyTy::BaseConstType *capacity_const, Context *ctx,
34+
location_t locus)
35+
{
36+
switch (capacity_const->const_kind ())
37+
{
38+
case TyTy::BaseConstType::ConstKind::Expr:
39+
{
40+
auto &expr_type
41+
= *static_cast<const TyTy::ConstExprType *> (capacity_const);
42+
// Compile the expression (handle { N + 1 })
43+
return CompileExpr::Compile (*expr_type.get_expr (), ctx);
44+
}
45+
case TyTy::BaseConstType::ConstKind::Value:
46+
{
47+
auto &capacity_value
48+
= *static_cast<const TyTy::ConstValueType *> (capacity_const);
49+
// Return the already-known value (handle 2)
50+
return capacity_value.get_value ();
51+
}
52+
default:
53+
// FAIL GRACEFULLY: Return error instead of crashing on Param/Infer/Error
54+
rust_error_at (locus, "unexpected constant kind in backend compilation");
55+
return error_mark_node;
56+
}
57+
}
58+
3059
static const std::string RUST_ENUM_DISR_FIELD_NAME = "RUST$ENUM$DISR";
3160

3261
TyTyResolveCompile::TyTyResolveCompile (Context *ctx, bool trait_object_mode)
@@ -165,6 +194,12 @@ TyTyResolveCompile::visit (const TyTy::ConstErrorType &type)
165194
translated = error_mark_node;
166195
}
167196

197+
void
198+
TyTyResolveCompile::visit (const TyTy::ConstExprType &type)
199+
{
200+
translated = CompileExpr::Compile (*type.get_expr (), ctx);
201+
}
202+
168203
void
169204
TyTyResolveCompile::visit (const TyTy::ProjectionType &type)
170205
{
@@ -488,9 +523,11 @@ TyTyResolveCompile::visit (const TyTy::ArrayType &type)
488523
{
489524
tree element_type
490525
= TyTyResolveCompile::compile (ctx, type.get_element_type ());
526+
527+
// 1. Get capacity
491528
auto const_capacity = type.get_capacity ();
492529

493-
// Check if capacity is a const type
530+
// 2. CHECK if it is valid BEFORE using it
494531
if (const_capacity->get_kind () != TyTy::TypeKind::CONST)
495532
{
496533
rust_error_at (type.get_locus (), "array capacity is not a const type");
@@ -499,14 +536,15 @@ TyTyResolveCompile::visit (const TyTy::ArrayType &type)
499536
}
500537

501538
auto *capacity_const = const_capacity->as_const_type ();
539+
tree folded_capacity_expr
540+
= compile_const_to_tree (capacity_const, ctx, type.get_locus ());
502541

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 ();
542+
if (folded_capacity_expr == error_mark_node)
543+
{
544+
translated = error_mark_node;
545+
return;
546+
}
507547

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

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)