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:
John McCall 2009-12-02 03:53:29 +00:00
parent 55a17b66cd
commit cd4b47747b
4 changed files with 127 additions and 125 deletions

View File

@ -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,

View File

@ -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

View File

@ -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;

View File

@ -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}}
}
}