diff options
-rw-r--r-- | clang/lib/Sema/Sema.h | 5 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 40 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 114 | ||||
-rw-r--r-- | clang/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp | 93 |
4 files changed, 127 insertions, 125 deletions
diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 3f8babbbbe9..8503887a617 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -1438,7 +1438,7 @@ public: OwningExprResult ActOnDependentIdExpression(const CXXScopeSpec &SS, DeclarationName Name, SourceLocation NameLoc, - bool CheckForImplicitMember, + bool isAddressOfOperand, const TemplateArgumentListInfo *TemplateArgs); OwningExprResult BuildDeclRefExpr(NamedDecl *D, QualType Ty, @@ -1549,8 +1549,7 @@ public: DeclPtrTy ObjCImpDecl); bool CheckQualifiedMemberReference(Expr *BaseExpr, QualType BaseType, - NestedNameSpecifier *Qualifier, - SourceRange QualifierRange, + const CXXScopeSpec &SS, const LookupResult &R); OwningExprResult ActOnDependentMemberExpr(ExprArg Base, diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 36ab6ed44bd..ae6ba321a22 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -901,10 +901,8 @@ Sema::OwningExprResult Sema::ActOnIdExpression(Scope *S, // Determine whether this is a member of an unknown specialization; // we need to handle these differently. if (SS.isSet() && IsDependentIdExpression(*this, SS)) { - bool CheckForImplicitMember = !isAddressOfOperand; - return ActOnDependentIdExpression(SS, Name, NameLoc, - CheckForImplicitMember, + isAddressOfOperand, TemplateArgs); } @@ -2140,15 +2138,18 @@ Sema::ActOnDependentMemberExpr(ExprArg Base, QualType BaseType, static void DiagnoseQualifiedMemberReference(Sema &SemaRef, Expr *BaseExpr, QualType BaseType, - NestedNameSpecifier *Qualifier, - SourceRange QualifierRange, + const CXXScopeSpec &SS, const LookupResult &R) { - DeclContext *DC = R.getRepresentativeDecl()->getDeclContext(); + // If this is an implicit member access, use a different set of + // diagnostics. + if (!BaseExpr) + return DiagnoseInstanceReference(SemaRef, SS, R); // FIXME: this is an exceedingly lame diagnostic for some of the more // complicated cases here. + DeclContext *DC = R.getRepresentativeDecl()->getDeclContext(); SemaRef.Diag(R.getNameLoc(), diag::err_not_direct_base_or_virtual) - << QualifierRange << DC << BaseType; + << SS.getRange() << DC << BaseType; } // Check whether the declarations we found through a nested-name @@ -2165,8 +2166,7 @@ static void DiagnoseQualifiedMemberReference(Sema &SemaRef, // we actually pick through overload resolution is from a superclass. bool Sema::CheckQualifiedMemberReference(Expr *BaseExpr, QualType BaseType, - NestedNameSpecifier *Qualifier, - SourceRange QualifierRange, + const CXXScopeSpec &SS, const LookupResult &R) { const RecordType *BaseRT = BaseType->getAs<RecordType>(); if (!BaseRT) { @@ -2195,8 +2195,7 @@ bool Sema::CheckQualifiedMemberReference(Expr *BaseExpr, return false; } - DiagnoseQualifiedMemberReference(*this, BaseExpr, BaseType, - Qualifier, QualifierRange, R); + DiagnoseQualifiedMemberReference(*this, BaseExpr, BaseType, SS, R); return true; } @@ -2216,6 +2215,12 @@ LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, // nested-name-specifier. DC = SemaRef.computeDeclContext(SS, false); + if (SemaRef.RequireCompleteDeclContext(SS)) { + SemaRef.Diag(SS.getRange().getEnd(), diag::err_typecheck_incomplete_tag) + << SS.getRange() << DC; + return true; + } + assert(DC && "Cannot handle non-computable dependent contexts in lookup"); if (!isa<TypeDecl>(DC)) { @@ -2240,7 +2245,8 @@ Sema::BuildMemberReferenceExpr(ExprArg BaseArg, QualType BaseType, const TemplateArgumentListInfo *TemplateArgs) { Expr *Base = BaseArg.takeAs<Expr>(); - if (BaseType->isDependentType()) + if (BaseType->isDependentType() || + (SS.isSet() && isDependentScopeSpecifier(SS))) return ActOnDependentMemberExpr(ExprArg(*this, Base), BaseType, IsArrow, OpLoc, SS, FirstQualifierInScope, @@ -2311,11 +2317,11 @@ Sema::BuildMemberReferenceExpr(ExprArg Base, QualType BaseExprType, return ExprError(); } - // We can't always diagnose the problem yet: it's permitted for - // lookup to find things from an invalid context as long as they - // don't get picked by overload resolution. - if (SS.isSet() && CheckQualifiedMemberReference(BaseExpr, BaseType, - Qualifier, SS.getRange(), R)) + // Diagnose qualified lookups that find only declarations from a + // non-base type. Note that it's okay for lookup to find + // declarations from a non-base type as long as those aren't the + // ones picked by overload resolution. + if (SS.isSet() && CheckQualifiedMemberReference(BaseExpr, BaseType, SS, R)) return ExprError(); // Construct an unresolved result if we in fact got an unresolved diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 5f5d891a226..565581a138b 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -248,119 +248,23 @@ void Sema::LookupTemplateName(LookupResult &Found, } } -/// Constructs a full type for the given nested-name-specifier. -static QualType GetTypeForQualifier(ASTContext &Context, - NestedNameSpecifier *Qualifier) { - // Three possibilities: - - // 1. A namespace (global or not). - assert(!Qualifier->getAsNamespace() && "can't construct type for namespace"); - - // 2. A type (templated or not). - Type *Ty = Qualifier->getAsType(); - if (Ty) return QualType(Ty, 0); - - // 3. A dependent identifier. - assert(Qualifier->getAsIdentifier()); - return Context.getTypenameType(Qualifier->getPrefix(), - Qualifier->getAsIdentifier()); -} - -static bool HasDependentTypeAsBase(ASTContext &Context, - CXXRecordDecl *Record, - CanQualType T) { - for (CXXRecordDecl::base_class_iterator I = Record->bases_begin(), - E = Record->bases_end(); I != E; ++I) { - CanQualType BaseT = Context.getCanonicalType((*I).getType()); - if (BaseT == T) - return true; - - // We have to recurse here to cover some really bizarre cases. - // Obviously, we can only have the dependent type as an indirect - // base class through a dependent base class, and usually it's - // impossible to know which instantiation a dependent base class - // will have. But! If we're actually *inside* the dependent base - // class, then we know its instantiation and can therefore be - // reasonably expected to look into it. - - // template <class T> class A : Base<T> { - // class Inner : A<T> { - // void foo() { - // Base<T>::foo(); // statically known to be an implicit member - // reference - // } - // }; - // }; - - CanQual<RecordType> RT = BaseT->getAs<RecordType>(); - - // Base might be a dependent member type, in which case we - // obviously can't look into it. - if (!RT) continue; - - CXXRecordDecl *BaseRecord = cast<CXXRecordDecl>(RT->getDecl()); - if (BaseRecord->isDefinition() && - HasDependentTypeAsBase(Context, BaseRecord, T)) - return true; - } - - return false; -} - -/// Checks whether the given dependent nested-name specifier -/// introduces an implicit member reference. This is only true if the -/// nested-name specifier names a type identical to one of the current -/// instance method's context's (possibly indirect) base classes. -static bool IsImplicitDependentMemberReference(Sema &SemaRef, - NestedNameSpecifier *Qualifier, - QualType &ThisType) { - // If the context isn't a C++ method, then it isn't an implicit - // member reference. - CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(SemaRef.CurContext); - if (!MD || MD->isStatic()) - return false; - - ASTContext &Context = SemaRef.Context; - - // We want to check whether the method's context is known to inherit - // from the type named by the nested name specifier. The trivial - // case here is: - // template <class T> class Base { ... }; - // template <class T> class Derived : Base<T> { - // void foo() { - // Base<T>::foo(); - // } - // }; - - QualType QT = GetTypeForQualifier(Context, Qualifier); - CanQualType T = Context.getCanonicalType(QT); - - // And now, just walk the non-dependent type hierarchy, trying to - // find the given type as a literal base class. - CXXRecordDecl *Record = cast<CXXRecordDecl>(MD->getParent()); - if (Context.getCanonicalType(Context.getTypeDeclType(Record)) == T || - HasDependentTypeAsBase(Context, Record, T)) { - ThisType = MD->getThisType(Context); - return true; - } - - return false; -} - -/// ActOnDependentIdExpression - Handle a dependent declaration name -/// that was just parsed. +/// ActOnDependentIdExpression - Handle a dependent id-expression that +/// was just parsed. This is only possible with an explicit scope +/// specifier naming a dependent type. Sema::OwningExprResult Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS, DeclarationName Name, SourceLocation NameLoc, - bool CheckForImplicitMember, + bool isAddressOfOperand, const TemplateArgumentListInfo *TemplateArgs) { NestedNameSpecifier *Qualifier = static_cast<NestedNameSpecifier*>(SS.getScopeRep()); - QualType ThisType; - if (CheckForImplicitMember && - IsImplicitDependentMemberReference(*this, Qualifier, ThisType)) { + if (!isAddressOfOperand && + isa<CXXMethodDecl>(CurContext) && + cast<CXXMethodDecl>(CurContext)->isInstance()) { + QualType ThisType = cast<CXXMethodDecl>(CurContext)->getThisType(Context); + // Since the 'this' expression is synthesized, we don't need to // perform the double-lookup check. NamedDecl *FirstQualifierInScope = 0; diff --git a/clang/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp b/clang/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp new file mode 100644 index 00000000000..b90661deefe --- /dev/null +++ b/clang/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp @@ -0,0 +1,93 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +// [class.mfct.non-static]p3: +// When an id-expression (5.1) that is not part of a class member +// access syntax (5.2.5) and not used to form a pointer to member +// (5.3.1) is used in the body of a non-static member function of +// class X, if name lookup (3.4.1) resolves the name in the +// id-expression to a non-static non-type member of some class C, +// the id-expression is transformed into a class member access +// expression (5.2.5) using (*this) (9.3.2) as the +// postfix-expression to the left of the . operator. [ Note: if C is +// not X or a base class of X, the class member access expression is +// ill-formed. --end note] Similarly during name lookup, when an +// unqualified-id (5.1) used in the definition of a member function +// for class X resolves to a static member, an enumerator or a +// nested type of class X or of a base class of X, the +// unqualified-id is transformed into a qualified-id (5.1) in which +// the nested-name-specifier names the class of the member function. + +namespace test0 { + class A { + int data_member; + int instance_method(); + static int static_method(); + + bool test() { + return data_member + instance_method() < static_method(); + } + }; +} + +namespace test1 { + struct Opaque1 {}; struct Opaque2 {}; struct Opaque3 {}; + + struct A { + void foo(Opaque1); // expected-note {{candidate}} + void foo(Opaque2); // expected-note {{candidate}} + void test(); + }; + + struct B : A { + + }; + + void A::test() { + B::foo(Opaque1()); + B::foo(Opaque2()); + B::foo(Opaque3()); // expected-error {{no matching member function}} + } +} + +namespace test2 { + class Unrelated { + void foo(); + }; + + template <class T> struct B; + template <class T> struct C; + + template <class T> struct A { + void foo(); + + void test0() { + Unrelated::foo(); // expected-error {{call to non-static member function without an object argument}} + } + + void test1() { + B<T>::foo(); + } + + static void test2() { + B<T>::foo(); // expected-error {{call to non-static member function without an object argument}} + } + + void test3() { + C<T>::foo(); // expected-error {{no member named 'foo'}} + } + }; + + template <class T> struct B : A<T> { + }; + + template <class T> struct C { + }; + + int test() { + A<int> a; + a.test0(); // no instantiation note here, decl is ill-formed + a.test1(); + a.test2(); // expected-note {{in instantiation}} + a.test3(); // expected-note {{in instantiation}} + } +} |