From 4be86b5e17f9d3d913d2d6099abbad673dcfdd9b 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. * Make-lang.in: Add new translation unit for new ice finalizer. * resolve/rust-ast-resolve-expr.cc: Move ice finalizer to it's own file. * resolve/rust-ice-finalizer.cc: New file. * resolve/rust-ice-finalizer.h: New file. gcc/testsuite/ChangeLog: * rust/compile/nr2/exclude: Remove break-rust3.rs from exclude list. Signed-off-by: Pierre-Emmanuel Patry --- gcc/rust/Make-lang.in | 1 + gcc/rust/resolve/rust-ast-resolve-expr.cc | 41 +----------- gcc/rust/resolve/rust-ice-finalizer.cc | 35 ++++++++++ gcc/rust/resolve/rust-ice-finalizer.h | 64 +++++++++++++++++++ .../resolve/rust-late-name-resolver-2.0.cc | 37 +++++++++++ .../resolve/rust-late-name-resolver-2.0.h | 3 + gcc/testsuite/rust/compile/nr2/exclude | 1 - 7 files changed, 141 insertions(+), 41 deletions(-) create mode 100644 gcc/rust/resolve/rust-ice-finalizer.cc create mode 100644 gcc/rust/resolve/rust-ice-finalizer.h diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 6de0a20b6a0a..f729285caf82 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 9c2be4a57160..32691cbf7761 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 000000000000..3ec8438289ba --- /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 000000000000..c73a729fd6da --- /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 a96dc1ccf346..e991a75c2c3a 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 "rust-ice-finalizer.h" namespace Rust { namespace Resolver2_0 { @@ -176,6 +178,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_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; +} + void Late::visit (AST::IdentifierExpr &expr) { @@ -191,6 +221,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 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