Skip to content

Commit 8e64c72

Browse files
committed
resolve: Add "break rust" Easter egg
When we encounter a "break rust" statement, emit a funny error message and intentionally cause an ICE. This matches the corresponding Easter egg in rustc. As a GNU extension, "break gcc" is also supported. The conditions for this to happen are: * The break expression must be literally "rust" or "gcc". For instance, "break (rust)" will not trigger the Easter egg. * The name ("rust" or "gcc") must not be in scope; if it is, no error is emitted, and the compilation proceeds as usual. In other words, this only affects how GCC diagnoses programs that would fail to compile anyway. Closes #1996 gcc/rust/ChangeLog: * resolve/rust-ast-resolve-expr.cc: Add "break rust" Easter egg gcc/testsuite/ChangeLog: * gcc/testsuite/lib/prune.exp (prune_ices): Also prune "You broke GCC Rust. This is a feature." * rust/compile/break-rust1.rs: New test * rust/compile/break-rust2.rs: New test * rust/compile/break-rust3.rs: New test Signed-off-by: Sergey Bugaev <[email protected]>
1 parent f627629 commit 8e64c72

File tree

5 files changed

+53
-1
lines changed

5 files changed

+53
-1
lines changed

gcc/rust/resolve/rust-ast-resolve-expr.cc

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "rust-ast-resolve-type.h"
2424
#include "rust-ast-resolve-pattern.h"
2525
#include "rust-ast-resolve-path.h"
26+
#include "diagnostic.h"
2627

2728
namespace Rust {
2829
namespace Resolver {
@@ -105,6 +106,19 @@ ResolveExpr::visit (AST::AssignmentExpr &expr)
105106
VerifyAsignee::go (expr.get_left_expr ().get ());
106107
}
107108

109+
/* The finalizer for our funny ICE. This prints a custom message instead of
110+
the default bug reporting instructions, as there is no bug to report. */
111+
112+
static void ATTRIBUTE_NORETURN
113+
funny_ice_finalizer (diagnostic_context *context, diagnostic_info *diagnostic,
114+
diagnostic_t diag_kind)
115+
{
116+
gcc_assert (diag_kind == DK_ICE_NOBT);
117+
default_diagnostic_finalizer (context, diagnostic, diag_kind);
118+
fnotice (stderr, "You broke GCC Rust. This is a feature.\n");
119+
exit (ICE_EXIT_CODE);
120+
}
121+
108122
void
109123
ResolveExpr::visit (AST::IdentifierExpr &expr)
110124
{
@@ -120,6 +134,17 @@ ResolveExpr::visit (AST::IdentifierExpr &expr)
120134
{
121135
resolver->insert_resolved_type (expr.get_node_id (), resolved_node);
122136
}
137+
else if (funny_error)
138+
{
139+
/* This was a "break rust" or "break gcc", and the identifier failed to
140+
resolve. Emit a funny ICE. We set the finalizer to our custom one,
141+
and use the lower-level emit_diagnostic () instead of the more common
142+
internal_error_no_backtrace () in order to pass our locus. */
143+
diagnostic_finalizer (global_dc) = funny_ice_finalizer;
144+
emit_diagnostic (DK_ICE_NOBT, expr.get_locus ().gcc_location (), -1,
145+
"are you trying to break %s? how dare you?",
146+
expr.as_string ().c_str ());
147+
}
123148
else
124149
{
125150
rust_error_at (expr.get_locus (), "failed to find name: %s",
@@ -384,7 +409,18 @@ ResolveExpr::visit (AST::BreakExpr &expr)
384409
}
385410

386411
if (expr.has_break_expr ())
387-
ResolveExpr::go (expr.get_break_expr ().get (), prefix, canonical_prefix);
412+
{
413+
bool funny_error = false;
414+
AST::Expr &break_expr = *expr.get_break_expr ().get ();
415+
if (break_expr.get_ast_kind () == AST::Kind::IDENTIFIER)
416+
{
417+
std::string ident
418+
= static_cast<AST::IdentifierExpr &> (break_expr).as_string ();
419+
if (ident == "rust" || ident == "gcc")
420+
funny_error = true;
421+
}
422+
ResolveExpr::go (&break_expr, prefix, canonical_prefix, funny_error);
423+
}
388424
}
389425

390426
void

gcc/testsuite/lib/prune.exp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ proc prune_file_path { text } {
158158
proc prune_ices { text } {
159159
regsub -all "(^|\n)\[^\n\]*: internal compiler error:.*\nSee \[^\n\]*" $text "" text
160160
regsub -all "(^|\n|')*Internal compiler error:.*\nSee \[^\n\]*" $text "" text
161+
regsub -all "(^|\n)\[^\n\]*: internal compiler error:.*\nYou broke GCC Rust. This is a feature." $text "" text
161162
return $text
162163
}
163164

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fn main() {
2+
let rust = "crab";
3+
let res = loop {
4+
// { dg-warning "unused name" "" { target *-*-* } .-1 }
5+
break rust;
6+
};
7+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn main() {
2+
break (rust);
3+
// { dg-error "failed to find name: rust" "" { target *-*-* } .-1 }
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn main() {
2+
break rust;
3+
// { dg-ice "are you trying to break rust? how dare you?" }
4+
}

0 commit comments

Comments
 (0)