diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index fe82ab42ec8..e1a4297c3a7 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -133,6 +133,7 @@ GRS_OBJS = \
rust/rust-toplevel-name-resolver-2.0.o \
rust/rust-early-name-resolver-2.0.o \
rust/rust-finalize-imports-2.0.o \
+ rust/rust-ice-finalizer.o \
rust/rust-late-name-resolver-2.0.o \
rust/rust-immutable-name-resolution-context.o \
rust/rust-early-name-resolver.o \
diff --git a/gcc/rust/resolve/rust-ast-resolve-expr.cc b/gcc/rust/resolve/rust-ast-resolve-expr.cc
index 5936cf2c31e..a7490156031 100644
--- a/gcc/rust/resolve/rust-ast-resolve-expr.cc
+++ b/gcc/rust/resolve/rust-ast-resolve-expr.cc
@@ -22,8 +22,8 @@
#include "rust-ast-resolve-type.h"
#include "rust-ast-resolve-pattern.h"
#include "rust-ast-resolve-path.h"
-#include "diagnostic.h"
#include "rust-expr.h"
+#include "rust-ice-finalizer.h"
namespace Rust {
namespace Resolver {
@@ -108,45 +108,6 @@ ResolveExpr::visit (AST::AssignmentExpr &expr)
ResolveExpr::go (expr.get_right_expr (), prefix, canonical_prefix);
}
-/* The "break rust" Easter egg.
-
- Backstory: once upon a time, there used to be a bug in rustc: it would ICE
- during typechecking on a 'break' with an expression outside of a loop. The
- issue has been reported [0] and fixed [1], but in recognition of this, as a
- special Easter egg, "break rust" was made to intentionally cause an ICE.
-
- [0]: https://github.com/rust-lang/rust/issues/43162
- [1]: https://github.com/rust-lang/rust/pull/43745
-
- This was made in a way that does not break valid programs: namely, it only
- happens when the 'break' is outside of a loop (so invalid anyway).
-
- GCC Rust supports this essential feature as well, but in a slightly
- different way. Instead of delaying the error until type checking, we emit
- it here in the resolution phase. We, too, only do this to programs that
- are already invalid: we only emit our funny ICE if the name "rust" (which
- must be immediately inside a break-with-a-value expression) fails to
- resolve. Note that "break (rust)" does not trigger our ICE, only using
- "break rust" directly does, and only if there's no "rust" in scope. We do
- this in the same way regardless of whether the "break" is outside of a loop
- or inside one.
-
- As a GNU extension, we also support "break gcc", much to the same effect,
- subject to the same rules. */
-
-/* The finalizer for our funny ICE. This prints a custom message instead of
- the default bug reporting instructions, as there is no bug to report. */
-
-static void ATTRIBUTE_NORETURN
-funny_ice_finalizer (diagnostic_context *context,
- const diagnostic_info *diagnostic, diagnostic_t diag_kind)
-{
- gcc_assert (diag_kind == DK_ICE_NOBT);
- default_diagnostic_finalizer (context, diagnostic, diag_kind);
- fnotice (stderr, "You have broken GCC Rust. This is a feature.\n");
- exit (ICE_EXIT_CODE);
-}
-
void
ResolveExpr::visit (AST::IdentifierExpr &expr)
{
diff --git a/gcc/rust/resolve/rust-ice-finalizer.cc b/gcc/rust/resolve/rust-ice-finalizer.cc
new file mode 100644
index 00000000000..3ec8438289b
--- /dev/null
+++ b/gcc/rust/resolve/rust-ice-finalizer.cc
@@ -0,0 +1,35 @@
+// Copyright (C) 2020-2024 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// .
+
+#include "rust-ice-finalizer.h"
+
+namespace Rust {
+namespace Resolver {
+
+void ATTRIBUTE_NORETURN
+funny_ice_finalizer (diagnostic_context *context,
+ const diagnostic_info *diagnostic, diagnostic_t diag_kind)
+{
+ gcc_assert (diag_kind == DK_ICE_NOBT);
+ default_diagnostic_finalizer (context, diagnostic, diag_kind);
+ fnotice (stderr, "You have broken GCC Rust. This is a feature.\n");
+ exit (ICE_EXIT_CODE);
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/resolve/rust-ice-finalizer.h b/gcc/rust/resolve/rust-ice-finalizer.h
new file mode 100644
index 00000000000..c73a729fd6d
--- /dev/null
+++ b/gcc/rust/resolve/rust-ice-finalizer.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2020-2024 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// .
+
+#ifndef RUST_ICE_FINALIZER_H
+#define RUST_ICE_FINALIZER_H
+
+#include "rust-linemap.h"
+#include "diagnostic.h"
+
+namespace Rust {
+namespace Resolver {
+
+/* The "break rust" Easter egg.
+
+ Backstory: once upon a time, there used to be a bug in rustc: it would ICE
+ during typechecking on a 'break' with an expression outside of a loop. The
+ issue has been reported [0] and fixed [1], but in recognition of this, as a
+ special Easter egg, "break rust" was made to intentionally cause an ICE.
+
+ [0]: https://github.com/rust-lang/rust/issues/43162
+ [1]: https://github.com/rust-lang/rust/pull/43745
+
+ This was made in a way that does not break valid programs: namely, it only
+ happens when the 'break' is outside of a loop (so invalid anyway).
+
+ GCC Rust supports this essential feature as well, but in a slightly
+ different way. Instead of delaying the error until type checking, we emit
+ it here in the resolution phase. We, too, only do this to programs that
+ are already invalid: we only emit our funny ICE if the name "rust" (which
+ must be immediately inside a break-with-a-value expression) fails to
+ resolve. Note that "break (rust)" does not trigger our ICE, only using
+ "break rust" directly does, and only if there's no "rust" in scope. We do
+ this in the same way regardless of whether the "break" is outside of a loop
+ or inside one.
+
+ As a GNU extension, we also support "break gcc", much to the same effect,
+ subject to the same rules. */
+
+/* The finalizer for our funny ICE. This prints a custom message instead of
+ the default bug reporting instructions, as there is no bug to report. */
+
+void ATTRIBUTE_NORETURN
+funny_ice_finalizer (diagnostic_context *context,
+ const diagnostic_info *diagnostic, diagnostic_t diag_kind);
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif /* ! RUST_ICE_FINALIZER_H */
diff --git a/gcc/rust/resolve/rust-late-name-resolver-2.0.cc b/gcc/rust/resolve/rust-late-name-resolver-2.0.cc
index b28d38f3e39..dca9f95c203 100644
--- a/gcc/rust/resolve/rust-late-name-resolver-2.0.cc
+++ b/gcc/rust/resolve/rust-late-name-resolver-2.0.cc
@@ -27,6 +27,8 @@
#include "rust-system.h"
#include "rust-tyty.h"
#include "rust-hir-type-check.h"
+#include "rust-ice-finalizer.h"
+#include "rust-ast.h"
namespace Rust {
namespace Resolver2_0 {
@@ -177,6 +179,34 @@ Late::visit (AST::SelfParam ¶m)
param.get_node_id ());
}
+void
+Late::visit (AST::BreakExpr &expr)
+{
+ if (expr.has_break_expr ())
+ {
+ auto &break_expr = expr.get_break_expr ();
+ if (break_expr.get_expr_kind () == AST::Expr::Kind::Identifier)
+ {
+ /* This is a break with an expression, and the expression is
+ just a single identifier. See if the identifier is either
+ "rust" or "gcc", in which case we have "break rust" or "break
+ gcc", and so may need to emit our funny error. We cannot yet
+ emit the error here though, because the identifier may still
+ be in scope, and ICE'ing on valid programs would not be very
+ funny. */
+ std::string ident
+ = static_cast (expr.get_break_expr ())
+ .as_string ();
+ if (ident == "rust" || ident == "gcc")
+ funny_error = true;
+ }
+ }
+
+ DefaultResolver::visit (expr);
+
+ funny_error = false;
+}
+
void
Late::visit (AST::IdentifierExpr &expr)
{
@@ -192,6 +222,13 @@ Late::visit (AST::IdentifierExpr &expr)
{
resolved = type;
}
+ else if (funny_error)
+ {
+ diagnostic_finalizer (global_dc) = Resolver::funny_ice_finalizer;
+ emit_diagnostic (DK_ICE_NOBT, expr.get_locus (), -1,
+ "are you trying to break %s? how dare you?",
+ expr.as_string ().c_str ());
+ }
else
{
rust_error_at (expr.get_locus (),
diff --git a/gcc/rust/resolve/rust-late-name-resolver-2.0.h b/gcc/rust/resolve/rust-late-name-resolver-2.0.h
index e3f636d78f8..f4bd9f5734a 100644
--- a/gcc/rust/resolve/rust-late-name-resolver-2.0.h
+++ b/gcc/rust/resolve/rust-late-name-resolver-2.0.h
@@ -45,6 +45,7 @@ class Late : public DefaultResolver
// resolutions
void visit (AST::IdentifierExpr &) override;
+ void visit (AST::BreakExpr &) override;
void visit (AST::PathInExpression &) override;
void visit (AST::LangItemPath &) override;
void visit (AST::TypePath &) override;
@@ -60,6 +61,8 @@ class Late : public DefaultResolver
private:
/* Setup Rust's builtin types (u8, i32, !...) in the resolver */
void setup_builtin_types ();
+
+ bool funny_error;
};
// TODO: Add missing mappings and data structures
diff --git a/gcc/testsuite/rust/compile/nr2/exclude b/gcc/testsuite/rust/compile/nr2/exclude
index da5880d9a57..36566cecf50 100644
--- a/gcc/testsuite/rust/compile/nr2/exclude
+++ b/gcc/testsuite/rust/compile/nr2/exclude
@@ -1,6 +1,5 @@
bounds1.rs
break-rust2.rs
-break-rust3.rs
macros/builtin/eager1.rs
macros/builtin/eager2.rs
macros/builtin/recurse2.rs