diff options
author | Fangrui Song <i@maskray.me> | 2023-01-05 14:36:36 -0800 |
---|---|---|
committer | Michał Górny <mgorny@gentoo.org> | 2023-01-13 08:50:32 +0100 |
commit | 1814c624437dda530cd5c5a0d8846526907dd15d (patch) | |
tree | a9ad455720597d907cadd716a5cc3f3e524f69a9 | |
parent | [test] Test __attribute__((noreturn)), _Noreturn, and [[return]] with conditi... (diff) | |
download | llvm-project-1814c624437dda530cd5c5a0d8846526907dd15d.tar.gz llvm-project-1814c624437dda530cd5c5a0d8846526907dd15d.tar.bz2 llvm-project-1814c624437dda530cd5c5a0d8846526907dd15d.zip |
[C] Make (c ? e1 : e2) noreturn only if both e1 and e2 are noreturn
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]
Reviewed By: aaron.ballman
Differential Revision: https://reviews.llvm.org/D140868
Gentoo-Component: clang
-rw-r--r-- | clang/docs/ReleaseNotes.rst | 4 | ||||
-rw-r--r-- | clang/include/clang/AST/ASTContext.h | 10 | ||||
-rw-r--r-- | clang/lib/AST/ASTContext.cpp | 36 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 4 | ||||
-rw-r--r-- | clang/test/CodeGen/attr-noreturn.c | 1 |
5 files changed, 42 insertions, 13 deletions
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 13cca2ebbfb8..f726f7054b0e 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -231,6 +231,10 @@ Bug Fixes and Clang 15 accidentally stopped predeclaring those functions in that language mode. Clang 16 now predeclares those functions again. This fixes `Issue 56607 <https://github.com/llvm/llvm-project/issues/56607>`_. +- In C mode, when ``e1`` has ``__attribute__((noreturn))`` but ``e2`` doesn't, + ``(c ? e1 : e2)`` is no longer considered noreturn. + `Issue 59792 <https://github.com/llvm/llvm-project/issues/59792>`_ + (backported to 15.x by Gentoo) Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 9536b3faa02d..2afef61a1185 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2853,10 +2853,12 @@ public: bool canBindObjCObjectType(QualType To, QualType From); // Functions for calculating composite types - QualType mergeTypes(QualType, QualType, bool OfBlockPointer=false, - bool Unqualified = false, bool BlockReturnType = false); - QualType mergeFunctionTypes(QualType, QualType, bool OfBlockPointer=false, - bool Unqualified = false, bool AllowCXX = false); + QualType mergeTypes(QualType, QualType, bool OfBlockPointer = false, + bool Unqualified = false, bool BlockReturnType = false, + bool IsConditionalOperator = false); + QualType mergeFunctionTypes(QualType, QualType, bool OfBlockPointer = false, + bool Unqualified = false, bool AllowCXX = false, + bool IsConditionalOperator = false); QualType mergeFunctionParameterTypes(QualType, QualType, bool OfBlockPointer = false, bool Unqualified = false); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index cfd7bf604542..21316e963249 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -10077,7 +10077,8 @@ QualType ASTContext::mergeFunctionParameterTypes(QualType lhs, QualType rhs, QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, bool OfBlockPointer, bool Unqualified, - bool AllowCXX) { + bool AllowCXX, + bool IsConditionalOperator) { const auto *lbase = lhs->castAs<FunctionType>(); const auto *rbase = rhs->castAs<FunctionType>(); const auto *lproto = dyn_cast<FunctionProtoType>(lbase); @@ -10140,9 +10141,27 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, if (lbaseInfo.getNoCfCheck() != rbaseInfo.getNoCfCheck()) return {}; - // FIXME: some uses, e.g. conditional exprs, really want this to be 'both'. - bool NoReturn = lbaseInfo.getNoReturn() || rbaseInfo.getNoReturn(); - + // When merging declarations, it's common for supplemental information like + // attributes to only be present in one of the declarations, and we generally + // want type merging to preserve the union of information. So a merged + // function type should be noreturn if it was noreturn in *either* operand + // type. + // + // But for the conditional operator, this is backwards. The result of the + // operator could be either operand, and its type should conservatively + // reflect that. So a function type in a composite type is noreturn only + // if it's noreturn in *both* operand types. + // + // Arguably, noreturn is a kind of subtype, and the conditional operator + // ought to produce the most specific common supertype of its operand types. + // That would differ from this rule in contravariant positions. However, + // neither C nor C++ generally uses this kind of subtype reasoning. Also, + // as a practical matter, it would only affect C code that does abstraction of + // higher-order functions (taking noreturn callbacks!), which is uncommon to + // say the least. So we use the simpler rule. + bool NoReturn = IsConditionalOperator + ? lbaseInfo.getNoReturn() && rbaseInfo.getNoReturn() + : lbaseInfo.getNoReturn() || rbaseInfo.getNoReturn(); if (lbaseInfo.getNoReturn() != NoReturn) allLTypes = false; if (rbaseInfo.getNoReturn() != NoReturn) @@ -10275,9 +10294,9 @@ static QualType mergeEnumWithInteger(ASTContext &Context, const EnumType *ET, return {}; } -QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, - bool OfBlockPointer, - bool Unqualified, bool BlockReturnType) { +QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, + bool Unqualified, bool BlockReturnType, + bool IsConditionalOperator) { // For C++ we will not reach this code with reference types (see below), // for OpenMP variant call overloading we might. // @@ -10570,7 +10589,8 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, ArrayType::ArraySizeModifier(), 0); } case Type::FunctionNoProto: - return mergeFunctionTypes(LHS, RHS, OfBlockPointer, Unqualified); + return mergeFunctionTypes(LHS, RHS, OfBlockPointer, Unqualified, + /*AllowCXX=*/false, IsConditionalOperator); case Type::Record: case Type::Enum: return {}; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 83081bbf0aa0..1e5509da423e 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -8275,7 +8275,9 @@ static QualType checkConditionalPointerCompatibility(Sema &S, ExprResult &LHS, lhptee = S.Context.getQualifiedType(lhptee.getUnqualifiedType(), lhQual); rhptee = S.Context.getQualifiedType(rhptee.getUnqualifiedType(), rhQual); - QualType CompositeTy = S.Context.mergeTypes(lhptee, rhptee); + QualType CompositeTy = S.Context.mergeTypes( + lhptee, rhptee, /*OfBlockPointer=*/false, /*Unqualified=*/false, + /*BlockReturnType=*/false, /*IsConditionalOperator=*/true); if (CompositeTy.isNull()) { // In this situation, we assume void* type. No especially good diff --git a/clang/test/CodeGen/attr-noreturn.c b/clang/test/CodeGen/attr-noreturn.c index 875ede488dff..9040a4484d6c 100644 --- a/clang/test/CodeGen/attr-noreturn.c +++ b/clang/test/CodeGen/attr-noreturn.c @@ -13,6 +13,7 @@ void __attribute__((noreturn)) f(void) { // CHECK-LABEL: @test_conditional_gnu( // CHECK: %cond = select i1 %tobool, ptr @t1, ptr @t2 // CHECK: call void %cond( +// CHECK: call void %cond2( // CHECK-NEXT: unreachable // CHECK-CXX-LABEL: @_Z20test_conditional_gnui( |