Code change:
This code crashes in LLVM during codegen on stable, beta, and nightly: This originally happened in real-world code (a code base derived from ). <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --This issue has been assigned to via . <!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
We generally don't guarantee behavior with no_mangle, I think. I guess a segfault in llvm is unexpected though :)
I think the is a red herring, since the name of that function doesn't actually matter (ie. it can be or something completely harmless). I think it's just needed to make sure the function is codegen-ed.
I keep seeing this in my output when I play around with this on the playpen: that does not use , adding credence to 's point that it is probably a red herring.
triage: P-high. Leaving nominated in hopes that someone at triage meeting has a bright idea has to what's going on here (and what we might do about it).
Hm, bad_alloc implies OOM to me, but maybe that's not always the case.
Compiling with LLVM debug symbols + assertions, I get the same assertion failure for both: Tracing the callstack with gdb, the assertion is triggered on the call in this function: As the error suggests, this means that the downcast from to failed for . Printing out what type is (with some casting because gdb doesn't know what the return types are): That's strange -- it's reported as a function pointer, which is the same as what's reported for actual s, but we're still failing the downcast. Usually, dumping an should give you the contents of the function. For example: gives us But dumping gives this: So it appears to be pointing to a instruction? I'm not 100% sure -- I've ever seen a instruction being prefixed with a type, but I've taken it to be indicating the type of , the operand. I'll do a bit more digging. claim
After manually removing the LLVM build to force a complete rebuild with debug symbols, I was able to get a proper dump which makes far more sense: Note that this is not a bitcast instruction but a constant expr bitcast -- ie, one generated at compile time. As it turns out, in the event that a function exists with athe same name but a different type in a module, will return a compile time bitcast to that function: which is the overload called here So, later, when we attempt to use any functions that do a downcast to , we trigger the assertion failure because the downcast from constant expr bitcast to is not valid. We run into this in both your cases because when codegen attempts to insert a declaration for with signature , the program's already exists in the module with the signature , so the bitcast is returned instead of an . This issue can be reproduced without causing a crash by forcing a name collision with a function that's not (as we don't end up trying to cast the bitcast to a Function in this scenario): Compiling with we get this bitcode here: which I think exposes the problem more clearly: In an extern block which declares a function with the same name as some existing function within the module, we'll end up generating a call to the function in the module (potentially with an unsafe bitcast), instead of shadowing the module's function and generating a call to the external function (which, to me, appeared to be the intent of the example). You can see that this example shows that the in the original example isn't quite so much a red herring as it might seem -- in my example particularly, it's needed to ensure the name collision which demonstrates this behaviour. The examples without work because we induce a name collision on the unmangled function instead (not the user-defined one; the one which calls ). At this point, I'm not sure how to proceed. Should we error out here because of the name collision to avoid generating crazy function pointer bitcasts? Or should we do some kind of trickery to rename the shadowing symbol so that it correctly calls the extern function? Or is it as says -- is the current codegen into bitcast fine because we don't make any behaviour guarantees for symbols?

Related discussion:
text: // listing. let main_ret_ty = cx.tcx().erase_regions(&main_ret_ty.no_bound_vars().unwrap()); <del> if cx.get_defined_value("main").is_some() { </del> <ins> if cx.get_declared_value("main").is_some() { </ins> // FIXME: We should be smart and show a better diagnostic here. cx.sess() <del> .struct_span_err(sp, "entry symbol `main` defined multiple times") </del> <ins> .struct_span_err(sp, "entry symbol `main` declared multiple times") </ins> .help("did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead") .emit(); cx.sess().abort_if_errors();