From: Harishankar <[email protected]>
Fixes Rust-GCC/gccrs#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.
Proof of fix using -fdump-tree-gimple:
__attribute__((cdecl))
struct () test::main ()
{
struct () D.107;
<D.101>:
<D.103>:
{
<D.104>:
{
<D.102>:
goto <D.102>; // Side-effect correctly preserved
if (0 != 0) goto <D.105>; else goto <D.106>;
<D.106>:
{
}
}
goto <D.104>;
<D.105>:
}
goto <D.103>;
return D.107;
}
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.
* typecheck/rust-hir-type-check-expr.cc: Update handling of
break/continue.
gcc/testsuite/ChangeLog:
* rust/compile/issue-3977.rs: New test.
Signed-off-by: Harishankar <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.
Commit on github:
https://github.com/Rust-GCC/gccrs/commit/f947b54cfc0360a0748280e77814b377dd077012
The commit has been mentioned in the following pull-request(s):
- https://github.com/Rust-GCC/gccrs/pull/4270
gcc/rust/backend/rust-compile-expr.cc | 13 +++-
.../typecheck/rust-hir-type-check-expr.cc | 16 +++--
gcc/testsuite/rust/compile/issue-3977.rs | 65 +++++++++++++++++++
3 files changed, 88 insertions(+), 6 deletions(-)
create mode 100644 gcc/testsuite/rust/compile/issue-3977.rs
diff --git a/gcc/rust/backend/rust-compile-expr.cc
b/gcc/rust/backend/rust-compile-expr.cc
index 9a9c31590..f2bca2d6d 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 b7374900e..0188ceae0 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
@@ -1550,18 +1550,25 @@ 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::BOOL
+ && predicate_type->get_kind () != TyTy::TypeKind::NEVER)
+ {
+ rust_error_at (expr.get_predicate_expr ().get_locus (),
+ "expected boolean expression in %<while%> condition");
+ 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 ());
+ context->pop_loop_context ();
return;
}
-
context->pop_loop_context ();
infered = TyTy::TupleType::get_unit_type ();
}
@@ -1611,7 +1618,6 @@ TypeCheckExpr::visit (HIR::ContinueExpr &expr)
"%<continue%> 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
new file mode 100644
index 000000000..ba4de544d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-3977.rs
@@ -0,0 +1,65 @@
+// Test for issue #3977 - ICE with continue/break/return in while condition
+
+fn diverge() -> ! {
+ loop {}
+}
+
+fn test_continue() {
+ loop {
+ while continue {}
+ }
+}
+
+fn test_break() {
+ loop {
+ while break {}
+ }
+}
+
+fn test_return() {
+ loop {
+ while return {}
+ }
+}
+
+fn test_labeled_break() {
+ 'outer: loop {
+ loop {
+ while break 'outer {}
+ }
+ }
+}
+
+fn test_labeled_continue() {
+ 'outer: loop {
+ loop {
+ while continue 'outer {}
+ }
+ }
+}
+
+fn test_complex_if_else() {
+ loop {
+ while if true { continue } else { break } {}
+ }
+}
+
+fn foo() {
+ while diverge() {
+ break
+ }
+ let _x = 5;
+}
+
+fn main() {
+ // 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
base-commit: 09de0d04151a75db8e4ad29e9d3bc6817802a803
--
2.52.0