From e90c20703fdc3de5ffbe87cf0ef6c2f280165315 Mon Sep 17 00:00:00 2001 From: Harishankar Date: Fri, 7 Nov 2025 01:45:25 +0530 Subject: [PATCH 1/2] Fix ICE with continue/break/return in while condition,Fixes #3977 --- .../typecheck/rust-hir-type-check-expr.cc | 36 ++++++++-- gcc/testsuite/rust/compile/issue-3977.rs | 72 +++++++++++++++++++ 2 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/rust/compile/issue-3977.rs diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc b/gcc/rust/typecheck/rust-hir-type-check-expr.cc index 7885dfcf746..e64885d9d72 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-expr.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc @@ -1541,18 +1541,46 @@ void TypeCheckExpr::visit (HIR::WhileLoopExpr &expr) { context->push_new_while_loop_context (expr.get_mappings ().get_hirid ()); - - TypeCheckExpr::Resolve (expr.get_predicate_expr ()); + TyTy::BaseType *predicate_type + = TypeCheckExpr::Resolve (expr.get_predicate_expr ()); + if (predicate_type->get_kind () == TyTy::TypeKind::ERROR) + { + infered = TyTy::TupleType::get_unit_type (); + context->pop_loop_context (); + return; + } + if (predicate_type->get_kind () == TyTy::TypeKind::NEVER) + { + rust_error_at (expr.get_predicate_expr ().get_locus (), + "expected boolean expression in % condition"); + infered = TyTy::TupleType::get_unit_type (); + context->pop_loop_context (); + return; + } + if (predicate_type->get_kind () != TyTy::TypeKind::BOOL) + { + rust_error_at (expr.get_predicate_expr ().get_locus (), + "expected boolean expression in % condition"); + infered = TyTy::TupleType::get_unit_type (); + context->pop_loop_context (); + return; + } TyTy::BaseType *block_expr = TypeCheckExpr::Resolve (expr.get_loop_block ()); - if (!block_expr->is_unit ()) { rust_error_at (expr.get_loop_block ().get_locus (), "expected %<()%> got %s", block_expr->as_string ().c_str ()); + infered = TyTy::TupleType::get_unit_type (); + context->pop_loop_context (); + return; + } + if (block_expr->get_kind () == TyTy::TypeKind::ERROR) + { + infered = TyTy::TupleType::get_unit_type (); + context->pop_loop_context (); return; } - context->pop_loop_context (); infered = TyTy::TupleType::get_unit_type (); } diff --git a/gcc/testsuite/rust/compile/issue-3977.rs b/gcc/testsuite/rust/compile/issue-3977.rs new file mode 100644 index 00000000000..93709d7c928 --- /dev/null +++ b/gcc/testsuite/rust/compile/issue-3977.rs @@ -0,0 +1,72 @@ +// Test for issue #3977: ICE in fold_convert_loc with diverging expressions in while conditions +// { dg-excess-errors "expected boolean expression" } + +fn test_continue() { + loop { + while continue {} // { dg-error "expected boolean expression in 'while' condition" } + } +} + +fn test_break() { + loop { + while break {} // { dg-error "expected boolean expression in 'while' condition" } + } +} + +fn test_return() { + while return {} // { dg-error "expected boolean expression in 'while' condition" } +} + +fn test_return_with_value() { + while return 42 {} // { dg-error "expected boolean expression in 'while' condition" } +} + +fn test_infinite_loop() { + while loop {} {} // { dg-error "expected boolean expression in 'while' condition" } +} + +fn test_nested_continue() { + loop { + loop { + while continue {} // { dg-error "expected boolean expression in 'while' condition" } + } + } +} + +fn test_labeled_break() { + 'outer: loop { + while break 'outer {} // { dg-error "expected boolean expression in 'while' condition" } + } +} + +fn test_labeled_continue() { + 'outer: loop { + while continue 'outer {} // { dg-error "expected boolean expression in 'while' condition" } + } +} + +// Valid cases that should NOT error +fn test_valid_boolean() { + while true {} + while false {} +} + +fn test_valid_expression() { + let x = 5; + while x > 0 {} +} + +fn test_valid_function_call() -> bool { + fn condition() -> bool { true } + while condition() {} + true +} + +fn main() { + test_valid_boolean(); + test_valid_expression(); + test_valid_function_call(); + + // The error cases would cause compilation to fail + // so they're tested separately +} \ No newline at end of file From c2a70d214acae26115e54b9ec88ad85783a2bffd Mon Sep 17 00:00:00 2001 From: Harishankar Date: Sun, 9 Nov 2025 20:15:39 +0530 Subject: [PATCH 2/2] Fix ICE with continue/break/return in while condition Fixes Rust-GCC#3977 The predicate expression must be evaluated before type checking to ensure side effects occur even when the predicate has never type. This prevents skipping function calls, panics, or other side effects in diverging predicates. gcc/rust/ChangeLog: * backend/rust-compile-expr.cc (CompileExpr::visit): Always evaluate predicate expression before checking for never type to preserve side effects in while loop conditions. gcc/testsuite/ChangeLog: * rust/compile/issue-3977.rs: New test. Signed-off-by: Harishankar --- gcc/rust/backend/rust-compile-expr.cc | 13 +++- .../typecheck/rust-hir-type-check-expr.cc | 26 +------ gcc/testsuite/rust/compile/issue-3977.rs | 75 +++++++++---------- 3 files changed, 48 insertions(+), 66 deletions(-) diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc index 0a627f35352..b89033841f2 100644 --- a/gcc/rust/backend/rust-compile-expr.cc +++ b/gcc/rust/backend/rust-compile-expr.cc @@ -803,7 +803,18 @@ CompileExpr::visit (HIR::WhileLoopExpr &expr) ctx->add_statement (loop_begin_label_decl); ctx->push_loop_begin_label (loop_begin_label); - tree condition = CompileExpr::Compile (expr.get_predicate_expr (), ctx); + HIR::Expr &predicate = expr.get_predicate_expr (); + TyTy::BaseType *predicate_type = nullptr; + bool ok + = ctx->get_tyctx ()->lookup_type (predicate.get_mappings ().get_hirid (), + &predicate_type); + rust_assert (ok && predicate_type != nullptr); + tree condition = CompileExpr::Compile (predicate, ctx); + if (predicate_type->get_kind () == TyTy::TypeKind::NEVER) + { + ctx->add_statement (condition); + condition = boolean_true_node; + } tree exit_condition = fold_build1_loc (expr.get_locus (), TRUTH_NOT_EXPR, boolean_type_node, condition); tree exit_expr = Backend::exit_expression (exit_condition, expr.get_locus ()); diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc b/gcc/rust/typecheck/rust-hir-type-check-expr.cc index e64885d9d72..5162cc6f9bb 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-expr.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc @@ -1543,25 +1543,11 @@ TypeCheckExpr::visit (HIR::WhileLoopExpr &expr) context->push_new_while_loop_context (expr.get_mappings ().get_hirid ()); TyTy::BaseType *predicate_type = TypeCheckExpr::Resolve (expr.get_predicate_expr ()); - if (predicate_type->get_kind () == TyTy::TypeKind::ERROR) - { - infered = TyTy::TupleType::get_unit_type (); - context->pop_loop_context (); - return; - } - if (predicate_type->get_kind () == TyTy::TypeKind::NEVER) - { - rust_error_at (expr.get_predicate_expr ().get_locus (), - "expected boolean expression in % condition"); - infered = TyTy::TupleType::get_unit_type (); - context->pop_loop_context (); - return; - } - if (predicate_type->get_kind () != TyTy::TypeKind::BOOL) + if (predicate_type->get_kind () != TyTy::TypeKind::BOOL + && predicate_type->get_kind () != TyTy::TypeKind::NEVER) { rust_error_at (expr.get_predicate_expr ().get_locus (), "expected boolean expression in % condition"); - infered = TyTy::TupleType::get_unit_type (); context->pop_loop_context (); return; } @@ -1571,13 +1557,6 @@ TypeCheckExpr::visit (HIR::WhileLoopExpr &expr) rust_error_at (expr.get_loop_block ().get_locus (), "expected %<()%> got %s", block_expr->as_string ().c_str ()); - infered = TyTy::TupleType::get_unit_type (); - context->pop_loop_context (); - return; - } - if (block_expr->get_kind () == TyTy::TypeKind::ERROR) - { - infered = TyTy::TupleType::get_unit_type (); context->pop_loop_context (); return; } @@ -1630,7 +1609,6 @@ TypeCheckExpr::visit (HIR::ContinueExpr &expr) "% outside of a loop"); return; } - infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ()); } diff --git a/gcc/testsuite/rust/compile/issue-3977.rs b/gcc/testsuite/rust/compile/issue-3977.rs index 93709d7c928..ba4de544d44 100644 --- a/gcc/testsuite/rust/compile/issue-3977.rs +++ b/gcc/testsuite/rust/compile/issue-3977.rs @@ -1,72 +1,65 @@ -// Test for issue #3977: ICE in fold_convert_loc with diverging expressions in while conditions -// { dg-excess-errors "expected boolean expression" } +// Test for issue #3977 - ICE with continue/break/return in while condition + +fn diverge() -> ! { + loop {} +} fn test_continue() { loop { - while continue {} // { dg-error "expected boolean expression in 'while' condition" } + while continue {} } } -fn test_break() { +fn test_break() { loop { - while break {} // { dg-error "expected boolean expression in 'while' condition" } + while break {} } } fn test_return() { - while return {} // { dg-error "expected boolean expression in 'while' condition" } -} - -fn test_return_with_value() { - while return 42 {} // { dg-error "expected boolean expression in 'while' condition" } -} - -fn test_infinite_loop() { - while loop {} {} // { dg-error "expected boolean expression in 'while' condition" } -} - -fn test_nested_continue() { loop { - loop { - while continue {} // { dg-error "expected boolean expression in 'while' condition" } - } + while return {} } } fn test_labeled_break() { 'outer: loop { - while break 'outer {} // { dg-error "expected boolean expression in 'while' condition" } + loop { + while break 'outer {} + } } } fn test_labeled_continue() { 'outer: loop { - while continue 'outer {} // { dg-error "expected boolean expression in 'while' condition" } + loop { + while continue 'outer {} + } } } -// Valid cases that should NOT error -fn test_valid_boolean() { - while true {} - while false {} -} - -fn test_valid_expression() { - let x = 5; - while x > 0 {} +fn test_complex_if_else() { + loop { + while if true { continue } else { break } {} + } } -fn test_valid_function_call() -> bool { - fn condition() -> bool { true } - while condition() {} - true +fn foo() { + while diverge() { + break + } + let _x = 5; } fn main() { - test_valid_boolean(); - test_valid_expression(); - test_valid_function_call(); - - // The error cases would cause compilation to fail - // so they're tested separately + // Just reference them so they're "used" + if false { + test_continue(); + test_break(); + test_return(); + test_labeled_break(); + test_labeled_continue(); + test_complex_if_else(); + foo(); + } } \ No newline at end of file