forked from OSchip/llvm-project
Stop trying to analyze class-hierarchies for dependently-scoped id-expressions;
there's nothing interesting we can say now that we're correctly not requiring the qualifier to name a known base class in dependent contexts. Require scope specifiers on member access expressions to name complete types if they're not dependent; delay lookup when they are dependent. Use more appropriate diagnostics when qualified implicit member access expressions find declarations from unrelated classes. llvm-svn: 90289
This commit is contained in:
parent
55a17b66cd
commit
cd4b47747b
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue