From: Richard Smith Date: Tue, 18 Oct 2016 19:29:18 +0000 (+0000) Subject: When two function types have equivalent (but distinct) noexcept specifications, creat... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a43654c7522c8d9f8ddd71fcd6bf7c50c5a09894;p=clang When two function types have equivalent (but distinct) noexcept specifications, create separate type sugar nodes. This is necessary so that substitution into the exception specification will substitute into the correct expression. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@284519 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 318523eed9..0ac1e174fd 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3150,15 +3150,17 @@ static bool isCanonicalExceptionSpecification( return true; // A dynamic exception specification is canonical if it only contains pack - // expansions (so we can't tell whether it's non-throwing). + // expansions (so we can't tell whether it's non-throwing) and all its + // contained types are canonical. if (ESI.Type == EST_Dynamic) { for (QualType ET : ESI.Exceptions) - if (!ET->getAs()) + if (!ET.isCanonical() || !ET->getAs()) return false; return true; } - // A noexcept(expr) specification is canonical if expr is value-dependent. + // A noexcept(expr) specification is (possibly) canonical if expr is + // value-dependent. if (ESI.Type == EST_ComputedNoexcept) return ESI.NoexceptExpr && ESI.NoexceptExpr->isValueDependent(); @@ -3170,33 +3172,50 @@ ASTContext::getFunctionType(QualType ResultTy, ArrayRef ArgArray, const FunctionProtoType::ExtProtoInfo &EPI) const { size_t NumArgs = ArgArray.size(); - bool NoexceptInType = getLangOpts().CPlusPlus1z; + // Unique functions, to guarantee there is only one function of a particular + // structure. + llvm::FoldingSetNodeID ID; + FunctionProtoType::Profile(ID, ResultTy, ArgArray.begin(), NumArgs, EPI, + *this, true); + + QualType Canonical; + bool Unique = false; + void *InsertPos = nullptr; + if (FunctionProtoType *FPT = + FunctionProtoTypes.FindNodeOrInsertPos(ID, InsertPos)) { + QualType Existing = QualType(FPT, 0); + + // If we find a pre-existing equivalent FunctionProtoType, we can just reuse + // it so long as our exception specification doesn't contain a dependent + // noexcept expression. If it /does/, we're going to need to create a type + // sugar node to hold the concrete expression. + if (EPI.ExceptionSpec.Type != EST_ComputedNoexcept || + EPI.ExceptionSpec.NoexceptExpr == FPT->getNoexceptExpr()) + return Existing; + + // We need a new type sugar node for this one, to hold the new noexcept + // expression. We do no canonicalization here, but that's OK since we don't + // expect to see the same noexcept expression much more than once. + Canonical = getCanonicalType(Existing); + Unique = true; + } + + bool NoexceptInType = getLangOpts().CPlusPlus1z; bool IsCanonicalExceptionSpec = isCanonicalExceptionSpecification(EPI.ExceptionSpec, NoexceptInType); // Determine whether the type being created is already canonical or not. - bool isCanonical = IsCanonicalExceptionSpec && + bool isCanonical = !Unique && IsCanonicalExceptionSpec && isCanonicalResultType(ResultTy) && !EPI.HasTrailingReturn; for (unsigned i = 0; i != NumArgs && isCanonical; ++i) if (!ArgArray[i].isCanonicalAsParam()) isCanonical = false; - // Unique functions, to guarantee there is only one function of a particular - // structure. - llvm::FoldingSetNodeID ID; - FunctionProtoType::Profile(ID, ResultTy, ArgArray.begin(), NumArgs, EPI, - *this, isCanonical); - - void *InsertPos = nullptr; - if (FunctionProtoType *FTP = - FunctionProtoTypes.FindNodeOrInsertPos(ID, InsertPos)) - return QualType(FTP, 0); - - // If this type isn't canonical, get the canonical version of it. - // The exception spec is not part of the canonical type. - QualType Canonical; - if (!isCanonical) { + // If this type isn't canonical, get the canonical version of it if we don't + // already have it. The exception spec is only partially part of the + // canonical type, and only in C++17 onwards. + if (!isCanonical && Canonical.isNull()) { SmallVector CanonicalArgs; CanonicalArgs.reserve(NumArgs); for (unsigned i = 0; i != NumArgs; ++i) @@ -3208,17 +3227,34 @@ ASTContext::getFunctionType(QualType ResultTy, ArrayRef ArgArray, if (IsCanonicalExceptionSpec) { // Exception spec is already OK. } else if (NoexceptInType) { + llvm::SmallVector ExceptionTypeStorage; switch (EPI.ExceptionSpec.Type) { case EST_Unparsed: case EST_Unevaluated: case EST_Uninstantiated: // We don't know yet. It shouldn't matter what we pick here; no-one // should ever look at this. LLVM_FALLTHROUGH; - case EST_None: case EST_MSAny: case EST_Dynamic: - // If we get here for EST_Dynamic, there is at least one - // non-pack-expansion type, so this is not non-throwing. + case EST_None: case EST_MSAny: CanonicalEPI.ExceptionSpec.Type = EST_None; break; + // A dynamic exception specification is almost always "not noexcept", + // with the exception that a pack expansion might expand to no types. + case EST_Dynamic: { + bool AnyPacks = false; + for (QualType ET : EPI.ExceptionSpec.Exceptions) { + if (ET->getAs()) + AnyPacks = true; + ExceptionTypeStorage.push_back(getCanonicalType(ET)); + } + if (!AnyPacks) + CanonicalEPI.ExceptionSpec.Type = EST_None; + else { + CanonicalEPI.ExceptionSpec.Type = EST_Dynamic; + CanonicalEPI.ExceptionSpec.Exceptions = ExceptionTypeStorage; + } + break; + } + case EST_DynamicNone: case EST_BasicNoexcept: CanonicalEPI.ExceptionSpec.Type = EST_BasicNoexcept; break; @@ -3289,7 +3325,8 @@ ASTContext::getFunctionType(QualType ResultTy, ArrayRef ArgArray, FunctionProtoType::ExtProtoInfo newEPI = EPI; new (FTP) FunctionProtoType(ResultTy, ArgArray, Canonical, newEPI); Types.push_back(FTP); - FunctionProtoTypes.InsertNode(FTP, InsertPos); + if (!Unique) + FunctionProtoTypes.InsertNode(FTP, InsertPos); return QualType(FTP, 0); } diff --git a/test/SemaCXX/cxx0x-defaulted-functions.cpp b/test/SemaCXX/cxx0x-defaulted-functions.cpp index c9c9fcef56..8225b01fd1 100644 --- a/test/SemaCXX/cxx0x-defaulted-functions.cpp +++ b/test/SemaCXX/cxx0x-defaulted-functions.cpp @@ -87,17 +87,14 @@ namespace DefaultedFnExceptionSpec { template struct Error { - // FIXME: Type canonicalization causes all the errors to point at the first - // declaration which has the type 'void () noexcept (T::error)'. We should - // get one error for 'Error::Error()' and one for 'Error::~Error()'. - void f() noexcept(T::error); // expected-error 2{{has no members}} + void f() noexcept(T::error); - Error() noexcept(T::error); + Error() noexcept(T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}} Error(const Error&) noexcept(T::error); Error(Error&&) noexcept(T::error); Error &operator=(const Error&) noexcept(T::error); Error &operator=(Error&&) noexcept(T::error); - ~Error() noexcept(T::error); + ~Error() noexcept(T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}} }; struct DelayImplicit { diff --git a/test/SemaCXX/cxx1z-noexcept-function-type.cpp b/test/SemaCXX/cxx1z-noexcept-function-type.cpp index e2a947eac0..711e0bca8b 100644 --- a/test/SemaCXX/cxx1z-noexcept-function-type.cpp +++ b/test/SemaCXX/cxx1z-noexcept-function-type.cpp @@ -11,3 +11,22 @@ template void redecl2() noexcept(B); // expected-error {{conflic // FIXME: It's not clear whether this is supposed to be valid. template void redecl3() throw(A); template void redecl3() throw(B); + +namespace DependentDefaultCtorExceptionSpec { + template struct T { static const bool value = true; }; + + template struct map { + typedef A a; + map() noexcept(T::value) {} + }; + + template struct multimap { + typedef B b; + multimap() noexcept(T::value) {} + }; + + // Don't crash here. + struct A { multimap Map; } a; + + static_assert(noexcept(A())); +}