From be683fc9f8d4580696418ae516dcbce73d1eb150 Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Patry Date: Mon, 25 Nov 2024 18:04:06 +0100 Subject: [PATCH] Fix rust breakage with nr2 Nr2 did not emit the correct error message for break identifier "rust". gcc/rust/ChangeLog: * resolve/rust-late-name-resolver-2.0.cc (Late::visit): Add "rust" identifier detection akin to nr1. (funny_ice_finalizer): Copy ICE finalizer from nr1. * resolve/rust-late-name-resolver-2.0.h: Add funny_error member context state. gcc/testsuite/ChangeLog: * rust/compile/nr2/exclude: Remove break-rust3.rs from exclude list. Signed-off-by: Pierre-Emmanuel Patry --- .../resolve/rust-late-name-resolver-2.0.cc | 76 +++++++++++++++++++ .../resolve/rust-late-name-resolver-2.0.h | 3 + gcc/testsuite/rust/compile/nr2/exclude | 1 - 3 files changed, 79 insertions(+), 1 deletion(-) 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 a96dc1ccf346..13b1dd8a094b 100644 --- a/gcc/rust/resolve/rust-late-name-resolver-2.0.cc +++ b/gcc/rust/resolve/rust-late-name-resolver-2.0.cc @@ -18,6 +18,7 @@ #include "optional.h" #include "rust-ast-full.h" +#include "rust-ast.h" #include "rust-hir-map.h" #include "rust-late-name-resolver-2.0.h" #include "rust-default-resolver.h" @@ -26,6 +27,7 @@ #include "rust-system.h" #include "rust-tyty.h" #include "rust-hir-type-check.h" +#include "diagnostic.h" namespace Rust { namespace Resolver2_0 { @@ -176,6 +178,73 @@ 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_ast_kind () == AST::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; +} + +/* 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 Late::visit (AST::IdentifierExpr &expr) { @@ -191,6 +260,13 @@ Late::visit (AST::IdentifierExpr &expr) { resolved = type; } + else if (funny_error) + { + diagnostic_finalizer (global_dc) = 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 59077035ce3c..11647f23934b 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::TypePath &) override; void visit (AST::StructExprStruct &) override; @@ -57,6 +58,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 6c589e4ab4e5..ccb8785fff0e 100644 --- a/gcc/testsuite/rust/compile/nr2/exclude +++ b/gcc/testsuite/rust/compile/nr2/exclude @@ -4,7 +4,6 @@ attr_deprecated_2.rs bad=file-name.rs bounds1.rs break-rust2.rs -break-rust3.rs macros/builtin/eager1.rs macros/builtin/eager2.rs macros/builtin/recurse2.rs