This is an archive of the discontinued LLVM Phabricator instance.

[C] Make (c ? e1 : e2) noreturn only if both e1 and e2 are noreturn
ClosedPublic

Authored by MaskRay on Jan 2 2023, 8:41 PM.

Details

Summary

In C mode, if e1 has attribute((noreturn)) but e2 doesn't, (c ? e1 : e2)
is incorrectly noreturn and Clang codegen produces unreachable
which may lead to miscompiles (see [1] gawk/support/dfa.c).
This problem has been known since
8c6b56f39d967347f28dd9c93f1cffddf6d7e4cd (2010) or earlier.

Fix this by making the result type noreturn only if both e1 and e2 are
noreturn, matching GCC.

_Noreturn and [[noreturn]] do not have the aforementioned problem.

Fix https://github.com/llvm/llvm-project/issues/59792 [1]

Diff Detail

Event Timeline

MaskRay created this revision.Jan 2 2023, 8:41 PM
Herald added a project: Restricted Project. · View Herald TranscriptJan 2 2023, 8:41 PM
Herald added a subscriber: StephenFan. · View Herald Transcript
MaskRay requested review of this revision.Jan 2 2023, 8:41 PM
Herald added a project: Restricted Project. · View Herald TranscriptJan 2 2023, 8:41 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript
MaskRay updated this revision to Diff 485912.Jan 2 2023, 8:46 PM

use %itanium_abi_triple. remove -S

LGTM, but I have a suggested elaboration for the comment.

clang/lib/AST/ASTContext.cpp
10258–10278
MaskRay updated this revision to Diff 486061.Jan 3 2023, 12:53 PM
MaskRay marked an inline comment as done.

use @rjmccall's comment (thanks a lot!)

C2x clarified the behavior of *standard* attributes when determining a composite type, and this particular case really straddles that boundary. We support [[noreturn]] and _Noreturn as standard attributes in C as well as __attribute__((noreturn)) (as a nonstandard attribute with the same semantics as the standard attribute).

C2x 6.2.7p3 says, in part: "If one of the types has a standard attribute, the composite type also has that attribute." which is the current behavior before your patch. So this change introduces a regression for C2x for the standard attribute spelling but is fine for the GNU attribute spelling, but do we really want to have different answers depending on how the user spells the attribute? (FWIW, this change came in as part of the "unsequenced functions" attribute paper: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2956.htm).

Isn't the C feature not technically part of the type? I thought Clang was fairly unique in modeling noreturn the way we do.

Isn't the C feature not technically part of the type? I thought Clang was fairly unique in modeling noreturn the way we do.

That's true, I just verified I didn't change that as part of https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2764.pdf when doing [[noreturn]]. That makes this more reasonable than I was originally thinking, but it still makes me a bit uneasy. We form a composite type in two situations: needing a common type for ?: and when doing function redeclaration merging. It seems odd to me that we would treat those differently, but at the same time, the rationale for why ?: should be conservative makes a lot of sense to me.

What do GCC and ICC do in this case?

noreturn is a bit special among the attributes, perhaps it'd be worthwhile altering the standard to say that a composite is noreturn only of all of it's types are.

It'd appear that GCC merges them here:

https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/c/c-typeck.cc;h=e06f052eb46a72d3d50835330c5af975e7c52084;hb=HEAD#l708

/* For function types do not merge const qualifiers, but drop them
   if used inconsistently.  The middle-end uses these to mark const
   and noreturn functions.  */
if (TREE_CODE (pointed_to_1) == FUNCTION_TYPE)
  target_quals = (quals1 & quals2);
else
  target_quals = (quals1 | quals2);

Volatile functions are considered noreturn, as a result, this makes target_quals only volatile if both types are.
This behavior seems better.
Note that the same applies for [[gnu::const]] by the same logic.

Flow added a subscriber: Flow.Jan 4 2023, 2:38 AM

I am not a C language lawyer :) I wonder what else should be done to move this patch forward.
The https://github.com/llvm/llvm-project/issues/59792 has got some traction and has been added a candidate for the next 15.x patch release https://github.com/llvm/llvm-project/milestone/18

Confirmed this patch fixes the miscompile.

Right. While the spec side needs discussion (and almost certainly addressing!), I don't think anyone in the real world is expecting the Clang behaviour vs the GCC one. The Clang one as-is is the one that's less conservative, as aggressively imposing Noreturn can only lead to something breaking.

I don't have ICC available to test.

I am not a C language lawyer :) I wonder what else should be done to move this patch forward.
The https://github.com/llvm/llvm-project/issues/59792 has got some traction and has been added a candidate for the next 15.x patch release https://github.com/llvm/llvm-project/milestone/18

We should test that this doesn't affect functions declared using the standard C attributes. Otherwise, I think we can go forward with this change. Aaron, does that seem reasonable to you? For better or worse, the different spellings of noreturn do have different behavior already, since the standard spellings aren't supposed to be type-affecting. And I feel like the suggested rule here is clearly correct if we have the flexibility to set it on our own.

MaskRay updated this revision to Diff 486453.Jan 4 2023, 7:54 PM

show that C23 [[noreturn]] codegen is unaffected.

The diff base improves attr-noreturn.c

aaron.ballman accepted this revision.Jan 5 2023, 6:05 AM

I am not a C language lawyer :) I wonder what else should be done to move this patch forward.
The https://github.com/llvm/llvm-project/issues/59792 has got some traction and has been added a candidate for the next 15.x patch release https://github.com/llvm/llvm-project/milestone/18

We should test that this doesn't affect functions declared using the standard C attributes. Otherwise, I think we can go forward with this change. Aaron, does that seem reasonable to you? For better or worse, the different spellings of noreturn do have different behavior already, since the standard spellings aren't supposed to be type-affecting. And I feel like the suggested rule here is clearly correct if we have the flexibility to set it on our own.

Yeah, I think this is the right approach. I was worried about the inconsistency with function declaration merging, but [[noreturn]] is required to be on the first declaration (6.7.12.6p3) and so there's no merging issue there. _Noreturn does not have the same requirement but neither _Noreturn nor [[noreturn]] are part of the function type anyway. So yeah, this seems like the right way to go in this case, thank you!

Please add a release note for the changes.

This revision is now accepted and ready to land.Jan 5 2023, 6:05 AM
MaskRay updated this revision to Diff 486641.Jan 5 2023, 11:34 AM
MaskRay edited the summary of this revision. (Show Details)

add release note (this makes it slightly difficult to backport)