[cfe-commits] r90289 - in /cfe/trunk: lib/Sema/Sema.h lib/Sema/SemaExpr.cpp lib/Sema/SemaTemplate.cpp test/CXX/class/class.mfct/class.mfct.non-static/ test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp
John McCall
rjmccall at apple.com
Tue Dec 1 19:53:29 PST 2009
Author: rjmccall
Date: Tue Dec 1 21:53:29 2009
New Revision: 90289
URL: http://llvm.org/viewvc/llvm-project?rev=90289&view=rev
Log:
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.
Added:
cfe/trunk/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp
Modified:
cfe/trunk/lib/Sema/Sema.h
cfe/trunk/lib/Sema/SemaExpr.cpp
cfe/trunk/lib/Sema/SemaTemplate.cpp
cfe/trunk/test/CXX/class/class.mfct/class.mfct.non-static/ (props changed)
Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=90289&r1=90288&r2=90289&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Tue Dec 1 21:53:29 2009
@@ -1438,7 +1438,7 @@
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 @@
DeclPtrTy ObjCImpDecl);
bool CheckQualifiedMemberReference(Expr *BaseExpr, QualType BaseType,
- NestedNameSpecifier *Qualifier,
- SourceRange QualifierRange,
+ const CXXScopeSpec &SS,
const LookupResult &R);
OwningExprResult ActOnDependentMemberExpr(ExprArg Base,
Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=90289&r1=90288&r2=90289&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Tue Dec 1 21:53:29 2009
@@ -901,10 +901,8 @@
// 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 @@
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 @@
// 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 @@
return false;
}
- DiagnoseQualifiedMemberReference(*this, BaseExpr, BaseType,
- Qualifier, QualifierRange, R);
+ DiagnoseQualifiedMemberReference(*this, BaseExpr, BaseType, SS, R);
return true;
}
@@ -2216,6 +2215,12 @@
// 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 @@
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 @@
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
Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=90289&r1=90288&r2=90289&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Tue Dec 1 21:53:29 2009
@@ -248,119 +248,23 @@
}
}
-/// 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;
Propchange: cfe/trunk/test/CXX/class/class.mfct/class.mfct.non-static/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Tue Dec 1 21:53:29 2009
@@ -0,0 +1 @@
+Output
Added: cfe/trunk/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp?rev=90289&view=auto
==============================================================================
--- cfe/trunk/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp (added)
+++ cfe/trunk/test/CXX/class/class.mfct/class.mfct.non-static/p3.cpp Tue Dec 1 21:53:29 2009
@@ -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}}
+ }
+}
More information about the cfe-commits
mailing list