mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-29 12:36:07 +00:00
Add Attribute NoThrow as an Exception Specifier Type
In response to https://bugs.llvm.org/show_bug.cgi?id=33235, it became clear that the current mechanism of hacking through checks for the exception specification of a function gets confused really quickly when there are alternate exception specifiers. This patch introcues EST_NoThrow, which is the equivilent of EST_noexcept when caused by EST_noThrow. The existing implementation is left in place to cover functions with no FunctionProtoType. Differential Revision: https://reviews.llvm.org/D62435 llvm-svn: 362119
This commit is contained in:
parent
51ce0b196a
commit
d02f4a1043
@ -32,7 +32,7 @@
|
|||||||
* compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable.
|
* compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable.
|
||||||
*/
|
*/
|
||||||
#define CINDEX_VERSION_MAJOR 0
|
#define CINDEX_VERSION_MAJOR 0
|
||||||
#define CINDEX_VERSION_MINOR 57
|
#define CINDEX_VERSION_MINOR 58
|
||||||
|
|
||||||
#define CINDEX_VERSION_ENCODE(major, minor) ( \
|
#define CINDEX_VERSION_ENCODE(major, minor) ( \
|
||||||
((major) * 10000) \
|
((major) * 10000) \
|
||||||
@ -221,7 +221,12 @@ enum CXCursor_ExceptionSpecificationKind {
|
|||||||
/**
|
/**
|
||||||
* The exception specification has not been parsed yet.
|
* The exception specification has not been parsed yet.
|
||||||
*/
|
*/
|
||||||
CXCursor_ExceptionSpecificationKind_Unparsed
|
CXCursor_ExceptionSpecificationKind_Unparsed,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cursor has a __declspec(nothrow) exception specification.
|
||||||
|
*/
|
||||||
|
CXCursor_ExceptionSpecificationKind_NoThrow
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2330,6 +2330,14 @@ public:
|
|||||||
return T->castAs<FunctionType>()->getReturnType();
|
return T->castAs<FunctionType>()->getReturnType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the ExceptionSpecificationType as declared.
|
||||||
|
ExceptionSpecificationType getExceptionSpecType() const {
|
||||||
|
auto *TSI = getTypeSourceInfo();
|
||||||
|
QualType T = TSI ? TSI->getType() : getType();
|
||||||
|
const auto *FPT = T->getAs<FunctionProtoType>();
|
||||||
|
return FPT ? FPT->getExceptionSpecType() : EST_None;
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempt to compute an informative source range covering the
|
/// Attempt to compute an informative source range covering the
|
||||||
/// function exception specification, if any.
|
/// function exception specification, if any.
|
||||||
SourceRange getExceptionSpecSourceRange() const;
|
SourceRange getExceptionSpecSourceRange() const;
|
||||||
|
@ -3855,6 +3855,7 @@ private:
|
|||||||
case EST_MSAny:
|
case EST_MSAny:
|
||||||
case EST_BasicNoexcept:
|
case EST_BasicNoexcept:
|
||||||
case EST_Unparsed:
|
case EST_Unparsed:
|
||||||
|
case EST_NoThrow:
|
||||||
return {0, 0, 0};
|
return {0, 0, 0};
|
||||||
|
|
||||||
case EST_Dynamic:
|
case EST_Dynamic:
|
||||||
|
@ -2792,6 +2792,9 @@ def warn_dllimport_dropped_from_inline_function : Warning<
|
|||||||
InGroup<IgnoredAttributes>;
|
InGroup<IgnoredAttributes>;
|
||||||
def warn_attribute_ignored : Warning<"%0 attribute ignored">,
|
def warn_attribute_ignored : Warning<"%0 attribute ignored">,
|
||||||
InGroup<IgnoredAttributes>;
|
InGroup<IgnoredAttributes>;
|
||||||
|
def warn_nothrow_attribute_ignored : Warning<"'nothrow' attribute conflicts with"
|
||||||
|
" exception specification; attribute ignored">,
|
||||||
|
InGroup<IgnoredAttributes>;
|
||||||
def warn_attribute_ignored_on_inline :
|
def warn_attribute_ignored_on_inline :
|
||||||
Warning<"%0 attribute ignored on inline function">,
|
Warning<"%0 attribute ignored on inline function">,
|
||||||
InGroup<IgnoredAttributes>;
|
InGroup<IgnoredAttributes>;
|
||||||
|
@ -22,6 +22,7 @@ enum ExceptionSpecificationType {
|
|||||||
EST_DynamicNone, ///< throw()
|
EST_DynamicNone, ///< throw()
|
||||||
EST_Dynamic, ///< throw(T1, T2)
|
EST_Dynamic, ///< throw(T1, T2)
|
||||||
EST_MSAny, ///< Microsoft throw(...) extension
|
EST_MSAny, ///< Microsoft throw(...) extension
|
||||||
|
EST_NoThrow, ///< Microsoft __declspec(nothrow) extension
|
||||||
EST_BasicNoexcept, ///< noexcept
|
EST_BasicNoexcept, ///< noexcept
|
||||||
EST_DependentNoexcept,///< noexcept(expression), value-dependent
|
EST_DependentNoexcept,///< noexcept(expression), value-dependent
|
||||||
EST_NoexceptFalse, ///< noexcept(expression), evals to 'false'
|
EST_NoexceptFalse, ///< noexcept(expression), evals to 'false'
|
||||||
@ -41,7 +42,8 @@ inline bool isComputedNoexcept(ExceptionSpecificationType ESpecType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline bool isNoexceptExceptionSpec(ExceptionSpecificationType ESpecType) {
|
inline bool isNoexceptExceptionSpec(ExceptionSpecificationType ESpecType) {
|
||||||
return ESpecType == EST_BasicNoexcept || isComputedNoexcept(ESpecType);
|
return ESpecType == EST_BasicNoexcept || ESpecType == EST_NoThrow ||
|
||||||
|
isComputedNoexcept(ESpecType);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isUnresolvedExceptionSpec(ExceptionSpecificationType ESpecType) {
|
inline bool isUnresolvedExceptionSpec(ExceptionSpecificationType ESpecType) {
|
||||||
|
@ -3742,7 +3742,10 @@ QualType ASTContext::getFunctionTypeInternal(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case EST_DynamicNone: case EST_BasicNoexcept: case EST_NoexceptTrue:
|
case EST_DynamicNone:
|
||||||
|
case EST_BasicNoexcept:
|
||||||
|
case EST_NoexceptTrue:
|
||||||
|
case EST_NoThrow:
|
||||||
CanonicalEPI.ExceptionSpec.Type = EST_BasicNoexcept;
|
CanonicalEPI.ExceptionSpec.Type = EST_BasicNoexcept;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -464,7 +464,9 @@ void JSONNodeDumper::VisitFunctionProtoType(const FunctionProtoType *T) {
|
|||||||
//JOS.attributeWithCall("exceptionSpecExpr",
|
//JOS.attributeWithCall("exceptionSpecExpr",
|
||||||
// [this, E]() { Visit(E.ExceptionSpec.NoexceptExpr); });
|
// [this, E]() { Visit(E.ExceptionSpec.NoexceptExpr); });
|
||||||
break;
|
break;
|
||||||
|
case EST_NoThrow:
|
||||||
|
JOS.attribute("exceptionSpec", "nothrow");
|
||||||
|
break;
|
||||||
// FIXME: I cannot find a way to trigger these cases while dumping the AST. I
|
// FIXME: I cannot find a way to trigger these cases while dumping the AST. I
|
||||||
// suspect you can only run into them when executing an AST dump from within
|
// suspect you can only run into them when executing an AST dump from within
|
||||||
// the debugger, which is not a use case we worry about for the JSON dumping
|
// the debugger, which is not a use case we worry about for the JSON dumping
|
||||||
|
@ -3077,6 +3077,7 @@ CanThrowResult FunctionProtoType::canThrow() const {
|
|||||||
case EST_DynamicNone:
|
case EST_DynamicNone:
|
||||||
case EST_BasicNoexcept:
|
case EST_BasicNoexcept:
|
||||||
case EST_NoexceptTrue:
|
case EST_NoexceptTrue:
|
||||||
|
case EST_NoThrow:
|
||||||
return CT_Cannot;
|
return CT_Cannot;
|
||||||
|
|
||||||
case EST_None:
|
case EST_None:
|
||||||
|
@ -6853,7 +6853,8 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
|
|||||||
handleNoCfCheckAttr(S, D, AL);
|
handleNoCfCheckAttr(S, D, AL);
|
||||||
break;
|
break;
|
||||||
case ParsedAttr::AT_NoThrow:
|
case ParsedAttr::AT_NoThrow:
|
||||||
handleSimpleAttribute<NoThrowAttr>(S, D, AL);
|
if (!AL.isUsedAsTypeAttr())
|
||||||
|
handleSimpleAttribute<NoThrowAttr>(S, D, AL);
|
||||||
break;
|
break;
|
||||||
case ParsedAttr::AT_CUDAShared:
|
case ParsedAttr::AT_CUDAShared:
|
||||||
handleSharedAttr(S, D, AL);
|
handleSharedAttr(S, D, AL);
|
||||||
|
@ -192,6 +192,7 @@ Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc,
|
|||||||
// If this function has a basic noexcept, it doesn't affect the outcome.
|
// If this function has a basic noexcept, it doesn't affect the outcome.
|
||||||
case EST_BasicNoexcept:
|
case EST_BasicNoexcept:
|
||||||
case EST_NoexceptTrue:
|
case EST_NoexceptTrue:
|
||||||
|
case EST_NoThrow:
|
||||||
return;
|
return;
|
||||||
// If we're still at noexcept(true) and there's a throw() callee,
|
// If we're still at noexcept(true) and there's a throw() callee,
|
||||||
// change to that specification.
|
// change to that specification.
|
||||||
@ -15457,6 +15458,7 @@ bool Sema::checkThisInStaticMemberFunctionExceptionSpec(CXXMethodDecl *Method) {
|
|||||||
case EST_Uninstantiated:
|
case EST_Uninstantiated:
|
||||||
case EST_Unevaluated:
|
case EST_Unevaluated:
|
||||||
case EST_BasicNoexcept:
|
case EST_BasicNoexcept:
|
||||||
|
case EST_NoThrow:
|
||||||
case EST_DynamicNone:
|
case EST_DynamicNone:
|
||||||
case EST_MSAny:
|
case EST_MSAny:
|
||||||
case EST_None:
|
case EST_None:
|
||||||
|
@ -6045,6 +6045,8 @@ mergeExceptionSpecs(Sema &S, FunctionProtoType::ExceptionSpecInfo ESI1,
|
|||||||
if (EST2 == EST_NoexceptFalse) return ESI2;
|
if (EST2 == EST_NoexceptFalse) return ESI2;
|
||||||
|
|
||||||
// If either of them is non-throwing, the result is the other.
|
// If either of them is non-throwing, the result is the other.
|
||||||
|
if (EST1 == EST_NoThrow) return ESI2;
|
||||||
|
if (EST2 == EST_NoThrow) return ESI1;
|
||||||
if (EST1 == EST_DynamicNone) return ESI2;
|
if (EST1 == EST_DynamicNone) return ESI2;
|
||||||
if (EST2 == EST_DynamicNone) return ESI1;
|
if (EST2 == EST_DynamicNone) return ESI1;
|
||||||
if (EST1 == EST_BasicNoexcept) return ESI2;
|
if (EST1 == EST_BasicNoexcept) return ESI2;
|
||||||
@ -6073,6 +6075,7 @@ mergeExceptionSpecs(Sema &S, FunctionProtoType::ExceptionSpecInfo ESI1,
|
|||||||
case EST_DependentNoexcept:
|
case EST_DependentNoexcept:
|
||||||
case EST_NoexceptFalse:
|
case EST_NoexceptFalse:
|
||||||
case EST_NoexceptTrue:
|
case EST_NoexceptTrue:
|
||||||
|
case EST_NoThrow:
|
||||||
llvm_unreachable("handled above");
|
llvm_unreachable("handled above");
|
||||||
|
|
||||||
case EST_Dynamic: {
|
case EST_Dynamic: {
|
||||||
|
@ -130,6 +130,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr,
|
|||||||
case ParsedAttr::AT_Regparm: \
|
case ParsedAttr::AT_Regparm: \
|
||||||
case ParsedAttr::AT_AnyX86NoCallerSavedRegisters: \
|
case ParsedAttr::AT_AnyX86NoCallerSavedRegisters: \
|
||||||
case ParsedAttr::AT_AnyX86NoCfCheck: \
|
case ParsedAttr::AT_AnyX86NoCfCheck: \
|
||||||
|
case ParsedAttr::AT_NoThrow: \
|
||||||
CALLING_CONV_ATTRS_CASELIST
|
CALLING_CONV_ATTRS_CASELIST
|
||||||
|
|
||||||
// Microsoft-specific type qualifiers.
|
// Microsoft-specific type qualifiers.
|
||||||
@ -4516,7 +4517,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
|
|||||||
// If the function declarator has a prototype (i.e. it is not () and
|
// If the function declarator has a prototype (i.e. it is not () and
|
||||||
// does not have a K&R-style identifier list), then the arguments are part
|
// does not have a K&R-style identifier list), then the arguments are part
|
||||||
// of the type, otherwise the argument list is ().
|
// of the type, otherwise the argument list is ().
|
||||||
const DeclaratorChunk::FunctionTypeInfo &FTI = DeclType.Fun;
|
DeclaratorChunk::FunctionTypeInfo &FTI = DeclType.Fun;
|
||||||
IsQualifiedFunction =
|
IsQualifiedFunction =
|
||||||
FTI.hasMethodTypeQualifiers() || FTI.hasRefQualifier();
|
FTI.hasMethodTypeQualifiers() || FTI.hasRefQualifier();
|
||||||
|
|
||||||
@ -6945,6 +6946,61 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (attr.getKind() == ParsedAttr::AT_NoThrow) {
|
||||||
|
if (S.CheckAttrNoArgs(attr))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Delay if this is not a function type.
|
||||||
|
if (!unwrapped.isFunctionType())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Otherwise we can process right away.
|
||||||
|
auto *Proto = unwrapped.get()->getAs<FunctionProtoType>();
|
||||||
|
|
||||||
|
// In the case where this is a FunctionNoProtoType instead of a
|
||||||
|
// FunctionProtoType, let the existing NoThrowAttr implementation do its
|
||||||
|
// thing.
|
||||||
|
if (!Proto)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
attr.setUsedAsTypeAttr();
|
||||||
|
|
||||||
|
// MSVC ignores nothrow if it is in conflict with an explicit exception
|
||||||
|
// specification.
|
||||||
|
if (Proto->hasExceptionSpec()) {
|
||||||
|
switch (Proto->getExceptionSpecType()) {
|
||||||
|
case EST_None:
|
||||||
|
llvm_unreachable("This doesn't have an exception spec!");
|
||||||
|
LLVM_FALLTHROUGH;
|
||||||
|
case EST_DynamicNone:
|
||||||
|
case EST_BasicNoexcept:
|
||||||
|
case EST_NoexceptTrue:
|
||||||
|
case EST_NoThrow:
|
||||||
|
// Exception spec doesn't conflict with nothrow, so don't warn.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EST_Dynamic:
|
||||||
|
case EST_MSAny:
|
||||||
|
case EST_NoexceptFalse:
|
||||||
|
case EST_DependentNoexcept:
|
||||||
|
case EST_Unevaluated:
|
||||||
|
case EST_Uninstantiated:
|
||||||
|
case EST_Unparsed:
|
||||||
|
S.Diag(attr.getLoc(), diag::warn_nothrow_attribute_ignored);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = unwrapped.wrap(
|
||||||
|
S, S.Context
|
||||||
|
.getFunctionTypeWithExceptionSpec(
|
||||||
|
QualType{Proto, 0},
|
||||||
|
FunctionProtoType::ExceptionSpecInfo{EST_NoThrow})
|
||||||
|
->getAs<FunctionType>());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Delay if the type didn't work out to a function.
|
// Delay if the type didn't work out to a function.
|
||||||
if (!unwrapped.isFunctionType()) return false;
|
if (!unwrapped.isFunctionType()) return false;
|
||||||
|
|
||||||
|
55
clang/test/SemaCXX/nothrow-vs-exception-specs.cpp
Normal file
55
clang/test/SemaCXX/nothrow-vs-exception-specs.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// RUN: %clang_cc1 %s -fcxx-exceptions -fdeclspec -fsyntax-only -Wexceptions -verify -std=c++14
|
||||||
|
// RUN: %clang_cc1 %s -fcxx-exceptions -fdeclspec -fsyntax-only -Wexceptions -verify -std=c++17 -DCPP17
|
||||||
|
|
||||||
|
__attribute__((nothrow)) void f1();
|
||||||
|
static_assert(noexcept(f1()), "");
|
||||||
|
void f1() noexcept;
|
||||||
|
// expected-error@+2 {{exception specification in declaration does not match previous declaration}}
|
||||||
|
// expected-note@-2 {{previous declaration is here}}
|
||||||
|
void f1() noexcept(false);
|
||||||
|
|
||||||
|
__attribute__((nothrow)) void f2();
|
||||||
|
static_assert(noexcept(f2()), "");
|
||||||
|
// expected-error@+2 {{exception specification in declaration does not match previous declaration}}
|
||||||
|
// expected-note@-3 {{previous declaration is here}}
|
||||||
|
void f2() noexcept(false);
|
||||||
|
|
||||||
|
void f3() __attribute__((nothrow));
|
||||||
|
static_assert(noexcept(f3()), "");
|
||||||
|
void f3() noexcept;
|
||||||
|
// expected-error@+2 {{exception specification in declaration does not match previous declaration}}
|
||||||
|
// expected-note@-2 {{previous declaration is here}}
|
||||||
|
void f3() noexcept(false);
|
||||||
|
|
||||||
|
// Still noexcept due to throw()
|
||||||
|
__attribute__((nothrow)) void f4() throw();
|
||||||
|
static_assert(noexcept(f4()), "");
|
||||||
|
|
||||||
|
// Still noexcept due to noexcept
|
||||||
|
__attribute__((nothrow)) void f5() noexcept;
|
||||||
|
static_assert(noexcept(f5()), "");
|
||||||
|
|
||||||
|
// Still noexcept due to noexcept(true)
|
||||||
|
__attribute__((nothrow)) void f6() noexcept(true);
|
||||||
|
static_assert(noexcept(f6()), "");
|
||||||
|
|
||||||
|
#ifndef CPP17
|
||||||
|
// Doesn't override C++ implementation.
|
||||||
|
// expected-warning@+1{{'nothrow' attribute conflicts with exception specification; attribute ignored}}
|
||||||
|
__attribute__((nothrow)) void f7() throw(int);
|
||||||
|
static_assert(!noexcept(f7()), "");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Doesn't override C++ implementation.
|
||||||
|
// expected-warning@+1{{'nothrow' attribute conflicts with exception specification; attribute ignored}}
|
||||||
|
__attribute__((nothrow)) void f8() noexcept(false);
|
||||||
|
static_assert(!noexcept(f8()), "");
|
||||||
|
|
||||||
|
__declspec(nothrow) void foo1() noexcept;
|
||||||
|
__declspec(nothrow) void foo2() noexcept(true);
|
||||||
|
// expected-warning@+1{{'nothrow' attribute conflicts with exception specification; attribute ignored}}
|
||||||
|
__declspec(nothrow) void foo3() noexcept(false);
|
||||||
|
__declspec(nothrow) void foo4() noexcept(noexcept(foo1()));
|
||||||
|
__declspec(nothrow) void foo5() noexcept(noexcept(foo2()));
|
||||||
|
// expected-warning@+1{{'nothrow' attribute conflicts with exception specification; attribute ignored}}
|
||||||
|
__declspec(nothrow) void foo6() noexcept(noexcept(foo3()));
|
@ -742,6 +742,8 @@ getExternalExceptionSpecificationKind(ExceptionSpecificationType EST) {
|
|||||||
return CXCursor_ExceptionSpecificationKind_MSAny;
|
return CXCursor_ExceptionSpecificationKind_MSAny;
|
||||||
case EST_BasicNoexcept:
|
case EST_BasicNoexcept:
|
||||||
return CXCursor_ExceptionSpecificationKind_BasicNoexcept;
|
return CXCursor_ExceptionSpecificationKind_BasicNoexcept;
|
||||||
|
case EST_NoThrow:
|
||||||
|
return CXCursor_ExceptionSpecificationKind_NoThrow;
|
||||||
case EST_NoexceptFalse:
|
case EST_NoexceptFalse:
|
||||||
case EST_NoexceptTrue:
|
case EST_NoexceptTrue:
|
||||||
case EST_DependentNoexcept:
|
case EST_DependentNoexcept:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user