[clang] 0e3a487 - PR12350: Handle remaining cases permitted by CWG DR 244.
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 6 16:07:00 PST 2020
On Fri, 6 Nov 2020 at 13:30, Alex L <arphaman at gmail.com> wrote:
> Hi Rchard,
>
> Some of our code started triggering the warning you added in this code,
> but I'm not sure if the warning is actually valid or not in this case:
>
> https://godbolt.org/z/9fxs3K
>
> namespace geo {
> template<typename T>
> class optional {
> optional() { }
> ~optional();
> };
> }
> template<typename T>
> geo::optional<T>::~optional() // Triggers ISO C++ requires the name after
> '::~' to be found in the same scope as the name before '::~'
> {
> }
>
> Could you confirm whether or not this warning is valid for our code?
>
Technically, yes, the warning is correct; the C++ rules require that to be
written as
geo::optional<T>::~optional<T>()
... but of course no-one does that, and it doesn't seem to be an intended
restriction. I've been working with the C++ committee to get the language
rule fixed, and the fix will be in P1787R6, which is due to be voted into
C++23 (with at least this part as a DR against prior standards) next week.
It's probably time to implement the new rule :)
In the meantime, using -Wno-dtor-name would be reasonable.
> Thanks,
> Alex
>
>
> On Thu, 13 Feb 2020 at 00:52, Richard Smith via cfe-commits <
> cfe-commits at lists.llvm.org> wrote:
>
>> Thanks, yes, fixed in llvmorg-11-init-3043-gc1394afb8df.
>>
>> On Thu, 13 Feb 2020 at 02:58, Kostya Serebryany via cfe-commits <
>> cfe-commits at lists.llvm.org> wrote:
>>
>>> Could this have caused a new ubsan failure?
>>>
>>> clang/lib/AST/NestedNameSpecifier.cpp:485:23: runtime error: null pointer passed as argument 2, which is declared to never be null
>>>
>>>
>>> http://lab.llvm.org:8011/builders/sanitizer-x86_64-linux-fast/builds/38698/steps/check-clang%20ubsan/logs/stdio
>>>
>>> On Fri, Feb 7, 2020 at 6:41 PM Richard Smith via cfe-commits <
>>> cfe-commits at lists.llvm.org> wrote:
>>>
>>>>
>>>> Author: Richard Smith
>>>> Date: 2020-02-07T18:40:41-08:00
>>>> New Revision: 0e3a48778408b505946e465abf5c77a2ddd4918c
>>>>
>>>> URL:
>>>> https://github.com/llvm/llvm-project/commit/0e3a48778408b505946e465abf5c77a2ddd4918c
>>>> DIFF:
>>>> https://github.com/llvm/llvm-project/commit/0e3a48778408b505946e465abf5c77a2ddd4918c.diff
>>>>
>>>> LOG: PR12350: Handle remaining cases permitted by CWG DR 244.
>>>>
>>>> Also add extension warnings for the cases that are disallowed by the
>>>> current rules for destructor name lookup, refactor and simplify the
>>>> lookup code, and improve the diagnostic quality when lookup fails.
>>>>
>>>> The special case we previously supported for converting
>>>> p->N::S<int>::~S() from naming a class template into naming a
>>>> specialization thereof is subsumed by a more general rule here (which is
>>>> also consistent with Clang's historical behavior and that of other
>>>> compilers): if we can't find a suitable S in N, also look in N::S<int>.
>>>>
>>>> The extension warnings are off by default, except for a warning when
>>>> lookup for p->N::S::~T() looks for T in scope instead of in N (or N::S).
>>>> That seems sufficiently heinous to warn on by default, especially since
>>>> we can't support it for a dependent nested-name-specifier.
>>>>
>>>> Added:
>>>>
>>>>
>>>> Modified:
>>>> clang/include/clang/Basic/DiagnosticGroups.td
>>>> clang/include/clang/Basic/DiagnosticSemaKinds.td
>>>> clang/lib/AST/NestedNameSpecifier.cpp
>>>> clang/lib/Sema/DeclSpec.cpp
>>>> clang/lib/Sema/SemaExprCXX.cpp
>>>> clang/test/CXX/class/class.mem/p13.cpp
>>>> clang/test/CXX/drs/dr2xx.cpp
>>>> clang/test/CXX/drs/dr3xx.cpp
>>>> clang/test/FixIt/fixit.cpp
>>>> clang/test/Parser/cxx-decl.cpp
>>>> clang/test/SemaCXX/constructor.cpp
>>>> clang/test/SemaCXX/destructor.cpp
>>>> clang/test/SemaCXX/pseudo-destructors.cpp
>>>>
>>>> Removed:
>>>>
>>>>
>>>>
>>>>
>>>> ################################################################################
>>>> diff --git a/clang/include/clang/Basic/DiagnosticGroups.td
>>>> b/clang/include/clang/Basic/DiagnosticGroups.td
>>>> index a2bc29986a07..8c54723cdbab 100644
>>>> --- a/clang/include/clang/Basic/DiagnosticGroups.td
>>>> +++ b/clang/include/clang/Basic/DiagnosticGroups.td
>>>> @@ -192,6 +192,7 @@ def CXX2aDesignator : DiagGroup<"c++2a-designator">;
>>>> // designators (including the warning controlled by
>>>> -Wc++2a-designator).
>>>> def C99Designator : DiagGroup<"c99-designator", [CXX2aDesignator]>;
>>>> def GNUDesignator : DiagGroup<"gnu-designator">;
>>>> +def DtorName : DiagGroup<"dtor-name">;
>>>>
>>>> def DynamicExceptionSpec
>>>> : DiagGroup<"dynamic-exception-spec",
>>>> [DeprecatedDynamicExceptionSpec]>;
>>>>
>>>> diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td
>>>> b/clang/include/clang/Basic/DiagnosticSemaKinds.td
>>>> index 9de60d3a8d27..82861f0d5d72 100644
>>>> --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
>>>> +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
>>>> @@ -1911,17 +1911,33 @@ def err_destructor_with_params :
>>>> Error<"destructor cannot have any parameters">;
>>>> def err_destructor_variadic : Error<"destructor cannot be variadic">;
>>>> def err_destructor_typedef_name : Error<
>>>> "destructor cannot be declared using a %select{typedef|type alias}1
>>>> %0 of the class name">;
>>>> +def err_undeclared_destructor_name : Error<
>>>> + "undeclared identifier %0 in destructor name">;
>>>> def err_destructor_name : Error<
>>>> "expected the class name after '~' to name the enclosing class">;
>>>> -def err_destructor_class_name : Error<
>>>> - "expected the class name after '~' to name a destructor">;
>>>> -def err_ident_in_dtor_not_a_type : Error<
>>>> +def err_destructor_name_nontype : Error<
>>>> + "identifier %0 after '~' in destructor name does not name a type">;
>>>> +def err_destructor_expr_mismatch : Error<
>>>> + "identifier %0 in object destruction expression does not name the
>>>> type "
>>>> + "%1 of the object being destroyed">;
>>>> +def err_destructor_expr_nontype : Error<
>>>> "identifier %0 in object destruction expression does not name a
>>>> type">;
>>>> def err_destructor_expr_type_mismatch : Error<
>>>> "destructor type %0 in object destruction expression does not match
>>>> the "
>>>> "type %1 of the object being destroyed">;
>>>> def note_destructor_type_here : Note<
>>>> - "type %0 is declared here">;
>>>> + "type %0 found by destructor name lookup">;
>>>> +def note_destructor_nontype_here : Note<
>>>> + "non-type declaration found by destructor name lookup">;
>>>> +def ext_dtor_named_in_wrong_scope : Extension<
>>>> + "ISO C++ requires the name after '::~' to be found in the same scope
>>>> as "
>>>> + "the name before '::~'">, InGroup<DtorName>;
>>>> +def ext_dtor_name_missing_template_arguments : Extension<
>>>> + "ISO C++ requires template argument list in destructor name">,
>>>> + InGroup<DtorName>;
>>>> +def ext_qualified_dtor_named_in_lexical_scope : ExtWarn<
>>>> + "qualified destructor name only found in lexical scope; omit the
>>>> qualifier "
>>>> + "to find this type name by unqualified lookup">, InGroup<DtorName>;
>>>>
>>>> def err_destroy_attr_on_non_static_var : Error<
>>>> "%select{no_destroy|always_destroy}0 attribute can only be applied
>>>> to a"
>>>>
>>>> diff --git a/clang/lib/AST/NestedNameSpecifier.cpp
>>>> b/clang/lib/AST/NestedNameSpecifier.cpp
>>>> index 137953fa8203..81130512bfe1 100644
>>>> --- a/clang/lib/AST/NestedNameSpecifier.cpp
>>>> +++ b/clang/lib/AST/NestedNameSpecifier.cpp
>>>> @@ -482,10 +482,9 @@ static void Append(char *Start, char *End, char
>>>> *&Buffer, unsigned &BufferSize,
>>>> (unsigned)(BufferCapacity ? BufferCapacity * 2 : sizeof(void
>>>> *) * 2),
>>>> (unsigned)(BufferSize + (End - Start)));
>>>> char *NewBuffer = static_cast<char
>>>> *>(llvm::safe_malloc(NewCapacity));
>>>> - if (BufferCapacity) {
>>>> - memcpy(NewBuffer, Buffer, BufferSize);
>>>> + memcpy(NewBuffer, Buffer, BufferSize);
>>>> + if (BufferCapacity)
>>>> free(Buffer);
>>>> - }
>>>> Buffer = NewBuffer;
>>>> BufferCapacity = NewCapacity;
>>>> }
>>>>
>>>> diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
>>>> index 94d87974624e..eca97734bc9d 100644
>>>> --- a/clang/lib/Sema/DeclSpec.cpp
>>>> +++ b/clang/lib/Sema/DeclSpec.cpp
>>>> @@ -130,6 +130,8 @@ void CXXScopeSpec::Adopt(NestedNameSpecifierLoc
>>>> Other) {
>>>>
>>>> Range = Other.getSourceRange();
>>>> Builder.Adopt(Other);
>>>> + assert(Range == Builder.getSourceRange() &&
>>>> + "NestedNameSpecifierLoc range computation incorrect");
>>>> }
>>>>
>>>> SourceLocation CXXScopeSpec::getLastQualifierNameLoc() const {
>>>>
>>>> diff --git a/clang/lib/Sema/SemaExprCXX.cpp
>>>> b/clang/lib/Sema/SemaExprCXX.cpp
>>>> index e53281d11755..a39b0b1f7766 100644
>>>> --- a/clang/lib/Sema/SemaExprCXX.cpp
>>>> +++ b/clang/lib/Sema/SemaExprCXX.cpp
>>>> @@ -156,103 +156,48 @@ ParsedType
>>>> Sema::getDestructorName(SourceLocation TildeLoc,
>>>> // }
>>>> //
>>>> // See also PR6358 and PR6359.
>>>> - // For this reason, we're currently only doing the C++03 version of
>>>> this
>>>> - // code; the C++0x version has to wait until we get a proper spec.
>>>> - QualType SearchType;
>>>> - DeclContext *LookupCtx = nullptr;
>>>> - bool isDependent = false;
>>>> - bool LookInScope = false;
>>>> + //
>>>> + // For now, we accept all the cases in which the name given could
>>>> plausibly
>>>> + // be interpreted as a correct destructor name, issuing
>>>> off-by-default
>>>> + // extension diagnostics on the cases that don't strictly conform to
>>>> the
>>>> + // C++20 rules. This basically means we always consider looking in
>>>> the
>>>> + // nested-name-specifier prefix, the complete nested-name-specifier,
>>>> and
>>>> + // the scope, and accept if we find the expected type in any of the
>>>> three
>>>> + // places.
>>>>
>>>> if (SS.isInvalid())
>>>> return nullptr;
>>>>
>>>> + // Whether we've failed with a diagnostic already.
>>>> + bool Failed = false;
>>>> +
>>>> + llvm::SmallVector<NamedDecl*, 8> FoundDecls;
>>>> + llvm::SmallSet<CanonicalDeclPtr<Decl>, 8> FoundDeclSet;
>>>> +
>>>> // If we have an object type, it's because we are in a
>>>> // pseudo-destructor-expression or a member access expression, and
>>>> // we know what type we're looking for.
>>>> - if (ObjectTypePtr)
>>>> - SearchType = GetTypeFromParser(ObjectTypePtr);
>>>> -
>>>> - if (SS.isSet()) {
>>>> - NestedNameSpecifier *NNS = SS.getScopeRep();
>>>> -
>>>> - bool AlreadySearched = false;
>>>> - bool LookAtPrefix = true;
>>>> - // C++11 [basic.lookup.qual]p6:
>>>> - // If a pseudo-destructor-name (5.2.4) contains a
>>>> nested-name-specifier,
>>>> - // the type-names are looked up as types in the scope designated
>>>> by the
>>>> - // nested-name-specifier. Similarly, in a qualified-id of the
>>>> form:
>>>> - //
>>>> - // nested-name-specifier[opt] class-name :: ~ class-name
>>>> - //
>>>> - // the second class-name is looked up in the same scope as the
>>>> first.
>>>> - //
>>>> - // Here, we determine whether the code below is permitted to look
>>>> at the
>>>> - // prefix of the nested-name-specifier.
>>>> - DeclContext *DC = computeDeclContext(SS, EnteringContext);
>>>> - if (DC && DC->isFileContext()) {
>>>> - AlreadySearched = true;
>>>> - LookupCtx = DC;
>>>> - isDependent = false;
>>>> - } else if (DC && isa<CXXRecordDecl>(DC)) {
>>>> - LookAtPrefix = false;
>>>> - LookInScope = true;
>>>> - }
>>>> -
>>>> - // The second case from the C++03 rules quoted further above.
>>>> - NestedNameSpecifier *Prefix = nullptr;
>>>> - if (AlreadySearched) {
>>>> - // Nothing left to do.
>>>> - } else if (LookAtPrefix && (Prefix = NNS->getPrefix())) {
>>>> - CXXScopeSpec PrefixSS;
>>>> - PrefixSS.Adopt(NestedNameSpecifierLoc(Prefix,
>>>> SS.location_data()));
>>>> - LookupCtx = computeDeclContext(PrefixSS, EnteringContext);
>>>> - isDependent = isDependentScopeSpecifier(PrefixSS);
>>>> - } else if (ObjectTypePtr) {
>>>> - LookupCtx = computeDeclContext(SearchType);
>>>> - isDependent = SearchType->isDependentType();
>>>> - } else {
>>>> - LookupCtx = computeDeclContext(SS, EnteringContext);
>>>> - isDependent = LookupCtx && LookupCtx->isDependentContext();
>>>> - }
>>>> - } else if (ObjectTypePtr) {
>>>> - // C++ [basic.lookup.classref]p3:
>>>> - // If the unqualified-id is ~type-name, the type-name is looked
>>>> up
>>>> - // in the context of the entire postfix-expression. If the type T
>>>> - // of the object expression is of a class type C, the type-name
>>>> is
>>>> - // also looked up in the scope of class C. At least one of the
>>>> - // lookups shall find a name that refers to (possibly
>>>> - // cv-qualified) T.
>>>> - LookupCtx = computeDeclContext(SearchType);
>>>> - isDependent = SearchType->isDependentType();
>>>> - assert((isDependent || !SearchType->isIncompleteType()) &&
>>>> - "Caller should have completed object type");
>>>> -
>>>> - LookInScope = true;
>>>> - } else {
>>>> - // Perform lookup into the current scope (only).
>>>> - LookInScope = true;
>>>> - }
>>>> -
>>>> - TypeDecl *NonMatchingTypeDecl = nullptr;
>>>> - LookupResult Found(*this, &II, NameLoc, LookupOrdinaryName);
>>>> - for (unsigned Step = 0; Step != 2; ++Step) {
>>>> - // Look for the name first in the computed lookup context (if we
>>>> - // have one) and, if that fails to find a match, in the scope (if
>>>> - // we're allowed to look there).
>>>> - Found.clear();
>>>> - if (Step == 0 && LookupCtx) {
>>>> - if (RequireCompleteDeclContext(SS, LookupCtx))
>>>> - return nullptr;
>>>> - LookupQualifiedName(Found, LookupCtx);
>>>> - } else if (Step == 1 && LookInScope && S) {
>>>> - LookupName(Found, S);
>>>> - } else {
>>>> - continue;
>>>> - }
>>>> + QualType SearchType =
>>>> + ObjectTypePtr ? GetTypeFromParser(ObjectTypePtr) : QualType();
>>>>
>>>> + auto CheckLookupResult = [&](LookupResult &Found) -> ParsedType {
>>>> // FIXME: Should we be suppressing ambiguities here?
>>>> - if (Found.isAmbiguous())
>>>> + if (Found.isAmbiguous()) {
>>>> + Failed = true;
>>>> return nullptr;
>>>> + }
>>>> +
>>>> + for (NamedDecl *D : Found) {
>>>> + // Don't list a class twice in the lookup failure diagnostic if
>>>> it's
>>>> + // found by both its injected-class-name and by the name in the
>>>> enclosing
>>>> + // scope.
>>>> + if (auto *RD = dyn_cast<CXXRecordDecl>(D))
>>>> + if (RD->isInjectedClassName())
>>>> + D = cast<NamedDecl>(RD->getParent());
>>>> +
>>>> + if (FoundDeclSet.insert(D).second)
>>>> + FoundDecls.push_back(D);
>>>> + }
>>>>
>>>> if (TypeDecl *Type = Found.getAsSingle<TypeDecl>()) {
>>>> QualType T = Context.getTypeDeclType(Type);
>>>> @@ -261,91 +206,121 @@ ParsedType
>>>> Sema::getDestructorName(SourceLocation TildeLoc,
>>>> if (SearchType.isNull() || SearchType->isDependentType() ||
>>>> Context.hasSameUnqualifiedType(T, SearchType)) {
>>>> // We found our type!
>>>> -
>>>> return CreateParsedType(T,
>>>> Context.getTrivialTypeSourceInfo(T,
>>>> NameLoc));
>>>> }
>>>> + }
>>>>
>>>> - if (!SearchType.isNull())
>>>> - NonMatchingTypeDecl = Type;
>>>> - }
>>>> -
>>>> - // If the name that we found is a class template name, and it is
>>>> - // the same name as the template name in the last part of the
>>>> - // nested-name-specifier (if present) or the object type, then
>>>> - // this is the destructor for that class.
>>>> - // FIXME: This is a workaround until we get real drafting for core
>>>> - // issue 399, for which there isn't even an obvious direction.
>>>> - if (ClassTemplateDecl *Template =
>>>> Found.getAsSingle<ClassTemplateDecl>()) {
>>>> - QualType MemberOfType;
>>>> - if (SS.isSet()) {
>>>> - if (DeclContext *Ctx = computeDeclContext(SS,
>>>> EnteringContext)) {
>>>> - // Figure out the type of the context, if it has one.
>>>> - if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Ctx))
>>>> - MemberOfType = Context.getTypeDeclType(Record);
>>>> - }
>>>> - }
>>>> - if (MemberOfType.isNull())
>>>> - MemberOfType = SearchType;
>>>> + return nullptr;
>>>> + };
>>>>
>>>> - if (MemberOfType.isNull())
>>>> - continue;
>>>> + bool IsDependent = false;
>>>>
>>>> - // We're referring into a class template specialization. If the
>>>> - // class template we found is the same as the template being
>>>> - // specialized, we found what we are looking for.
>>>> - if (const RecordType *Record =
>>>> MemberOfType->getAs<RecordType>()) {
>>>> - if (ClassTemplateSpecializationDecl *Spec
>>>> - =
>>>> dyn_cast<ClassTemplateSpecializationDecl>(Record->getDecl())) {
>>>> - if (Spec->getSpecializedTemplate()->getCanonicalDecl() ==
>>>> - Template->getCanonicalDecl())
>>>> - return CreateParsedType(
>>>> - MemberOfType,
>>>> - Context.getTrivialTypeSourceInfo(MemberOfType,
>>>> NameLoc));
>>>> - }
>>>> + auto LookupInObjectType = [&]() -> ParsedType {
>>>> + if (Failed || SearchType.isNull())
>>>> + return nullptr;
>>>>
>>>> - continue;
>>>> - }
>>>> + IsDependent |= SearchType->isDependentType();
>>>>
>>>> - // We're referring to an unresolved class template
>>>> - // specialization. Determine whether we class template we found
>>>> - // is the same as the template being specialized or, if we don't
>>>> - // know which template is being specialized, that it at least
>>>> - // has the same name.
>>>> - if (const TemplateSpecializationType *SpecType
>>>> - = MemberOfType->getAs<TemplateSpecializationType>()) {
>>>> - TemplateName SpecName = SpecType->getTemplateName();
>>>> -
>>>> - // The class template we found is the same template being
>>>> - // specialized.
>>>> - if (TemplateDecl *SpecTemplate = SpecName.getAsTemplateDecl())
>>>> {
>>>> - if (SpecTemplate->getCanonicalDecl() ==
>>>> Template->getCanonicalDecl())
>>>> - return CreateParsedType(
>>>> - MemberOfType,
>>>> - Context.getTrivialTypeSourceInfo(MemberOfType,
>>>> NameLoc));
>>>> + LookupResult Found(*this, &II, NameLoc, LookupOrdinaryName);
>>>> + DeclContext *LookupCtx = computeDeclContext(SearchType);
>>>> + if (!LookupCtx)
>>>> + return nullptr;
>>>> + LookupQualifiedName(Found, LookupCtx);
>>>> + return CheckLookupResult(Found);
>>>> + };
>>>>
>>>> - continue;
>>>> - }
>>>> + auto LookupInNestedNameSpec = [&](CXXScopeSpec &LookupSS) ->
>>>> ParsedType {
>>>> + if (Failed)
>>>> + return nullptr;
>>>>
>>>> - // The class template we found has the same name as the
>>>> - // (dependent) template name being specialized.
>>>> - if (DependentTemplateName *DepTemplate
>>>> - =
>>>> SpecName.getAsDependentTemplateName()) {
>>>> - if (DepTemplate->isIdentifier() &&
>>>> - DepTemplate->getIdentifier() ==
>>>> Template->getIdentifier())
>>>> - return CreateParsedType(
>>>> - MemberOfType,
>>>> - Context.getTrivialTypeSourceInfo(MemberOfType,
>>>> NameLoc));
>>>> + IsDependent |= isDependentScopeSpecifier(LookupSS);
>>>> + DeclContext *LookupCtx = computeDeclContext(LookupSS,
>>>> EnteringContext);
>>>> + if (!LookupCtx)
>>>> + return nullptr;
>>>>
>>>> - continue;
>>>> - }
>>>> - }
>>>> + LookupResult Found(*this, &II, NameLoc, LookupOrdinaryName);
>>>> + if (RequireCompleteDeclContext(LookupSS, LookupCtx)) {
>>>> + Failed = true;
>>>> + return nullptr;
>>>> }
>>>> + LookupQualifiedName(Found, LookupCtx);
>>>> + return CheckLookupResult(Found);
>>>> + };
>>>> +
>>>> + auto LookupInScope = [&]() -> ParsedType {
>>>> + if (Failed || !S)
>>>> + return nullptr;
>>>> +
>>>> + LookupResult Found(*this, &II, NameLoc, LookupOrdinaryName);
>>>> + LookupName(Found, S);
>>>> + return CheckLookupResult(Found);
>>>> + };
>>>> +
>>>> + // C++2a [basic.lookup.qual]p6:
>>>> + // In a qualified-id of the form
>>>> + //
>>>> + // nested-name-specifier[opt] type-name :: ~ type-name
>>>> + //
>>>> + // the second type-name is looked up in the same scope as the
>>>> first.
>>>> + //
>>>> + // We interpret this as meaning that if you do a dual-scope lookup
>>>> for the
>>>> + // first name, you also do a dual-scope lookup for the second name,
>>>> per
>>>> + // C++ [basic.lookup.classref]p4:
>>>> + //
>>>> + // If the id-expression in a class member access is a qualified-id
>>>> of the
>>>> + // form
>>>> + //
>>>> + // class-name-or-namespace-name :: ...
>>>> + //
>>>> + // the class-name-or-namespace-name following the . or -> is first
>>>> looked
>>>> + // up in the class of the object expression and the name, if
>>>> found, is used.
>>>> + // Otherwise, it is looked up in the context of the entire
>>>> + // postfix-expression.
>>>> + //
>>>> + // This looks in the same scopes as for an unqualified destructor
>>>> name:
>>>> + //
>>>> + // C++ [basic.lookup.classref]p3:
>>>> + // If the unqualified-id is ~ type-name, the type-name is looked up
>>>> + // in the context of the entire postfix-expression. If the type T
>>>> + // of the object expression is of a class type C, the type-name is
>>>> + // also looked up in the scope of class C. At least one of the
>>>> + // lookups shall find a name that refers to cv T.
>>>> + //
>>>> + // FIXME: The intent is unclear here. Should type-name::~type-name
>>>> look in
>>>> + // the scope anyway if it finds a non-matching name declared in the
>>>> class?
>>>> + // If both lookups succeed and find a dependent result, which result
>>>> should
>>>> + // we retain? (Same question for p->~type-name().)
>>>> +
>>>> + if (NestedNameSpecifier *Prefix =
>>>> + SS.isSet() ? SS.getScopeRep()->getPrefix() : nullptr) {
>>>> + // This is
>>>> + //
>>>> + // nested-name-specifier type-name :: ~ type-name
>>>> + //
>>>> + // Look for the second type-name in the nested-name-specifier.
>>>> + CXXScopeSpec PrefixSS;
>>>> + PrefixSS.Adopt(NestedNameSpecifierLoc(Prefix, SS.location_data()));
>>>> + if (ParsedType T = LookupInNestedNameSpec(PrefixSS))
>>>> + return T;
>>>> + } else {
>>>> + // This is one of
>>>> + //
>>>> + // type-name :: ~ type-name
>>>> + // ~ type-name
>>>> + //
>>>> + // Look in the scope and (if any) the object type.
>>>> + if (ParsedType T = LookupInScope())
>>>> + return T;
>>>> + if (ParsedType T = LookupInObjectType())
>>>> + return T;
>>>> }
>>>>
>>>> - if (isDependent) {
>>>> - // We didn't find our type, but that's okay: it's dependent
>>>> - // anyway.
>>>> + if (Failed)
>>>> + return nullptr;
>>>> +
>>>> + if (IsDependent) {
>>>> + // We didn't find our type, but that's OK: it's dependent anyway.
>>>>
>>>> // FIXME: What if we have no nested-name-specifier?
>>>> QualType T = CheckTypenameType(ETK_None, SourceLocation(),
>>>> @@ -354,26 +329,98 @@ ParsedType Sema::getDestructorName(SourceLocation
>>>> TildeLoc,
>>>> return ParsedType::make(T);
>>>> }
>>>>
>>>> - if (NonMatchingTypeDecl) {
>>>> - QualType T = Context.getTypeDeclType(NonMatchingTypeDecl);
>>>> - Diag(NameLoc, diag::err_destructor_expr_type_mismatch)
>>>> - << T << SearchType;
>>>> - Diag(NonMatchingTypeDecl->getLocation(),
>>>> diag::note_destructor_type_here)
>>>> - << T;
>>>> - } else if (ObjectTypePtr)
>>>> - Diag(NameLoc, diag::err_ident_in_dtor_not_a_type)
>>>> - << &II;
>>>> - else {
>>>> - SemaDiagnosticBuilder DtorDiag = Diag(NameLoc,
>>>> -
>>>> diag::err_destructor_class_name);
>>>> - if (S) {
>>>> - const DeclContext *Ctx = S->getEntity();
>>>> - if (const CXXRecordDecl *Class =
>>>> dyn_cast_or_null<CXXRecordDecl>(Ctx))
>>>> - DtorDiag << FixItHint::CreateReplacement(SourceRange(NameLoc),
>>>> -
>>>> Class->getNameAsString());
>>>> + // The remaining cases are all non-standard extensions imitating the
>>>> behavior
>>>> + // of various other compilers.
>>>> + unsigned NumNonExtensionDecls = FoundDecls.size();
>>>> +
>>>> + if (SS.isSet()) {
>>>> + // For compatibility with older broken C++ rules and existing code,
>>>> + //
>>>> + // nested-name-specifier :: ~ type-name
>>>> + //
>>>> + // also looks for type-name within the nested-name-specifier.
>>>> + if (ParsedType T = LookupInNestedNameSpec(SS)) {
>>>> + Diag(SS.getEndLoc(), diag::ext_dtor_named_in_wrong_scope)
>>>> + << SS.getRange()
>>>> + << FixItHint::CreateInsertion(SS.getEndLoc(),
>>>> + ("::" + II.getName()).str());
>>>> + return T;
>>>> + }
>>>> +
>>>> + // For compatibility with other compilers and older versions of
>>>> Clang,
>>>> + //
>>>> + // nested-name-specifier type-name :: ~ type-name
>>>> + //
>>>> + // also looks for type-name in the scope. Unfortunately, we can't
>>>> + // reasonably apply this fallback for dependent
>>>> nested-name-specifiers.
>>>> + if (SS.getScopeRep()->getPrefix()) {
>>>> + if (ParsedType T = LookupInScope()) {
>>>> + Diag(SS.getEndLoc(),
>>>> diag::ext_qualified_dtor_named_in_lexical_scope)
>>>> + << FixItHint::CreateRemoval(SS.getRange());
>>>> + Diag(FoundDecls.back()->getLocation(),
>>>> diag::note_destructor_type_here)
>>>> + << GetTypeFromParser(T);
>>>> + return T;
>>>> + }
>>>> }
>>>> }
>>>>
>>>> + // We didn't find anything matching; tell the user what we did find
>>>> (if
>>>> + // anything).
>>>> +
>>>> + // Don't tell the user about declarations we shouldn't have found.
>>>> + FoundDecls.resize(NumNonExtensionDecls);
>>>> +
>>>> + // List types before non-types.
>>>> + std::stable_sort(FoundDecls.begin(), FoundDecls.end(),
>>>> + [](NamedDecl *A, NamedDecl *B) {
>>>> + return isa<TypeDecl>(A->getUnderlyingDecl()) >
>>>> + isa<TypeDecl>(B->getUnderlyingDecl());
>>>> + });
>>>> +
>>>> + // Suggest a fixit to properly name the destroyed type.
>>>> + auto MakeFixItHint = [&]{
>>>> + const CXXRecordDecl *Destroyed = nullptr;
>>>> + // FIXME: If we have a scope specifier, suggest its last component?
>>>> + if (!SearchType.isNull())
>>>> + Destroyed = SearchType->getAsCXXRecordDecl();
>>>> + else if (S)
>>>> + Destroyed = dyn_cast_or_null<CXXRecordDecl>(S->getEntity());
>>>> + if (Destroyed)
>>>> + return FixItHint::CreateReplacement(SourceRange(NameLoc),
>>>> +
>>>> Destroyed->getNameAsString());
>>>> + return FixItHint();
>>>> + };
>>>> +
>>>> + if (FoundDecls.empty()) {
>>>> + // FIXME: Attempt typo-correction?
>>>> + Diag(NameLoc, diag::err_undeclared_destructor_name)
>>>> + << &II << MakeFixItHint();
>>>> + } else if (!SearchType.isNull() && FoundDecls.size() == 1) {
>>>> + if (auto *TD =
>>>> dyn_cast<TypeDecl>(FoundDecls[0]->getUnderlyingDecl())) {
>>>> + assert(!SearchType.isNull() &&
>>>> + "should only reject a type result if we have a search
>>>> type");
>>>> + QualType T = Context.getTypeDeclType(TD);
>>>> + Diag(NameLoc, diag::err_destructor_expr_type_mismatch)
>>>> + << T << SearchType << MakeFixItHint();
>>>> + } else {
>>>> + Diag(NameLoc, diag::err_destructor_expr_nontype)
>>>> + << &II << MakeFixItHint();
>>>> + }
>>>> + } else {
>>>> + Diag(NameLoc, SearchType.isNull() ?
>>>> diag::err_destructor_name_nontype
>>>> + :
>>>> diag::err_destructor_expr_mismatch)
>>>> + << &II << SearchType << MakeFixItHint();
>>>> + }
>>>> +
>>>> + for (NamedDecl *FoundD : FoundDecls) {
>>>> + if (auto *TD = dyn_cast<TypeDecl>(FoundD->getUnderlyingDecl()))
>>>> + Diag(FoundD->getLocation(), diag::note_destructor_type_here)
>>>> + << Context.getTypeDeclType(TD);
>>>> + else
>>>> + Diag(FoundD->getLocation(), diag::note_destructor_nontype_here)
>>>> + << FoundD;
>>>> + }
>>>> +
>>>> return nullptr;
>>>> }
>>>>
>>>>
>>>> diff --git a/clang/test/CXX/class/class.mem/p13.cpp
>>>> b/clang/test/CXX/class/class.mem/p13.cpp
>>>> index 1b1c0c7f8fc3..d947586c4194 100644
>>>> --- a/clang/test/CXX/class/class.mem/p13.cpp
>>>> +++ b/clang/test/CXX/class/class.mem/p13.cpp
>>>> @@ -110,7 +110,7 @@ template<typename B> struct Dtemplate_with_ctors :
>>>> B {
>>>> };
>>>>
>>>> template<typename B> struct CtorDtorName : B {
>>>> - using B::CtorDtorName; // expected-error {{member 'CtorDtorName' has
>>>> the same name as its class}}
>>>> + using B::CtorDtorName; // expected-error {{member 'CtorDtorName' has
>>>> the same name as its class}} expected-note {{non-type declaration found by
>>>> destructor name lookup}}
>>>> CtorDtorName();
>>>> - ~CtorDtorName(); // expected-error {{expected the class name after
>>>> '~' to name a destructor}}
>>>> + ~CtorDtorName(); // expected-error {{identifier 'CtorDtorName' after
>>>> '~' in destructor name does not name a type}}
>>>> };
>>>>
>>>> diff --git a/clang/test/CXX/drs/dr2xx.cpp
>>>> b/clang/test/CXX/drs/dr2xx.cpp
>>>> index 1f625efe2b55..905a2b07888d 100644
>>>> --- a/clang/test/CXX/drs/dr2xx.cpp
>>>> +++ b/clang/test/CXX/drs/dr2xx.cpp
>>>> @@ -474,45 +474,82 @@ namespace dr243 { // dr243: yes
>>>> A a2 = b; // expected-error {{ambiguous}}
>>>> }
>>>>
>>>> -namespace dr244 { // dr244: partial
>>>> - struct B {}; struct D : B {}; // expected-note {{here}}
>>>> +namespace dr244 { // dr244: 11
>>>> + struct B {}; // expected-note {{type 'dr244::B' found by destructor
>>>> name lookup}}
>>>> + struct D : B {};
>>>>
>>>> D D_object;
>>>> typedef B B_alias;
>>>> B* B_ptr = &D_object;
>>>>
>>>> void f() {
>>>> - D_object.~B(); // expected-error {{expression does not match the
>>>> type}}
>>>> + D_object.~B(); // expected-error {{does not match the type
>>>> 'dr244::D' of the object being destroyed}}
>>>> D_object.B::~B();
>>>> + D_object.D::~B(); // FIXME: Missing diagnostic for this.
>>>> B_ptr->~B();
>>>> B_ptr->~B_alias();
>>>> B_ptr->B_alias::~B();
>>>> - // This is valid under DR244.
>>>> B_ptr->B_alias::~B_alias();
>>>> B_ptr->dr244::~B(); // expected-error {{refers to a member in
>>>> namespace}}
>>>> B_ptr->dr244::~B_alias(); // expected-error {{refers to a member
>>>> in namespace}}
>>>> }
>>>>
>>>> + template<typename T, typename U>
>>>> + void f(T *B_ptr, U D_object) {
>>>> + D_object.~B(); // FIXME: Missing diagnostic for this.
>>>> + D_object.B::~B();
>>>> + D_object.D::~B(); // FIXME: Missing diagnostic for this.
>>>> + B_ptr->~B();
>>>> + B_ptr->~B_alias();
>>>> + B_ptr->B_alias::~B();
>>>> + B_ptr->B_alias::~B_alias();
>>>> + B_ptr->dr244::~B(); // expected-error {{does not refer to a type
>>>> name}}
>>>> + B_ptr->dr244::~B_alias(); // expected-error {{does not refer to a
>>>> type name}}
>>>> + }
>>>> + template void f<B, D>(B*, D);
>>>> +
>>>> namespace N {
>>>> template<typename T> struct E {};
>>>> typedef E<int> F;
>>>> }
>>>> void g(N::F f) {
>>>> - typedef N::F G;
>>>> + typedef N::F G; // expected-note {{found by destructor name
>>>> lookup}}
>>>> f.~G();
>>>> - f.G::~E();
>>>> - f.G::~F(); // expected-error {{expected the class name after '~'
>>>> to name a destructor}}
>>>> + f.G::~E(); // expected-error {{ISO C++ requires the name after
>>>> '::~' to be found in the same scope as the name before '::~'}}
>>>> + f.G::~F(); // expected-error {{undeclared identifier 'F' in
>>>> destructor name}}
>>>> f.G::~G();
>>>> // This is technically ill-formed; E is looked up in 'N::' and
>>>> names the
>>>> // class template, not the injected-class-name of the class. But
>>>> that's
>>>> // probably a bug in the standard.
>>>> - f.N::F::~E();
>>>> + f.N::F::~E(); // expected-error {{ISO C++ requires the name after
>>>> '::~' to be found in the same scope as the name before '::~'}}
>>>> // This is valid; we look up the second F in the same scope in
>>>> which we
>>>> // found the first one, that is, 'N::'.
>>>> - f.N::F::~F(); // FIXME: expected-error {{expected the class name
>>>> after '~' to name a destructor}}
>>>> - // This is technically ill-formed; G is looked up in 'N::' and is
>>>> not found;
>>>> - // as above, this is probably a bug in the standard.
>>>> - f.N::F::~G();
>>>> + f.N::F::~F();
>>>> + // This is technically ill-formed; G is looked up in 'N::' and is
>>>> not found.
>>>> + // Rejecting this seems correct, but most compilers accept, so we
>>>> do also.
>>>> + f.N::F::~G(); // expected-error {{qualified destructor name only
>>>> found in lexical scope; omit the qualifier to find this type name by
>>>> unqualified lookup}}
>>>> + }
>>>> +
>>>> + // Bizarrely, compilers perform lookup in the scope for qualified
>>>> destructor
>>>> + // names, if the nested-name-specifier is non-dependent. Ensure we
>>>> diagnose
>>>> + // this.
>>>> + namespace QualifiedLookupInScope {
>>>> + namespace N {
>>>> + template <typename> struct S { struct Inner {}; };
>>>> + }
>>>> + template <typename U> void f(typename N::S<U>::Inner *p) {
>>>> + typedef typename N::S<U>::Inner T;
>>>> + p->::dr244::QualifiedLookupInScope::N::S<U>::Inner::~T(); //
>>>> expected-error {{no type named 'T' in}}
>>>> + }
>>>> + template void f<int>(N::S<int>::Inner *); // expected-note
>>>> {{instantiation of}}
>>>> +
>>>> + template <typename U> void g(U *p) {
>>>> + typedef U T;
>>>> + p->T::~T();
>>>> + p->U::~T();
>>>> + p->::dr244::QualifiedLookupInScope::N::S<int>::Inner::~T(); //
>>>> expected-error {{'T' does not refer to a type name}}
>>>> + }
>>>> + template void g(N::S<int>::Inner *);
>>>> }
>>>> }
>>>>
>>>>
>>>> diff --git a/clang/test/CXX/drs/dr3xx.cpp
>>>> b/clang/test/CXX/drs/dr3xx.cpp
>>>> index d723c5b78cdf..4ce624974bbe 100644
>>>> --- a/clang/test/CXX/drs/dr3xx.cpp
>>>> +++ b/clang/test/CXX/drs/dr3xx.cpp
>>>> @@ -98,7 +98,7 @@ namespace dr305 { // dr305: no
>>>> b->~C();
>>>> }
>>>> void h(B *b) {
>>>> - struct B {}; // expected-note {{declared here}}
>>>> + struct B {}; // expected-note {{type 'B' found by destructor name
>>>> lookup}}
>>>> b->~B(); // expected-error {{does not match}}
>>>> }
>>>>
>>>>
>>>> diff --git a/clang/test/FixIt/fixit.cpp b/clang/test/FixIt/fixit.cpp
>>>> index 92c561a20acc..6e3a41303af9 100644
>>>> --- a/clang/test/FixIt/fixit.cpp
>>>> +++ b/clang/test/FixIt/fixit.cpp
>>>> @@ -2,7 +2,7 @@
>>>> // RUN: cp %s %t-98
>>>> // RUN: not %clang_cc1 -pedantic -Wall -Wno-comment -fcxx-exceptions
>>>> -fixit -x c++ -std=c++98 %t-98
>>>> // RUN: %clang_cc1 -fsyntax-only -pedantic -Wall -Werror -Wno-comment
>>>> -fcxx-exceptions -x c++ -std=c++98 %t-98
>>>> -// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -x
>>>> c++ -std=c++11 %s 2>&1 | FileCheck %s
>>>> +// RUN: not %clang_cc1 -fsyntax-only -pedantic
>>>> -fdiagnostics-parseable-fixits -x c++ -std=c++11 %s 2>&1 | FileCheck %s
>>>> // RUN: %clang_cc1 -pedantic -Wall -Wno-comment -verify
>>>> -fcxx-exceptions -x c++ -std=c++11 %s
>>>> // RUN: cp %s %t-11
>>>> // RUN: not %clang_cc1 -pedantic -Wall -Wno-comment -fcxx-exceptions
>>>> -fixit -x c++ -std=c++11 %t-11
>>>> @@ -318,17 +318,43 @@ class foo {
>>>> };
>>>>
>>>> namespace dtor_fixit {
>>>> - class foo {
>>>> - ~bar() { } // expected-error {{expected the class name after '~'
>>>> to name a destructor}}
>>>> + struct foo {
>>>> + ~bar() { } // expected-error {{undeclared identifier 'bar' in
>>>> destructor name}}
>>>> // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:6-[[@LINE-1]]:9}:"foo"
>>>> };
>>>>
>>>> - class bar {
>>>> + class bar { // expected-note {{found}}
>>>> ~bar();
>>>> };
>>>> ~bar::bar() {} // expected-error {{'~' in destructor name should be
>>>> after nested name specifier}}
>>>> // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:4}:""
>>>> // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:9-[[@LINE-2]]:9}:"~"
>>>> +
>>>> + namespace N {
>>>> + typedef foo T;
>>>> + template <typename T> struct X {};
>>>> + }
>>>> + void f(foo *p, N::X<int> *x) {
>>>> + p->~undeclared(); // expected-error {{undeclared}}
>>>> + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:19}:"foo"
>>>> +
>>>> + p->~bar(); // expected-error {{does not match}}
>>>> + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:12}:"foo"
>>>> +
>>>> + // FIXME: This is a bad fixit; it'd be better to suggest replacing
>>>> 'foo'
>>>> + // with 'T'.
>>>> + p->N::T::~foo(); // expected-warning {{requires the name after
>>>> '::~' to be found in the same scope as the name before}}
>>>> + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:12-[[@LINE-1]]:12}:"::foo"
>>>> +
>>>> + typedef foo baz; // expected-note {{found}}
>>>> + p->dtor_fixit::foo::~baz(); // expected-warning {{only found in
>>>> lexical scope}}
>>>> + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:8-[[@LINE-1]]:25}:""
>>>> +
>>>> + // FIXME: This is a bad fixit; it'd be better to suggest adding the
>>>> + // template arguments.
>>>> + x->N::X<int>::~X(); // expected-warning {{same scope}}
>>>> + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:17-[[@LINE-1]]:17}:"::X"
>>>> + }
>>>> }
>>>>
>>>> namespace PR5066 {
>>>>
>>>> diff --git a/clang/test/Parser/cxx-decl.cpp
>>>> b/clang/test/Parser/cxx-decl.cpp
>>>> index 1914f347d9dd..ba1cce419a46 100644
>>>> --- a/clang/test/Parser/cxx-decl.cpp
>>>> +++ b/clang/test/Parser/cxx-decl.cpp
>>>> @@ -249,10 +249,10 @@ void foo() {
>>>> namespace PR17567 {
>>>> struct Foobar { // expected-note 2{{declared here}}
>>>> FooBar(); // expected-error {{missing return type for function
>>>> 'FooBar'; did you mean the constructor name 'Foobar'?}}
>>>> - ~FooBar(); // expected-error {{expected the class name after '~'
>>>> to name a destructor}}
>>>> + ~FooBar(); // expected-error {{undeclared identifier 'FooBar' in
>>>> destructor name}}
>>>> };
>>>> FooBar::FooBar() {} // expected-error {{undeclared}} expected-error
>>>> {{missing return type}}
>>>> - FooBar::~FooBar() {} // expected-error {{undeclared}} expected-error
>>>> {{expected the class name}}
>>>> + FooBar::~FooBar() {} // expected-error 2{{undeclared}}
>>>> }
>>>>
>>>> namespace DuplicateFriend {
>>>>
>>>> diff --git a/clang/test/SemaCXX/constructor.cpp
>>>> b/clang/test/SemaCXX/constructor.cpp
>>>> index 33ea49663491..d2133240cb14 100644
>>>> --- a/clang/test/SemaCXX/constructor.cpp
>>>> +++ b/clang/test/SemaCXX/constructor.cpp
>>>> @@ -94,6 +94,6 @@ namespace PR38286 {
>>>> /*FIXME: needed to recover properly from previous error*/;
>>>> template<typename> struct B;
>>>> template<typename T> void B<T>::f() {} // expected-error
>>>> {{out-of-line definition of 'f' from class 'B<type-parameter-0-0>'}}
>>>> - template<typename> struct C;
>>>> - template<typename T> C<T>::~C() {} // expected-error {{no type named
>>>> 'C' in 'C<type-parameter-0-0>'}}
>>>> + template<typename> struct C; // expected-note {{non-type declaration
>>>> found}}
>>>> + template<typename T> C<T>::~C() {} // expected-error {{identifier
>>>> 'C' after '~' in destructor name does not name a type}}
>>>> }
>>>>
>>>> diff --git a/clang/test/SemaCXX/destructor.cpp
>>>> b/clang/test/SemaCXX/destructor.cpp
>>>> index 2859953a0280..92afc1256ced 100644
>>>> --- a/clang/test/SemaCXX/destructor.cpp
>>>> +++ b/clang/test/SemaCXX/destructor.cpp
>>>> @@ -75,7 +75,7 @@ struct F {
>>>> };
>>>>
>>>> ~; // expected-error {{expected a class name after '~' to name a
>>>> destructor}}
>>>> -~undef(); // expected-error {{expected the class name after '~' to
>>>> name a destructor}}
>>>> +~undef(); // expected-error {{undeclared identifier 'undef' in
>>>> destructor name}}
>>>> ~operator+(int, int); // expected-error {{expected a class name after
>>>> '~' to name a destructor}}
>>>> ~F(){} // expected-error {{destructor must be a non-static member
>>>> function}}
>>>>
>>>> @@ -432,7 +432,7 @@ namespace PR9238 {
>>>> }
>>>>
>>>> namespace PR7900 {
>>>> - struct A { // expected-note 2{{type 'PR7900::A' is declared here}}
>>>> + struct A { // expected-note 2{{type 'PR7900::A' found by destructor
>>>> name lookup}}
>>>> };
>>>> struct B : public A {
>>>> };
>>>>
>>>> diff --git a/clang/test/SemaCXX/pseudo-destructors.cpp
>>>> b/clang/test/SemaCXX/pseudo-destructors.cpp
>>>> index dfdd1174b8a4..0cd139047432 100644
>>>> --- a/clang/test/SemaCXX/pseudo-destructors.cpp
>>>> +++ b/clang/test/SemaCXX/pseudo-destructors.cpp
>>>> @@ -2,7 +2,7 @@
>>>> struct A {};
>>>>
>>>> enum Foo { F };
>>>> -typedef Foo Bar; // expected-note{{type 'Bar' (aka 'Foo') is declared
>>>> here}}
>>>> +typedef Foo Bar; // expected-note{{type 'Bar' (aka 'Foo') found by
>>>> destructor name lookup}}
>>>>
>>>> typedef int Integer;
>>>> typedef double Double;
>>>> @@ -23,7 +23,7 @@ void f(A* a, Foo *f, int *i, double *d, int ii) {
>>>> a->~A();
>>>> a->A::~A();
>>>>
>>>> - a->~foo(); // expected-error{{identifier 'foo' in object destruction
>>>> expression does not name a type}}
>>>> + a->~foo(); // expected-error{{undeclared identifier 'foo' in
>>>> destructor name}}
>>>>
>>>> a->~Bar(); // expected-error{{destructor type 'Bar' (aka 'Foo') in
>>>> object destruction expression does not match the type 'A' of the object
>>>> being destroyed}}
>>>>
>>>> @@ -83,7 +83,7 @@ namespace PR11339 {
>>>> template<class T>
>>>> void destroy(T* p) {
>>>> p->~T(); // ok
>>>> - p->~oops(); // expected-error{{identifier 'oops' in object
>>>> destruction expression does not name a type}}
>>>> + p->~oops(); // expected-error{{undeclared identifier 'oops' in
>>>> destructor name}}
>>>> }
>>>>
>>>> template void destroy(int*); // expected-note{{in instantiation of
>>>> function template specialization}}
>>>>
>>>>
>>>>
>>>> _______________________________________________
>>>> cfe-commits mailing list
>>>> cfe-commits at lists.llvm.org
>>>> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>>>
>>> _______________________________________________
>>> cfe-commits mailing list
>>> cfe-commits at lists.llvm.org
>>> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>>
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits at lists.llvm.org
>> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20201106/419dc17b/attachment-0001.html>
More information about the cfe-commits
mailing list