Skip to content

Commit ce25686

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 immediately during type checking path resolution. Previously, these expressions were allowed to propagate, leading to assertion failures and internal compiler errors (ICE) in the backend when it attempted to resolve them as concrete values. This patch adds validation in the typechecker to detect non-literal const generic arguments during path resolution. It rejects symbolic expressions gracefully by emitting a diagnostic, preventing the compiler from crashing. Fixes #4302 gcc/rust/ChangeLog: * typecheck/rust-hir-type-check-path.cc (TypeCheckExpr::resolve_root_path): Reject non-literal generic const arguments to prevent ICE. (TypeCheckExpr::resolve_segments): Likewise. gcc/testsuite/ChangeLog: * rust/compile/issue-4302.rs: New test. Signed-off-by: Jayant Chauhan <0001jayant@gmail.com>
1 parent 32622b7 commit ce25686

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

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

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,69 @@ TypeCheckExpr::visit (HIR::PathInExpression &expr)
241241
}
242242
}
243243

244+
/* Helper to check if a const generic expression is dependent (symbolic).
245+
Returns true if the expression contains paths or identifiers (e.g., { N + 1
246+
}). Returns false if the expression is purely literal/concrete (e.g., { 1 + 1
247+
}). */
248+
static bool
249+
is_const_dependent (HIR::Expr &expr)
250+
{
251+
switch (expr.get_expression_type ())
252+
{
253+
// These imply dependency on a name (likely a generic parameter)
254+
case HIR::Expr::ExprType::Path:
255+
return true;
256+
257+
// Literals are not dependent
258+
case HIR::Expr::ExprType::Lit:
259+
return false;
260+
261+
// Recurse into blocks
262+
case HIR::Expr::ExprType::Block:
263+
{
264+
auto &block = static_cast<HIR::BlockExpr &> (expr);
265+
if (block.has_expr ())
266+
// get_final_expr returns reference -> No '*'
267+
return is_const_dependent (block.get_final_expr ());
268+
269+
if (!block.get_statements ().empty ())
270+
return true;
271+
272+
return false;
273+
}
274+
275+
// Recurse into grouping (e.g., (N))
276+
case HIR::Expr::ExprType::Grouped:
277+
{
278+
auto &group = static_cast<HIR::GroupedExpr &> (expr);
279+
// get_expr_in_parens returns unique_ptr -> Needs '*'
280+
return is_const_dependent (*group.get_expr_in_parens ());
281+
}
282+
283+
default:
284+
break;
285+
}
286+
287+
// Fallback: Check specific classes using dynamic_cast.
288+
// This handles types like ArithmeticOrLogical and Negation regardless
289+
// of their enum mapping (which may be ExprType::Operator or similar).
290+
291+
if (auto *binary = dynamic_cast<HIR::ArithmeticOrLogicalExpr *> (&expr))
292+
{
293+
// get_lhs/rhs return unique_ptr -> Needs '*'
294+
return is_const_dependent (*binary->get_lhs ())
295+
|| is_const_dependent (*binary->get_rhs ());
296+
}
297+
298+
if (auto *neg = dynamic_cast<HIR::NegationExpr *> (&expr))
299+
{
300+
// get_expr returns unique_ptr -> Needs '*'
301+
return is_const_dependent (*neg->get_expr ());
302+
}
303+
304+
return false;
305+
}
306+
244307
TyTy::BaseType *
245308
TypeCheckExpr::resolve_root_path (HIR::PathInExpression &expr, size_t *offset,
246309
NodeId *root_resolved_node_id)
@@ -366,6 +429,25 @@ TypeCheckExpr::resolve_root_path (HIR::PathInExpression &expr, size_t *offset,
366429
// turbo-fish segment path::<ty>
367430
if (seg.has_generic_args ())
368431
{
432+
// Check for dependent const expressions (like { N + 1 })
433+
bool is_dependent = false;
434+
for (auto &arg : seg.get_generic_args ().get_const_args ())
435+
{
436+
if (is_const_dependent (*arg.get_expression ()))
437+
{
438+
is_dependent = true;
439+
break;
440+
}
441+
}
442+
443+
if (is_dependent)
444+
{
445+
*root_resolved_node_id = ref_node_id;
446+
*offset = *offset + 1;
447+
root_tyty = lookup;
448+
continue;
449+
}
450+
369451
lookup = SubstMapper::Resolve (lookup, expr.get_locus (),
370452
&seg.get_generic_args (),
371453
context->regions_from_generic_args (
@@ -524,6 +606,20 @@ TypeCheckExpr::resolve_segments (NodeId root_resolved_node_id,
524606

525607
if (seg.has_generic_args ())
526608
{
609+
// Check for dependent const expressions (like { N + 1 })
610+
bool is_dependent = false;
611+
for (auto &arg : seg.get_generic_args ().get_const_args ())
612+
{
613+
if (is_const_dependent (*arg.get_expression ()))
614+
{
615+
is_dependent = true;
616+
break;
617+
}
618+
}
619+
if (is_dependent)
620+
{
621+
continue;
622+
}
527623
rust_debug_loc (seg.get_locus (), "applying segment generics: %s",
528624
tyseg->as_string ().c_str ());
529625
tyseg
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pub struct Foo<const N: usize>;
2+
3+
pub fn foo<const N: usize>() -> Foo<{ N + 1 }> { // { dg-error "cannot evaluate constant expression with generic parameters" }
4+
Foo
5+
}
6+
// { dg-excess-errors "Noisy error cascade" }

0 commit comments

Comments
 (0)