From: John McCall Date: Wed, 31 Mar 2010 01:36:47 +0000 (+0000) Subject: Regularize support for naming conversion functions in using decls. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=32daa4223ccb2c0afe5fbe151c6eb1ab64816957;p=clang Regularize support for naming conversion functions in using decls. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@99979 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 2ed2cefe5d..f99b9655f5 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -329,6 +329,10 @@ class CXXRecordDecl : public RecordDecl { /// instantiated or specialized. llvm::PointerUnion TemplateOrInstantiation; + +#ifndef NDEBUG + void CheckConversionFunction(NamedDecl *D); +#endif protected: CXXRecordDecl(Kind K, TagKind TK, DeclContext *DC, @@ -550,17 +554,26 @@ public: return getConversionFunctions()->replace(Old, New); } + /// Removes a conversion function from this class. The conversion + /// function must currently be a member of this class. Furthermore, + /// this class must currently be in the process of being defined. + void removeConversion(const NamedDecl *Old); + /// getVisibleConversionFunctions - get all conversion functions visible /// in current class; including conversion function templates. const UnresolvedSetImpl *getVisibleConversionFunctions(); - /// addConversionFunction - Add a new conversion function to the - /// list of conversion functions. - void addConversionFunction(CXXConversionDecl *ConvDecl); + /// addConversionFunction - Registers a conversion function which + /// this class declares directly. + void addConversionFunction(NamedDecl *Decl) { +#ifndef NDEBUG + CheckConversionFunction(Decl); +#endif - /// \brief Add a new conversion function template to the list of conversion - /// functions. - void addConversionFunction(FunctionTemplateDecl *ConvDecl); + // We intentionally don't use the decl's access here because it + // hasn't been set yet. That's really just a misdesign in Sema. + data().Conversions.addDecl(Decl); + } /// isAggregate - Whether this class is an aggregate (C++ /// [dcl.init.aggr]), which is a class with no user-declared diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index ed02edd394..94ed85c7cd 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -308,6 +308,8 @@ void CXXRecordDecl::addedAssignmentOperator(ASTContext &Context, static CanQualType GetConversionType(ASTContext &Context, NamedDecl *Conv) { QualType T; + if (isa(Conv)) + Conv = cast(Conv)->getTargetDecl(); if (FunctionTemplateDecl *ConvTemp = dyn_cast(Conv)) T = ConvTemp->getTemplatedDecl()->getResultType(); else @@ -445,26 +447,45 @@ const UnresolvedSetImpl *CXXRecordDecl::getVisibleConversionFunctions() { return &data().VisibleConversions; } -void CXXRecordDecl::addConversionFunction(CXXConversionDecl *ConvDecl) { - assert(!ConvDecl->getDescribedFunctionTemplate() && - "Conversion function templates should cast to FunctionTemplateDecl."); +#ifndef NDEBUG +void CXXRecordDecl::CheckConversionFunction(NamedDecl *ConvDecl) { assert(ConvDecl->getDeclContext() == this && "conversion function does not belong to this record"); - // We intentionally don't use the decl's access here because it - // hasn't been set yet. That's really just a misdesign in Sema. - data().Conversions.addDecl(ConvDecl); + ConvDecl = ConvDecl->getUnderlyingDecl(); + if (FunctionTemplateDecl *Temp = dyn_cast(ConvDecl)) { + assert(isa(Temp->getTemplatedDecl())); + } else { + assert(isa(ConvDecl)); + } } +#endif + +void CXXRecordDecl::removeConversion(const NamedDecl *ConvDecl) { + // This operation is O(N) but extremely rare. Sema only uses it to + // remove UsingShadowDecls in a class that were followed by a direct + // declaration, e.g.: + // class A : B { + // using B::operator int; + // operator int(); + // }; + // This is uncommon by itself and even more uncommon in conjunction + // with sufficiently large numbers of directly-declared conversions + // that asymptotic behavior matters. + + UnresolvedSetImpl &Convs = *getConversionFunctions(); + for (unsigned I = 0, E = Convs.size(); I != E; ++I) { + if (Convs[I].getDecl() == ConvDecl) { + Convs.erase(I); + assert(std::find(Convs.begin(), Convs.end(), ConvDecl) == Convs.end() + && "conversion was found multiple times in unresolved set"); + return; + } + } -void CXXRecordDecl::addConversionFunction(FunctionTemplateDecl *ConvDecl) { - assert(isa(ConvDecl->getTemplatedDecl()) && - "Function template is not a conversion function template"); - assert(ConvDecl->getDeclContext() == this && - "conversion function does not belong to this record"); - data().Conversions.addDecl(ConvDecl); + llvm_unreachable("conversion not found in set!"); } - void CXXRecordDecl::setMethodAsVirtual(FunctionDecl *Method) { Method->setVirtualAsWritten(true); setAggregate(false); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index dfaa85559b..3c3786d217 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -3327,6 +3327,11 @@ UsingShadowDecl *Sema::BuildUsingShadowDecl(Scope *S, CurContext->addDecl(Shadow); Shadow->setAccess(UD->getAccess()); + // Register it as a conversion if appropriate. + if (Shadow->getDeclName().getNameKind() + == DeclarationName::CXXConversionFunctionName) + cast(CurContext)->addConversionFunction(Shadow); + if (Orig->isInvalidDecl() || UD->isInvalidDecl()) Shadow->setInvalidDecl(); @@ -3361,6 +3366,10 @@ UsingShadowDecl *Sema::BuildUsingShadowDecl(Scope *S, /// decl structures are (very reasonably) not designed for removal. /// (2) avoids this but is very fiddly and phase-dependent. void Sema::HideUsingShadowDecl(Scope *S, UsingShadowDecl *Shadow) { + if (Shadow->getDeclName().getNameKind() == + DeclarationName::CXXConversionFunctionName) + cast(Shadow->getDeclContext())->removeConversion(Shadow); + // Remove it from the DeclContext... Shadow->getDeclContext()->removeDecl(Shadow); @@ -3374,7 +3383,7 @@ void Sema::HideUsingShadowDecl(Scope *S, UsingShadowDecl *Shadow) { Shadow->getUsingDecl()->removeShadowDecl(Shadow); // TODO: complain somehow if Shadow was used. It shouldn't - // be possible for this to happen, because + // be possible for this to happen, because...? } /// Builds a using declaration. diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 9baa6ac079..3befb7e24e 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1302,17 +1302,21 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, QualType Type = Ex->getType(); if (const RecordType *Record = Type->getAs()) { - llvm::SmallVector ObjectPtrConversions; + llvm::SmallVector ObjectPtrConversions; + CXXRecordDecl *RD = cast(Record->getDecl()); - const UnresolvedSetImpl *Conversions = RD->getVisibleConversionFunctions(); - + const UnresolvedSetImpl *Conversions = RD->getVisibleConversionFunctions(); for (UnresolvedSetImpl::iterator I = Conversions->begin(), E = Conversions->end(); I != E; ++I) { + NamedDecl *D = I.getDecl(); + if (isa(D)) + D = cast(D)->getTargetDecl(); + // Skip over templated conversion functions; they aren't considered. - if (isa(*I)) + if (isa(D)) continue; - CXXConversionDecl *Conv = cast(*I); + CXXConversionDecl *Conv = cast(D); QualType ConvType = Conv->getConversionType().getNonReferenceType(); if (const PointerType *ConvPtrType = ConvType->getAs()) @@ -1322,9 +1326,10 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, if (ObjectPtrConversions.size() == 1) { // We have a single conversion to a pointer-to-object type. Perform // that conversion. + // TODO: don't redo the conversion calculation. Operand.release(); - if (!PerformImplicitConversion(Ex, - ObjectPtrConversions.front()->getConversionType(), + if (!PerformImplicitConversion(Ex, + ObjectPtrConversions.front()->getConversionType(), AA_Converting)) { Operand = Owned(Ex); Type = Ex->getType(); @@ -1333,10 +1338,8 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, else if (ObjectPtrConversions.size() > 1) { Diag(StartLoc, diag::err_ambiguous_delete_operand) << Type << Ex->getSourceRange(); - for (unsigned i= 0; i < ObjectPtrConversions.size(); i++) { - CXXConversionDecl *Conv = ObjectPtrConversions[i]; - NoteOverloadCandidate(Conv); - } + for (unsigned i= 0; i < ObjectPtrConversions.size(); i++) + NoteOverloadCandidate(ObjectPtrConversions[i]); return ExprError(); } } diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 4612bda7b2..00e2fba5fe 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -2838,7 +2838,7 @@ static void TryUserDefinedConversion(Sema &S, if (ConvTemplate) Conv = cast(ConvTemplate->getTemplatedDecl()); else - Conv = cast(*I); + Conv = cast(D); if (AllowExplicit || !Conv->isExplicit()) { if (ConvTemplate) diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 8d3313982c..be367c3660 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -1582,10 +1582,10 @@ OverloadingResult Sema::IsUserDefinedConversion(Expr *From, QualType ToType, CXXConversionDecl *Conv; FunctionTemplateDecl *ConvTemplate; - if ((ConvTemplate = dyn_cast(*I))) - Conv = dyn_cast(ConvTemplate->getTemplatedDecl()); + if ((ConvTemplate = dyn_cast(D))) + Conv = cast(ConvTemplate->getTemplatedDecl()); else - Conv = dyn_cast(*I); + Conv = cast(D); if (AllowExplicit || !Conv->isExplicit()) { if (ConvTemplate) @@ -3310,13 +3310,16 @@ BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty, = ClassDecl->getVisibleConversionFunctions(); for (UnresolvedSetImpl::iterator I = Conversions->begin(), E = Conversions->end(); I != E; ++I) { + NamedDecl *D = I.getDecl(); + if (isa(D)) + D = cast(D)->getTargetDecl(); // Skip conversion function templates; they don't tell us anything // about which builtin types we can convert to. - if (isa(*I)) + if (isa(D)) continue; - CXXConversionDecl *Conv = cast(*I); + CXXConversionDecl *Conv = cast(D); if (AllowExplicitConversions || !Conv->isExplicit()) { AddTypesConvertedFrom(Conv->getConversionType(), Loc, false, false, VisibleQuals); @@ -3378,7 +3381,10 @@ static Qualifiers CollectVRQualifiers(ASTContext &Context, Expr* ArgExpr) { for (UnresolvedSetImpl::iterator I = Conversions->begin(), E = Conversions->end(); I != E; ++I) { - if (CXXConversionDecl *Conv = dyn_cast(*I)) { + NamedDecl *D = I.getDecl(); + if (isa(D)) + D = cast(D)->getTargetDecl(); + if (CXXConversionDecl *Conv = dyn_cast(D)) { QualType CanTy = Context.getCanonicalType(Conv->getConversionType()); if (const ReferenceType *ResTypeRef = CanTy->getAs()) CanTy = ResTypeRef->getPointeeType(); diff --git a/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p1.cpp b/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p1.cpp index 935f576788..89e9c897d2 100644 --- a/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p1.cpp +++ b/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p1.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -faccess-control -verify %s // We have to avoid ADL for this test. @@ -65,3 +65,44 @@ namespace Test1 { b _2 = B::b(); } } + +namespace test2 { + class A { + protected: + operator int(); + operator bool(); + }; + + class B : private A { + protected: + using A::operator int; // expected-note {{'declared protected here'}} + public: + using A::operator bool; + }; + + int test() { + bool b = B(); + return B(); // expected-error {{'operator int' is a protected member of 'test2::B'}} + } +} + +namespace test3 { + class A { + ~A(); + }; + + class B { + friend class C; + private: + operator A*(); + }; + + class C : public B { + public: + using B::operator A*; + }; + + void test() { + delete C(); + } +}