aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFangrui Song <i@maskray.me>2023-01-05 14:36:36 -0800
committerMichał Górny <mgorny@gentoo.org>2023-01-13 08:50:32 +0100
commit1814c624437dda530cd5c5a0d8846526907dd15d (patch)
treea9ad455720597d907cadd716a5cc3f3e524f69a9
parent[test] Test __attribute__((noreturn)), _Noreturn, and [[return]] with conditi... (diff)
downloadllvm-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.rst4
-rw-r--r--clang/include/clang/AST/ASTContext.h10
-rw-r--r--clang/lib/AST/ASTContext.cpp36
-rw-r--r--clang/lib/Sema/SemaExpr.cpp4
-rw-r--r--clang/test/CodeGen/attr-noreturn.c1
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(