Skip to content

Commit

Permalink
Fix rust breakage with nr2
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
P-E-P committed Nov 25, 2024
1 parent fa54737 commit be683fc
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 1 deletion.
76 changes: 76 additions & 0 deletions gcc/rust/resolve/rust-late-name-resolver-2.0.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 {
Expand Down Expand Up @@ -176,6 +178,73 @@ Late::visit (AST::SelfParam &param)
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<AST::IdentifierExpr &> (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)
{
Expand All @@ -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 (),
Expand Down
3 changes: 3 additions & 0 deletions gcc/rust/resolve/rust-late-name-resolver-2.0.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
1 change: 0 additions & 1 deletion gcc/testsuite/rust/compile/nr2/exclude
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit be683fc

Please sign in to comment.