// C++ name lookup
def err_incomplete_nested_name_spec : Error<
"incomplete type %0 named in nested name specifier">;
+def err_nested_name_member_ref_lookup_ambiguous : Error<
+ "lookup of %0 in member access expression is ambiguous">;
+def note_ambig_member_ref_object_type : Note<
+ "lookup in the object type %0 refers here">;
+def note_ambig_member_ref_scope : Note<
+ "lookup from the current scope refers here">;
// C++ class members
def err_storageclass_invalid_for_member : Error<
/// \brief Determine whether the given identifier refers to the name of a
/// template.
///
+ /// \param S the scope in which name lookup occurs
+ ///
/// \param II the identifier that we are querying to determine whether it
/// is a template.
///
- /// \param S the scope in which name lookup occurs
+ /// \param IdLoc the source location of the identifier
///
/// \param SS the C++ scope specifier that precedes the template name, if
/// any.
/// of the template that the name refers to.
///
/// \returns the kind of template that this name refers to.
- virtual TemplateNameKind isTemplateName(const IdentifierInfo &II, Scope *S,
+ virtual TemplateNameKind isTemplateName(Scope *S,
+ const IdentifierInfo &II,
+ SourceLocation IdLoc,
const CXXScopeSpec *SS,
+ TypeTy *ObjectType,
bool EnteringContext,
TemplateTy &Template) = 0;
return 0;
}
- /// ActOnCXXNestedNameSpecifier - Called during parsing of a
- /// nested-name-specifier. e.g. for "foo::bar::" we parsed "foo::" and now
- /// we want to resolve "bar::". 'SS' is empty or the previously parsed
- /// nested-name part ("foo::"), 'IdLoc' is the source location of 'bar',
- /// 'CCLoc' is the location of '::' and 'II' is the identifier for 'bar'.
- /// Returns a CXXScopeTy* object representing the C++ scope.
+ /// \brief Parsed an identifier followed by '::' in a C++
+ /// nested-name-specifier.
+ ///
+ /// \param S the scope in which the nested-name-specifier was parsed.
+ ///
+ /// \param SS the nested-name-specifier that precedes the identifier. For
+ /// example, if we are parsing "foo::bar::", \p SS will describe the "foo::"
+ /// that has already been parsed.
+ ///
+ /// \param IdLoc the location of the identifier we have just parsed (e.g.,
+ /// the "bar" in "foo::bar::".
+ ///
+ /// \param CCLoc the location of the '::' at the end of the
+ /// nested-name-specifier.
+ ///
+ /// \param II the identifier that represents the scope that this
+ /// nested-name-specifier refers to, e.g., the "bar" in "foo::bar::".
+ ///
+ /// \param ObjectType if this nested-name-specifier occurs as part of a
+ /// C++ member access expression such as "x->Base::f", the type of the base
+ /// object (e.g., *x in the example, if "x" were a pointer).
+ ///
+ /// \param EnteringContext if true, then we intend to immediately enter the
+ /// context of this nested-name-specifier, e.g., for an out-of-line
+ /// definition of a class member.
+ ///
+ /// \returns a CXXScopeTy* object representing the C++ scope.
virtual CXXScopeTy *ActOnCXXNestedNameSpecifier(Scope *S,
const CXXScopeSpec &SS,
SourceLocation IdLoc,
SourceLocation CCLoc,
IdentifierInfo &II,
+ TypeTy *ObjectType,
bool EnteringContext) {
return 0;
}
return 0;
}
- /// ActOnCXXEnterMemberScope - Called when a C++ class member accessor ('.'
- /// or '->') is parsed. After this method is called, according to
- /// [C++ 3.4.5p4], qualified-ids should be looked up in the contexts of both
- /// the entire postfix-expression and the scope of the class of the object
- /// expression.
- /// 'SS' should be an empty CXXScopeSpec to be filled with the class's scope.
- virtual OwningExprResult ActOnCXXEnterMemberScope(Scope *S,
- CXXScopeSpec &SS,
- ExprArg Base,
- tok::TokenKind OpKind) {
- return ExprEmpty();
- }
-
- /// ActOnCXXExitMemberScope - Called when a postfix-expression that previously
- /// invoked ActOnCXXEnterMemberScope() is finished. 'SS' is the same
- /// CXXScopeSpec that was passed to ActOnCXXEnterMemberScope. Used to
- /// indicate that names should revert to being looked up in the defining
- /// scope.
- virtual void ActOnCXXExitMemberScope(Scope *S, const CXXScopeSpec &SS) {
- }
-
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
/// scope or nested-name-specifier) is parsed, part of a declarator-id.
/// After this method is called, according to [C++ 3.4.3p3], names should be
return ExprEmpty();
}
+ /// \brief Invoked when the parser is starting to parse a C++ member access
+ /// expression such as x.f or x->f.
+ ///
+ /// \param S the scope in which the member access expression occurs.
+ ///
+ /// \param Base the expression in which a member is being accessed, e.g., the
+ /// "x" in "x.f".
+ ///
+ /// \param OpLoc the location of the member access operator ("." or "->")
+ ///
+ /// \param OpKind the kind of member access operator ("." or "->")
+ ///
+ /// \param ObjectType originally NULL. The action should fill in this type
+ /// with the type into which name lookup should look to find the member in
+ /// the member access expression.
+ ///
+ /// \returns the (possibly modified) \p Base expression
+ virtual OwningExprResult ActOnStartCXXMemberReference(Scope *S,
+ ExprArg Base,
+ SourceLocation OpLoc,
+ tok::TokenKind OpKind,
+ TypeTy *&ObjectType) {
+ return ExprEmpty();
+ }
+
/// ActOnDestructorReferenceExpr - Parsed a destructor reference, for example:
///
/// t->~T();
/// example, given "MetaFun::template apply", the scope specifier \p
/// SS will be "MetaFun::", \p TemplateKWLoc contains the location
/// of the "template" keyword, and "apply" is the \p Name.
+ ///
+ /// \param TemplateKWLoc the location of the "template" keyword (if any).
+ ///
+ /// \param Name the name of the template (an identifier)
+ ///
+ /// \param NameLoc the location of the identifier
+ ///
+ /// \param SS the nested-name-specifier that precedes the "template" keyword
+ /// or the template name. FIXME: If the dependent template name occurs in
+ /// a member access expression, e.g., "x.template f<T>", this
+ /// nested-name-specifier will be empty.
+ ///
+ /// \param ObjectType if this dependent template name occurs in the
+ /// context of a member access expression, the type of the object being
+ /// accessed.
virtual TemplateTy ActOnDependentTemplateName(SourceLocation TemplateKWLoc,
const IdentifierInfo &Name,
SourceLocation NameLoc,
- const CXXScopeSpec &SS) {
+ const CXXScopeSpec &SS,
+ TypeTy *ObjectType) {
return TemplateTy();
}
virtual bool isCurrentClassName(const IdentifierInfo& II, Scope *S,
const CXXScopeSpec *SS);
- /// \brief Determine whether the given identifier refers to the name of a
- /// template.
- ///
- /// \param II the identifier that we are querying to determine whether it
- /// is a template.
- ///
- /// \param S the scope in which name lookup occurs
- ///
- /// \param SS the C++ scope specifier that precedes the template name, if
- /// any.
- ///
- /// \param EnteringContext whether we are potentially entering the context
- /// referred to by the scope specifier \p SS
- ///
- /// \param Template if the name does refer to a template, the declaration
- /// of the template that the name refers to.
- ///
- /// \returns the kind of template that this name refers to.
- virtual TemplateNameKind isTemplateName(const IdentifierInfo &II, Scope *S,
+ virtual TemplateNameKind isTemplateName(Scope *S,
+ const IdentifierInfo &II,
+ SourceLocation IdLoc,
const CXXScopeSpec *SS,
+ TypeTy *ObjectType,
bool EnteringContext,
TemplateTy &Template);
// C++ Expressions
OwningExprResult ParseCXXIdExpression(bool isAddressOfOperand = false);
- /// ParseOptionalCXXScopeSpecifier - Parse global scope or
- /// nested-name-specifier if present. Returns true if a nested-name-specifier
- /// was parsed from the token stream. Note that this routine will not parse
- /// ::new or ::delete, it will just leave them in the token stream.
- ///
bool ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
- bool EnteringContext = false);
+ TypeTy *ObjectType,
+ bool EnteringContext);
//===--------------------------------------------------------------------===//
// C++ 5.2p1: C++ Casts
}
TemplateNameKind
-MinimalAction::isTemplateName(const IdentifierInfo &II, Scope *S,
+MinimalAction::isTemplateName(Scope *S,
+ const IdentifierInfo &II,
+ SourceLocation IdLoc,
const CXXScopeSpec *SS,
+ TypeTy *ObjectType,
bool EnteringScope,
TemplateTy &TemplateDecl) {
return TNK_Non_template;
->Kind == TNK_Type_template) {
// We have a qualified template-id, e.g., N::A<int>
CXXScopeSpec SS;
- ParseOptionalCXXScopeSpecifier(SS, true);
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, true);
assert(Tok.is(tok::annot_template_id) &&
"ParseOptionalCXXScopeSpecifier not working");
AnnotateTemplateIdTokenAsType(&SS);
Attr = ParseAttributes();
CXXScopeSpec SS;
- if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS)) {
+ if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS, 0, false)) {
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected_ident);
if (Tok.isNot(tok::l_brace)) {
(Tok.is(tok::coloncolon) || Tok.is(tok::identifier) ||
Tok.is(tok::annot_cxxscope))) {
CXXScopeSpec SS;
- if (ParseOptionalCXXScopeSpecifier(SS, true)) {
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, true)) {
if(Tok.isNot(tok::star)) {
// The scope spec really belongs to the direct-declarator.
D.getCXXScopeSpec() = SS;
if (D.mayHaveIdentifier()) {
// ParseDeclaratorInternal might already have parsed the scope.
bool afterCXXScope = D.getCXXScopeSpec().isSet() ||
- ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), true);
+ ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), /*ObjectType=*/0,
+ true);
if (afterCXXScope) {
// Change the declaration context for name lookup, until this function
// is exited (and the declarator has been parsed).
CXXScopeSpec SS;
// Parse (optional) nested-name-specifier.
- ParseOptionalCXXScopeSpecifier(SS);
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
if (SS.isInvalid() || Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected_namespace_name);
CXXScopeSpec SS;
// Parse (optional) nested-name-specifier.
- ParseOptionalCXXScopeSpecifier(SS);
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
AttributeList *AttrList = 0;
IdentifierInfo *NamespcName = 0;
IsTypeName = false;
// Parse nested-name-specifier.
- ParseOptionalCXXScopeSpecifier(SS);
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
AttributeList *AttrList = 0;
// Parse the (optional) nested-name-specifier.
CXXScopeSpec SS;
- if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS, true))
+ if (getLang().CPlusPlus &&
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, true))
if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id))
Diag(Tok, diag::err_expected_ident);
// Parse optional '::' and optional nested-name-specifier.
CXXScopeSpec SS;
- ParseOptionalCXXScopeSpecifier(SS, true);
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, true);
// The location of the base class itself.
SourceLocation BaseLoc = Tok.getLocation();
Parser::MemInitResult Parser::ParseMemInitializer(DeclPtrTy ConstructorDecl) {
// parse '::'[opt] nested-name-specifier[opt]
CXXScopeSpec SS;
- ParseOptionalCXXScopeSpecifier(SS);
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
TypeTy *TemplateTypeTy = 0;
if (Tok.is(tok::annot_template_id)) {
TemplateIdAnnotation *TemplateId
tok::TokenKind OpKind = Tok.getKind();
SourceLocation OpLoc = ConsumeToken(); // Eat the "." or "->" token.
- CXXScopeSpec MemberSS;
CXXScopeSpec SS;
+ Action::TypeTy *ObjectType = 0;
if (getLang().CPlusPlus && !LHS.isInvalid()) {
- LHS = Actions.ActOnCXXEnterMemberScope(CurScope, MemberSS, move(LHS),
- OpKind);
+ LHS = Actions.ActOnStartCXXMemberReference(CurScope, move(LHS),
+ OpLoc, OpKind, ObjectType);
if (LHS.isInvalid())
break;
- ParseOptionalCXXScopeSpecifier(SS);
+ ParseOptionalCXXScopeSpecifier(SS, ObjectType, false);
}
if (Tok.is(tok::identifier)) {
ConsumeToken();
if (!Tok.is(tok::identifier)) {
- if (getLang().CPlusPlus)
- Actions.ActOnCXXExitMemberScope(CurScope, MemberSS);
Diag(Tok, diag::err_expected_ident);
return ExprError();
}
Tok.getLocation(),
ConvType, &SS);
} else {
- if (getLang().CPlusPlus)
- Actions.ActOnCXXExitMemberScope(CurScope, MemberSS);
// Don't emit a diagnostic; ParseConversionFunctionId does it for us
return ExprError();
}
}
ConsumeToken();
} else {
- if (getLang().CPlusPlus)
- Actions.ActOnCXXExitMemberScope(CurScope, MemberSS);
Diag(Tok, diag::err_expected_ident);
return ExprError();
}
-
- if (getLang().CPlusPlus)
- Actions.ActOnCXXExitMemberScope(CurScope, MemberSS);
break;
}
case tok::plusplus: // postfix-expression: postfix-expression '++'
#include "clang/Parse/DeclSpec.h"
using namespace clang;
-/// ParseOptionalCXXScopeSpecifier - Parse global scope or
-/// nested-name-specifier if present. Returns true if a nested-name-specifier
-/// was parsed from the token stream. Note that this routine will not parse
-/// ::new or ::delete, it will just leave them in the token stream.
+/// \brief Parse global scope or nested-name-specifier if present.
+///
+/// Parses a C++ global scope specifier ('::') or nested-name-specifier (which
+/// may be preceded by '::'). Note that this routine will not parse ::new or
+/// ::delete; it will just leave them in the token stream.
///
/// '::'[opt] nested-name-specifier
/// '::'
/// type-name '::'
/// namespace-name '::'
/// nested-name-specifier identifier '::'
-/// nested-name-specifier 'template'[opt] simple-template-id '::' [TODO]
+/// nested-name-specifier 'template'[opt] simple-template-id '::'
+///
+///
+/// \param SS the scope specifier that will be set to the parsed
+/// nested-name-specifier (or empty)
+///
+/// \param ObjectType if this nested-name-specifier is being parsed following
+/// the "." or "->" of a member access expression, this parameter provides the
+/// type of the object whose members are being accessed.
///
+/// \param EnteringContext whether we will be entering into the context of
+/// the nested-name-specifier after parsing it.
+///
+/// \returns true if a scope specifier was parsed.
bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
+ Action::TypeTy *ObjectType,
bool EnteringContext) {
assert(getLang().CPlusPlus &&
"Call sites of this function should be guarded by checking for C++");
}
while (true) {
+ if (HasScopeSpecifier) {
+ // C++ [basic.lookup.classref]p5:
+ // If the qualified-id has the form
+ // ::class-name-or-namespace-name::...
+ // the class-name-or-namespace-name is looked up in global scope as a
+ // class-name or namespace-name.
+ //
+ // To implement this, we clear out the object type as soon as we've
+ // seen a leading '::' or part of a nested-name-specifier.
+ ObjectType = 0;
+ }
+
// nested-name-specifier:
// nested-name-specifier 'template'[opt] simple-template-id '::'
// Parse the optional 'template' keyword, then make sure we have
// 'identifier <' after it.
if (Tok.is(tok::kw_template)) {
- // If we don't have a scope specifier, this isn't a
+ // If we don't have a scope specifier or an object type, this isn't a
// nested-name-specifier, since they aren't allowed to start with
// 'template'.
- if (!HasScopeSpecifier)
+ if (!HasScopeSpecifier && !ObjectType)
break;
SourceLocation TemplateKWLoc = ConsumeToken();
TemplateTy Template
= Actions.ActOnDependentTemplateName(TemplateKWLoc,
*Tok.getIdentifierInfo(),
- Tok.getLocation(), SS);
+ Tok.getLocation(), SS,
+ ObjectType);
if (!Template)
break;
if (AnnotateTemplateIdToken(Template, TNK_Dependent_template_name,
SS.setScopeRep(
Actions.ActOnCXXNestedNameSpecifier(CurScope, SS, IdLoc, CCLoc, II,
- EnteringContext));
+ ObjectType, EnteringContext));
SS.setEndLoc(CCLoc);
continue;
}
// type-name '<'
if (Next.is(tok::less)) {
TemplateTy Template;
- if (TemplateNameKind TNK = Actions.isTemplateName(II, CurScope, &SS,
+ if (TemplateNameKind TNK = Actions.isTemplateName(CurScope, II,
+ Tok.getLocation(),
+ &SS,
+ ObjectType,
EnteringContext,
Template)) {
// We have found a template name, so annotate this this token
// '::' unqualified-id
//
CXXScopeSpec SS;
- ParseOptionalCXXScopeSpecifier(SS);
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
// unqualified-id:
// identifier
// simple-template-id
SourceLocation TypenameLoc = ConsumeToken();
CXXScopeSpec SS;
- bool HadNestedNameSpecifier = ParseOptionalCXXScopeSpecifier(SS, false);
+ bool HadNestedNameSpecifier
+ = ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false);
if (!HadNestedNameSpecifier) {
Diag(Tok.getLocation(), diag::err_expected_qualified_after_typename);
return false;
CXXScopeSpec SS;
if (getLang().CPlusPlus)
- ParseOptionalCXXScopeSpecifier(SS, EnteringContext);
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, EnteringContext);
if (Tok.is(tok::identifier)) {
// Determine whether the identifier is a type name.
if (NextToken().is(tok::less)) {
TemplateTy Template;
if (TemplateNameKind TNK
- = Actions.isTemplateName(*Tok.getIdentifierInfo(),
- CurScope, &SS, EnteringContext, Template))
+ = Actions.isTemplateName(CurScope, *Tok.getIdentifierInfo(),
+ Tok.getLocation(), &SS,
+ /*ObjectType=*/0, EnteringContext,
+ Template))
if (AnnotateTemplateIdToken(Template, TNK, &SS)) {
// If an unrecoverable error occurred, we need to return true here,
// because the token stream is in a damaged state. We may not return
"Cannot be a type or scope token!");
CXXScopeSpec SS;
- if (!ParseOptionalCXXScopeSpecifier(SS, EnteringContext))
+ if (!ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, EnteringContext))
return Tok.is(tok::annot_template_id);
// Push the current token back into the token stream (or revert it if it is
TypeTy *Ty,
SourceLocation RParen);
+ virtual OwningExprResult ActOnStartCXXMemberReference(Scope *S,
+ ExprArg Base,
+ SourceLocation OpLoc,
+ tok::TokenKind OpKind,
+ TypeTy *&ObjectType);
+
virtual OwningExprResult
ActOnDestructorReferenceExpr(Scope *S, ExprArg Base,
SourceLocation OpLoc,
bool RequireCompleteDeclContext(const CXXScopeSpec &SS);
+ DeclContext *computeDeclContext(QualType T);
DeclContext *computeDeclContext(const CXXScopeSpec &SS,
bool EnteringContext = false);
bool isDependentScopeSpecifier(const CXXScopeSpec &SS);
SourceLocation IdLoc,
SourceLocation CCLoc,
IdentifierInfo &II,
+ TypeTy *ObjectType,
bool EnteringContext);
/// ActOnCXXNestedNameSpecifier - Called during parsing of a
SourceRange TypeRange,
SourceLocation CCLoc);
- /// ActOnCXXEnterMemberScope - Called when a C++ class member accessor ('.'
- /// or '->') is parsed. After this method is called, according to
- /// [C++ 3.4.5p4], qualified-ids should be looked up in the contexts of both
- /// the entire postfix-expression and the scope of the class of the object
- /// expression.
- /// 'SS' should be an empty CXXScopeSpec to be filled with the class's scope.
- virtual OwningExprResult ActOnCXXEnterMemberScope(Scope *S, CXXScopeSpec &SS,
- ExprArg Base,
- tok::TokenKind OpKind);
-
- /// ActOnCXXExitMemberScope - Called when a postfix-expression that previously
- /// invoked ActOnCXXEnterMemberScope() is finished. 'SS' is the same
- /// CXXScopeSpec that was passed to ActOnCXXEnterMemberScope. Used to
- /// indicate that names should revert to being looked up in the defining
- /// scope.
- virtual void ActOnCXXExitMemberScope(Scope *S, const CXXScopeSpec &SS);
-
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
/// scope or nested-name-specifier) is parsed, part of a declarator-id.
/// After this method is called, according to [C++ 3.4.3p3], names should be
//===--------------------------------------------------------------------===//
// C++ Templates [C++ 14]
//
- virtual TemplateNameKind isTemplateName(const IdentifierInfo &II, Scope *S,
+ virtual TemplateNameKind isTemplateName(Scope *S,
+ const IdentifierInfo &II,
+ SourceLocation IdLoc,
const CXXScopeSpec *SS,
+ TypeTy *ObjectType,
bool EnteringContext,
TemplateTy &Template);
bool DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl);
virtual TemplateTy ActOnDependentTemplateName(SourceLocation TemplateKWLoc,
const IdentifierInfo &Name,
SourceLocation NameLoc,
- const CXXScopeSpec &SS);
+ const CXXScopeSpec &SS,
+ TypeTy *ObjectType);
bool CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate,
ClassTemplateSpecializationDecl *PrevDecl,
#include "llvm/Support/raw_ostream.h"
using namespace clang;
+/// \brief Compute the DeclContext that is associated with the given type.
+///
+/// \param T the type for which we are attempting to find a DeclContext.
+///
+/// \returns the declaration context represented by the type T,
+/// or NULL if the declaration context cannot be computed (e.g., because it is
+/// dependent and not the current instantiation).
+DeclContext *Sema::computeDeclContext(QualType T) {
+ if (const TagType *Tag = T->getAs<TagType>())
+ return Tag->getDecl();
+
+ return 0;
+}
+
/// \brief Compute the DeclContext that is associated with the given
/// scope specifier.
///
return NestedNameSpecifier::GlobalSpecifier(Context);
}
+/// \brief Determines whether the given declaration is an valid acceptable
+/// result for name lookup of a nested-name-specifier.
+bool isAcceptableNestedNameSpecifier(ASTContext &Context, NamedDecl *SD) {
+ if (!SD)
+ return false;
+
+ // Namespace and namespace aliases are fine.
+ if (isa<NamespaceDecl>(SD) || isa<NamespaceAliasDecl>(SD))
+ return true;
+
+ if (!isa<TypeDecl>(SD))
+ return false;
+
+ // Determine whether we have a class (or, in C++0x, an enum) or
+ // a typedef thereof. If so, build the nested-name-specifier.
+ QualType T = Context.getTypeDeclType(cast<TypeDecl>(SD));
+ if (T->isDependentType())
+ return true;
+ else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(SD)) {
+ if (TD->getUnderlyingType()->isRecordType() ||
+ (Context.getLangOptions().CPlusPlus0x &&
+ TD->getUnderlyingType()->isEnumeralType()))
+ return true;
+ } else if (isa<RecordDecl>(SD) ||
+ (Context.getLangOptions().CPlusPlus0x && isa<EnumDecl>(SD)))
+ return true;
+
+ return false;
+}
+
/// ActOnCXXNestedNameSpecifier - Called during parsing of a
/// nested-name-specifier. e.g. for "foo::bar::" we parsed "foo::" and now
/// we want to resolve "bar::". 'SS' is empty or the previously parsed
SourceLocation IdLoc,
SourceLocation CCLoc,
IdentifierInfo &II,
+ TypeTy *ObjectTypePtr,
bool EnteringContext) {
NestedNameSpecifier *Prefix
= static_cast<NestedNameSpecifier *>(SS.getScopeRep());
+
+ // Determine where to perform name lookup
+ DeclContext *LookupCtx = 0;
+ bool isDependent = false;
+ if (ObjectTypePtr) {
+ // This nested-name-specifier occurs in a member access expression, e.g.,
+ // x->B::f, and we are looking into the type of the object.
+ assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist");
+ QualType ObjectType = QualType::getFromOpaquePtr(ObjectTypePtr);
+ LookupCtx = computeDeclContext(ObjectType);
+ isDependent = ObjectType->isDependentType();
+ } else if (SS.isSet()) {
+ // This nested-name-specifier occurs after another nested-name-specifier,
+ // so long into the context associated with the prior nested-name-specifier.
+ LookupCtx = computeDeclContext(SS, EnteringContext);
+ isDependent = isDependentScopeSpecifier(SS);
+ }
- NamedDecl *SD = LookupParsedName(S, &SS, &II, LookupNestedNameSpecifierName,
- false, false, SourceLocation(),
- EnteringContext);
+ LookupResult Found;
+ bool ObjectTypeSearchedInScope = false;
+ if (LookupCtx) {
+ // Perform "qualified" name lookup into the declaration context we
+ // computed, which is either the type of the base of a member access
+ // expression or the declaration context associated with a prior
+ // nested-name-specifier.
+
+ // The declaration context must be complete.
+ if (!LookupCtx->isDependentContext() && RequireCompleteDeclContext(SS))
+ return 0;
- if (SD) {
+ Found = LookupQualifiedName(LookupCtx, &II, LookupNestedNameSpecifierName,
+ false);
+
+ if (ObjectTypePtr && Found.getKind() == LookupResult::NotFound && S) {
+ // C++ [basic.lookup.classref]p4:
+ // If the id-expression in a class member access is a qualified-id of
+ // the form
+ //
+ // class-name-or-namespace-name::...
+ //
+ // the class-name-or-namespace-name following the . or -> operator is
+ // looked up both in the context of the entire postfix-expression and in
+ // the scope of the class of the object expression. If the name is found
+ // only in the scope of the class of the object expression, the name
+ // shall refer to a class-name. If the name is found only in the
+ // context of the entire postfix-expression, the name shall refer to a
+ // class-name or namespace-name. [...]
+ //
+ // Qualified name lookup into a class will not find a namespace-name,
+ // so we do not need to diagnoste that case specifically. However,
+ // this qualified name lookup may find nothing. In that case, perform
+ // unqualified name lookup in the given scope.
+
+ // FIXME: When we're instantiating a template, do we actually have to
+ // look in the scope of the template? Seems fishy...
+ Found = LookupName(S, &II, LookupNestedNameSpecifierName);
+ ObjectTypeSearchedInScope = true;
+ }
+ } else if (isDependent) {
+ // We were not able to compute the declaration context for a dependent
+ // base object type or prior nested-name-specifier, so this
+ // nested-name-specifier refers to an unknown specialization. Just build
+ // a dependent nested-name-specifier.
+ return NestedNameSpecifier::Create(Context, Prefix, &II);
+ } else {
+ // Perform unqualified name lookup in the current scope.
+ Found = LookupName(S, &II, LookupNestedNameSpecifierName);
+ }
+
+ // FIXME: Deal with ambiguities cleanly.
+ NamedDecl *SD = Found;
+ if (isAcceptableNestedNameSpecifier(Context, SD)) {
+ if (ObjectTypePtr && !ObjectTypeSearchedInScope && S) {
+ // C++ [basic.lookup.classref]p4:
+ // [...] If the name is found in both contexts, the
+ // class-name-or-namespace-name shall refer to the same entity.
+ //
+ // We already found the name in the scope of the object. Now, look
+ // into the current scope (the scope of the postfix-expression) to
+ // see if we can find the same name there.
+ LookupResult FoundOuter
+ = LookupName(S, &II, LookupNestedNameSpecifierName);
+
+ // FIXME: Handle ambiguities in FoundOuter!
+ NamedDecl *OuterDecl = FoundOuter;
+ if (isAcceptableNestedNameSpecifier(Context, OuterDecl) &&
+ OuterDecl->getCanonicalDecl() != SD->getCanonicalDecl() &&
+ (!isa<TypeDecl>(OuterDecl) || !isa<TypeDecl>(SD) ||
+ !Context.hasSameType(
+ Context.getTypeDeclType(cast<TypeDecl>(OuterDecl)),
+ Context.getTypeDeclType(cast<TypeDecl>(SD))))) {
+ Diag(IdLoc, diag::err_nested_name_member_ref_lookup_ambiguous)
+ << &II;
+ Diag(SD->getLocation(), diag::note_ambig_member_ref_object_type)
+ << QualType::getFromOpaquePtr(ObjectTypePtr);
+ Diag(OuterDecl->getLocation(), diag::note_ambig_member_ref_scope);
+
+ // Fall through so that we'll pick the name we found in the object type,
+ // since that's probably what the user wanted anyway.
+ }
+ }
+
if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(SD))
return NestedNameSpecifier::Create(Context, Prefix, Namespace);
- if (TypeDecl *Type = dyn_cast<TypeDecl>(SD)) {
- // Determine whether we have a class (or, in C++0x, an enum) or
- // a typedef thereof. If so, build the nested-name-specifier.
- QualType T = Context.getTypeDeclType(Type);
- bool AcceptableType = false;
- if (T->isDependentType())
- AcceptableType = true;
- else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(SD)) {
- if (TD->getUnderlyingType()->isRecordType() ||
- (getLangOptions().CPlusPlus0x &&
- TD->getUnderlyingType()->isEnumeralType()))
- AcceptableType = true;
- } else if (isa<RecordDecl>(Type) ||
- (getLangOptions().CPlusPlus0x && isa<EnumDecl>(Type)))
- AcceptableType = true;
-
- if (AcceptableType)
- return NestedNameSpecifier::Create(Context, Prefix, false,
- T.getTypePtr());
- }
-
// FIXME: It would be nice to maintain the namespace alias name, then
// see through that alias when resolving the nested-name-specifier down to
// a declaration context.
if (NamespaceAliasDecl *Alias = dyn_cast<NamespaceAliasDecl>(SD))
return NestedNameSpecifier::Create(Context, Prefix,
- Alias->getNamespace());
- // Fall through to produce an error: we found something that isn't
- // a class or a namespace.
- } else if (SS.isSet() && isDependentScopeSpecifier(SS))
- return NestedNameSpecifier::Create(Context, Prefix, &II);
+ Alias->getNamespace());
+
+ QualType T = Context.getTypeDeclType(cast<TypeDecl>(SD));
+ return NestedNameSpecifier::Create(Context, Prefix, false,
+ T.getTypePtr());
+ }
// If we didn't find anything during our lookup, try again with
// ordinary name lookup, which can help us produce better error
// messages.
if (!SD)
- SD = LookupParsedName(S, &SS, &II, LookupOrdinaryName,
- false, false, SourceLocation(),
- EnteringContext);
+ SD = LookupName(S, &II, LookupOrdinaryName);
+
unsigned DiagID;
if (SD)
DiagID = diag::err_expected_class_or_namespace;
T.getTypePtr());
}
-Action::OwningExprResult
-Sema::ActOnCXXEnterMemberScope(Scope *S, CXXScopeSpec &SS, ExprArg Base,
- tok::TokenKind OpKind) {
- // Since this might be a postfix expression, get rid of ParenListExprs.
- Base = MaybeConvertParenListExprToParenExpr(S, move(Base));
-
- Expr *BaseExpr = (Expr*)Base.get();
- assert(BaseExpr && "no record expansion");
-
- QualType BaseType = BaseExpr->getType();
- // FIXME: handle dependent types
- if (BaseType->isDependentType())
- return move(Base);
-
- // C++ [over.match.oper]p8:
- // [...] When operator->returns, the operator-> is applied to the value
- // returned, with the original second operand.
- if (OpKind == tok::arrow) {
- while (BaseType->isRecordType()) {
- Base = BuildOverloadedArrowExpr(S, move(Base), BaseExpr->getExprLoc());
- BaseExpr = (Expr*)Base.get();
- if (BaseExpr == NULL)
- return ExprError();
- BaseType = BaseExpr->getType();
- }
- }
-
- if (BaseType->isPointerType())
- BaseType = BaseType->getPointeeType();
-
- // We could end up with various non-record types here, such as extended
- // vector types or Objective-C interfaces. Just return early and let
- // ActOnMemberReferenceExpr do the work.
- if (!BaseType->isRecordType())
- return move(Base);
-
- SS.setRange(BaseExpr->getSourceRange());
- SS.setScopeRep(
- NestedNameSpecifier::Create(Context, 0, false, BaseType.getTypePtr())
- );
-
- if (S)
- ActOnCXXEnterDeclaratorScope(S,SS);
- return move(Base);
-}
-
-void Sema::ActOnCXXExitMemberScope(Scope *S, const CXXScopeSpec &SS) {
- if (S && SS.isSet())
- ActOnCXXExitDeclaratorScope(S,SS);
-}
-
-
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
/// scope or nested-name-specifier) is parsed, part of a declarator-id.
/// After this method is called, according to [C++ 3.4.3p3], names should be
return E;
}
+Sema::OwningExprResult
+Sema::ActOnStartCXXMemberReference(Scope *S, ExprArg Base, SourceLocation OpLoc,
+ tok::TokenKind OpKind, TypeTy *&ObjectType) {
+ // Since this might be a postfix expression, get rid of ParenListExprs.
+ Base = MaybeConvertParenListExprToParenExpr(S, move(Base));
+
+ Expr *BaseExpr = (Expr*)Base.get();
+ assert(BaseExpr && "no record expansion");
+
+ QualType BaseType = BaseExpr->getType();
+ if (BaseType->isDependentType()) {
+ // FIXME: member of the current instantiation
+ ObjectType = BaseType.getAsOpaquePtr();
+ return move(Base);
+ }
+
+ // C++ [over.match.oper]p8:
+ // [...] When operator->returns, the operator-> is applied to the value
+ // returned, with the original second operand.
+ if (OpKind == tok::arrow) {
+ while (BaseType->isRecordType()) {
+ Base = BuildOverloadedArrowExpr(S, move(Base), BaseExpr->getExprLoc());
+ BaseExpr = (Expr*)Base.get();
+ if (BaseExpr == NULL)
+ return ExprError();
+ BaseType = BaseExpr->getType();
+ }
+ }
+
+ if (BaseType->isPointerType())
+ BaseType = BaseType->getPointeeType();
+
+ // We could end up with various non-record types here, such as extended
+ // vector types or Objective-C interfaces. Just return early and let
+ // ActOnMemberReferenceExpr do the work.
+ if (!BaseType->isRecordType())
+ return move(Base);
+
+ ObjectType = BaseType.getAsOpaquePtr();
+ return move(Base);
+}
+
Sema::OwningExprResult
Sema::ActOnDestructorReferenceExpr(Scope *S, ExprArg Base,
SourceLocation OpLoc,
LookupNameKind NameKind, bool RedeclarationOnly) {
assert(LookupCtx && "Sema::LookupQualifiedName requires a lookup context");
- if (!Name) return LookupResult::CreateLookupResult(Context, 0);
-
+ if (!Name)
+ return LookupResult::CreateLookupResult(Context, 0);
+
// If we're performing qualified name lookup (e.g., lookup into a
// struct), find fields as part of ordinary name lookup.
unsigned IDNS
getLangOptions().CPlusPlus);
if (NameKind == LookupOrdinaryName)
IDNS |= Decl::IDNS_Member;
-
+
+ // Make sure that the declaration context is complete.
+ assert((!isa<TagDecl>(LookupCtx) ||
+ LookupCtx->isDependentContext() ||
+ cast<TagDecl>(LookupCtx)->isDefinition() ||
+ Context.getTypeDeclType(cast<TagDecl>(LookupCtx))->getAs<TagType>()
+ ->isBeingDefined()) &&
+ "Declaration context must already be complete!");
+
// Perform qualified name lookup into the LookupCtx.
DeclContext::lookup_iterator I, E;
for (llvm::tie(I, E) = LookupCtx->lookup(Name); I != E; ++I)
if (isAcceptableLookupResult(*I, NameKind, IDNS))
return LookupResult::CreateLookupResult(Context, I, E);
- // If this isn't a C++ class or we aren't allowed to look into base
- // classes, we're done.
- if (RedeclarationOnly || !isa<CXXRecordDecl>(LookupCtx))
+ // If this isn't a C++ class, we aren't allowed to look into base
+ // classes, we're done, or the lookup context is dependent, we're done.
+ if (RedeclarationOnly || !isa<CXXRecordDecl>(LookupCtx) ||
+ LookupCtx->isDependentContext())
return LookupResult::CreateLookupResult(Context, 0);
// Perform lookup into our base classes.
if (DeclContext *DC = computeDeclContext(*SS, EnteringContext)) {
// We have resolved the scope specifier to a particular declaration
// contex, and will perform name lookup in that context.
-
- if (DC->isDependentContext()) {
- // If this is a dependent context, then we are looking for a member of
- // the current instantiation. This is a narrow search that looks into
- // just the described declaration context (C++0x [temp.dep.type]).
- unsigned IDNS = getIdentifierNamespacesFromLookupNameKind(NameKind,
- true);
- DeclContext::lookup_iterator I, E;
- for (llvm::tie(I, E) = DC->lookup(Name); I != E; ++I)
- if (isAcceptableLookupResult(*I, NameKind, IDNS))
- return LookupResult::CreateLookupResult(Context, I, E);
- }
-
- // Qualified name lookup into the named declaration context.
- // The declaration context must be complete.
- if (RequireCompleteDeclContext(*SS))
+ if (!DC->isDependentContext() && RequireCompleteDeclContext(*SS))
return LookupResult::CreateLookupResult(Context, 0);
-
+
return LookupQualifiedName(DC, Name, NameKind, RedeclarationOnly);
}
using namespace clang;
-/// isTemplateName - Determines whether the identifier II is a
-/// template name in the current scope, and returns the template
-/// declaration if II names a template. An optional CXXScope can be
-/// passed to indicate the C++ scope in which the identifier will be
-/// found.
-TemplateNameKind Sema::isTemplateName(const IdentifierInfo &II, Scope *S,
- const CXXScopeSpec *SS,
- bool EnteringContext,
- TemplateTy &TemplateResult) {
- LookupResult Found = LookupParsedName(S, SS, &II, LookupOrdinaryName,
- false, false, SourceLocation(),
- EnteringContext);
+/// \brief Determine whether the declaration found is acceptable as the name
+/// of a template and, if so, return that template declaration. Otherwise,
+/// returns NULL.
+static NamedDecl *isAcceptableTemplateName(ASTContext &Context, NamedDecl *D) {
+ if (!D)
+ return 0;
- // FIXME: Cope with ambiguous name-lookup results.
- assert(!Found.isAmbiguous() &&
- "Cannot handle template name-lookup ambiguities");
+ if (isa<TemplateDecl>(D))
+ return D;
+
+ if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(D)) {
+ // C++ [temp.local]p1:
+ // Like normal (non-template) classes, class templates have an
+ // injected-class-name (Clause 9). The injected-class-name
+ // can be used with or without a template-argument-list. When
+ // it is used without a template-argument-list, it is
+ // equivalent to the injected-class-name followed by the
+ // template-parameters of the class template enclosed in
+ // <>. When it is used with a template-argument-list, it
+ // refers to the specified class template specialization,
+ // which could be the current specialization or another
+ // specialization.
+ if (Record->isInjectedClassName()) {
+ Record = cast<CXXRecordDecl>(Record->getCanonicalDecl());
+ if (Record->getDescribedClassTemplate())
+ return Record->getDescribedClassTemplate();
+
+ if (ClassTemplateSpecializationDecl *Spec
+ = dyn_cast<ClassTemplateSpecializationDecl>(Record))
+ return Spec->getSpecializedTemplate();
+ }
+
+ return 0;
+ }
- NamedDecl *IIDecl = Found;
+ OverloadedFunctionDecl *Ovl = dyn_cast<OverloadedFunctionDecl>(D);
+ if (!Ovl)
+ return 0;
- TemplateNameKind TNK = TNK_Non_template;
- TemplateDecl *Template = 0;
-
- if (IIDecl) {
- if ((Template = dyn_cast<TemplateDecl>(IIDecl))) {
- if (isa<FunctionTemplateDecl>(IIDecl))
- TNK = TNK_Function_template;
- else if (isa<ClassTemplateDecl>(IIDecl) ||
- isa<TemplateTemplateParmDecl>(IIDecl))
- TNK = TNK_Type_template;
- else
- assert(false && "Unknown template declaration kind");
- } else if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(IIDecl)) {
- // C++ [temp.local]p1:
- // Like normal (non-template) classes, class templates have an
- // injected-class-name (Clause 9). The injected-class-name
- // can be used with or without a template-argument-list. When
- // it is used without a template-argument-list, it is
- // equivalent to the injected-class-name followed by the
- // template-parameters of the class template enclosed in
- // <>. When it is used with a template-argument-list, it
- // refers to the specified class template specialization,
- // which could be the current specialization or another
- // specialization.
- if (Record->isInjectedClassName()) {
- Record = cast<CXXRecordDecl>(Record->getCanonicalDecl());
- if ((Template = Record->getDescribedClassTemplate()))
- TNK = TNK_Type_template;
- else if (ClassTemplateSpecializationDecl *Spec
- = dyn_cast<ClassTemplateSpecializationDecl>(Record)) {
- Template = Spec->getSpecializedTemplate();
- TNK = TNK_Type_template;
- }
+ for (OverloadedFunctionDecl::function_iterator F = Ovl->function_begin(),
+ FEnd = Ovl->function_end();
+ F != FEnd; ++F) {
+ if (FunctionTemplateDecl *FuncTmpl = dyn_cast<FunctionTemplateDecl>(*F)) {
+ // We've found a function template. Determine whether there are
+ // any other function templates we need to bundle together in an
+ // OverloadedFunctionDecl
+ for (++F; F != FEnd; ++F) {
+ if (isa<FunctionTemplateDecl>(*F))
+ break;
}
- } else if (OverloadedFunctionDecl *Ovl
- = dyn_cast<OverloadedFunctionDecl>(IIDecl)) {
- for (OverloadedFunctionDecl::function_iterator F = Ovl->function_begin(),
- FEnd = Ovl->function_end();
- F != FEnd; ++F) {
- if (FunctionTemplateDecl *FuncTmpl
- = dyn_cast<FunctionTemplateDecl>(*F)) {
- // We've found a function template. Determine whether there are
- // any other function templates we need to bundle together in an
- // OverloadedFunctionDecl
- for (++F; F != FEnd; ++F) {
- if (isa<FunctionTemplateDecl>(*F))
- break;
- }
-
- if (F != FEnd) {
- // Build an overloaded function decl containing only the
- // function templates in Ovl.
- OverloadedFunctionDecl *OvlTemplate
- = OverloadedFunctionDecl::Create(Context,
- Ovl->getDeclContext(),
- Ovl->getDeclName());
- OvlTemplate->addOverload(FuncTmpl);
+
+ if (F != FEnd) {
+ // Build an overloaded function decl containing only the
+ // function templates in Ovl.
+ OverloadedFunctionDecl *OvlTemplate
+ = OverloadedFunctionDecl::Create(Context,
+ Ovl->getDeclContext(),
+ Ovl->getDeclName());
+ OvlTemplate->addOverload(FuncTmpl);
+ OvlTemplate->addOverload(*F);
+ for (++F; F != FEnd; ++F) {
+ if (isa<FunctionTemplateDecl>(*F))
OvlTemplate->addOverload(*F);
- for (++F; F != FEnd; ++F) {
- if (isa<FunctionTemplateDecl>(*F))
- OvlTemplate->addOverload(*F);
- }
-
- // Form the resulting TemplateName
- if (SS && SS->isSet() && !SS->isInvalid()) {
- NestedNameSpecifier *Qualifier
- = static_cast<NestedNameSpecifier *>(SS->getScopeRep());
- TemplateResult
- = TemplateTy::make(Context.getQualifiedTemplateName(Qualifier,
- false,
- OvlTemplate));
- } else {
- TemplateResult = TemplateTy::make(TemplateName(OvlTemplate));
- }
- return TNK_Function_template;
- }
-
- TNK = TNK_Function_template;
- Template = FuncTmpl;
- break;
}
+
+ return OvlTemplate;
}
+
+ return FuncTmpl;
}
+ }
+
+ return 0;
+}
- if (TNK != TNK_Non_template) {
- if (SS && SS->isSet() && !SS->isInvalid()) {
- NestedNameSpecifier *Qualifier
- = static_cast<NestedNameSpecifier *>(SS->getScopeRep());
- TemplateResult
- = TemplateTy::make(Context.getQualifiedTemplateName(Qualifier,
- false,
- Template));
- } else
- TemplateResult = TemplateTy::make(TemplateName(Template));
+TemplateNameKind Sema::isTemplateName(Scope *S,
+ const IdentifierInfo &II,
+ SourceLocation IdLoc,
+ const CXXScopeSpec *SS,
+ TypeTy *ObjectTypePtr,
+ bool EnteringContext,
+ TemplateTy &TemplateResult) {
+ // Determine where to perform name lookup
+ DeclContext *LookupCtx = 0;
+ bool isDependent = false;
+ if (ObjectTypePtr) {
+ // This nested-name-specifier occurs in a member access expression, e.g.,
+ // x->B::f, and we are looking into the type of the object.
+ assert((!SS || !SS->isSet()) &&
+ "ObjectType and scope specifier cannot coexist");
+ QualType ObjectType = QualType::getFromOpaquePtr(ObjectTypePtr);
+ LookupCtx = computeDeclContext(ObjectType);
+ isDependent = ObjectType->isDependentType();
+ } else if (SS && SS->isSet()) {
+ // This nested-name-specifier occurs after another nested-name-specifier,
+ // so long into the context associated with the prior nested-name-specifier.
+
+ LookupCtx = computeDeclContext(*SS, EnteringContext);
+ isDependent = isDependentScopeSpecifier(*SS);
+ }
+
+ LookupResult Found;
+ bool ObjectTypeSearchedInScope = false;
+ if (LookupCtx) {
+ // Perform "qualified" name lookup into the declaration context we
+ // computed, which is either the type of the base of a member access
+ // expression or the declaration context associated with a prior
+ // nested-name-specifier.
+
+ // The declaration context must be complete.
+ if (!LookupCtx->isDependentContext() && RequireCompleteDeclContext(*SS))
+ return TNK_Non_template;
+
+ Found = LookupQualifiedName(LookupCtx, &II, LookupOrdinaryName);
+
+ if (ObjectTypePtr && Found.getKind() == LookupResult::NotFound) {
+ // C++ [basic.lookup.classref]p1:
+ // In a class member access expression (5.2.5), if the . or -> token is
+ // immediately followed by an identifier followed by a <, the
+ // identifier must be looked up to determine whether the < is the
+ // beginning of a template argument list (14.2) or a less-than operator.
+ // The identifier is first looked up in the class of the object
+ // expression. If the identifier is not found, it is then looked up in
+ // the context of the entire postfix-expression and shall name a class
+ // or function template.
+ //
+ // FIXME: When we're instantiating a template, do we actually have to
+ // look in the scope of the template? Seems fishy...
+ Found = LookupName(S, &II, LookupOrdinaryName);
+ ObjectTypeSearchedInScope = true;
}
+ } else if (isDependent) {
+ // We cannot look into a dependent object type or
+ return TNK_Non_template;
+ } else {
+ // Perform unqualified name lookup in the current scope.
+ Found = LookupName(S, &II, LookupOrdinaryName);
+ }
+
+ // FIXME: Cope with ambiguous name-lookup results.
+ assert(!Found.isAmbiguous() &&
+ "Cannot handle template name-lookup ambiguities");
+
+ NamedDecl *Template = isAcceptableTemplateName(Context, Found);
+ if (!Template)
+ return TNK_Non_template;
+
+ if (ObjectTypePtr && !ObjectTypeSearchedInScope) {
+ // C++ [basic.lookup.classref]p1:
+ // [...] If the lookup in the class of the object expression finds a
+ // template, the name is also looked up in the context of the entire
+ // postfix-expression and [...]
+ //
+ LookupResult FoundOuter = LookupName(S, &II, LookupOrdinaryName);
+ // FIXME: Handle ambiguities in this lookup better
+ NamedDecl *OuterTemplate = isAcceptableTemplateName(Context, FoundOuter);
+
+ if (!OuterTemplate) {
+ // - if the name is not found, the name found in the class of the
+ // object expression is used, otherwise
+ } else if (!isa<ClassTemplateDecl>(OuterTemplate)) {
+ // - if the name is found in the context of the entire
+ // postfix-expression and does not name a class template, the name
+ // found in the class of the object expression is used, otherwise
+ } else {
+ // - if the name found is a class template, it must refer to the same
+ // entity as the one found in the class of the object expression,
+ // otherwise the program is ill-formed.
+ if (OuterTemplate->getCanonicalDecl() != Template->getCanonicalDecl()) {
+ Diag(IdLoc, diag::err_nested_name_member_ref_lookup_ambiguous)
+ << &II;
+ Diag(Template->getLocation(), diag::note_ambig_member_ref_object_type)
+ << QualType::getFromOpaquePtr(ObjectTypePtr);
+ Diag(OuterTemplate->getLocation(), diag::note_ambig_member_ref_scope);
+
+ // Recover by taking the template that we found in the object
+ // expression's type.
+ }
+ }
}
- return TNK;
+
+ if (SS && SS->isSet() && !SS->isInvalid()) {
+ NestedNameSpecifier *Qualifier
+ = static_cast<NestedNameSpecifier *>(SS->getScopeRep());
+ if (OverloadedFunctionDecl *Ovl
+ = dyn_cast<OverloadedFunctionDecl>(Template))
+ TemplateResult
+ = TemplateTy::make(Context.getQualifiedTemplateName(Qualifier, false,
+ Ovl));
+ else
+ TemplateResult
+ = TemplateTy::make(Context.getQualifiedTemplateName(Qualifier, false,
+ cast<TemplateDecl>(Template)));
+ } else if (OverloadedFunctionDecl *Ovl
+ = dyn_cast<OverloadedFunctionDecl>(Template)) {
+ TemplateResult = TemplateTy::make(TemplateName(Ovl));
+ } else {
+ TemplateResult = TemplateTy::make(
+ TemplateName(cast<TemplateDecl>(Template)));
+ }
+
+ if (isa<ClassTemplateDecl>(Template) ||
+ isa<TemplateTemplateParmDecl>(Template))
+ return TNK_Type_template;
+
+ assert((isa<FunctionTemplateDecl>(Template) ||
+ isa<OverloadedFunctionDecl>(Template)) &&
+ "Unhandled template kind in Sema::isTemplateName");
+ return TNK_Function_template;
}
/// DiagnoseTemplateParameterShadow - Produce a diagnostic complaining
Sema::ActOnDependentTemplateName(SourceLocation TemplateKWLoc,
const IdentifierInfo &Name,
SourceLocation NameLoc,
- const CXXScopeSpec &SS) {
- if (!SS.isSet() || SS.isInvalid())
- return TemplateTy();
-
- NestedNameSpecifier *Qualifier
- = static_cast<NestedNameSpecifier *>(SS.getScopeRep());
-
- if (computeDeclContext(SS, false)) {
+ const CXXScopeSpec &SS,
+ TypeTy *ObjectType) {
+ if ((ObjectType &&
+ computeDeclContext(QualType::getFromOpaquePtr(ObjectType))) ||
+ (SS.isSet() && computeDeclContext(SS, false))) {
// C++0x [temp.names]p5:
// If a name prefixed by the keyword template is not the name of
// a template, the program is ill-formed. [Note: the keyword
// "template" keyword is now permitted). We follow the C++0x
// rules, even in C++03 mode, retroactively applying the DR.
TemplateTy Template;
- TemplateNameKind TNK = isTemplateName(Name, 0, &SS, false, Template);
+ TemplateNameKind TNK = isTemplateName(0, Name, NameLoc, &SS, ObjectType,
+ false, Template);
if (TNK == TNK_Non_template) {
Diag(NameLoc, diag::err_template_kw_refers_to_non_template)
<< &Name;
return Template;
}
+ // FIXME: We need to be able to create a dependent template name with just
+ // an identifier, to handle the x->template f<T> case.
+ assert(!ObjectType &&
+ "Cannot handle dependent template names without a nested-name-specifier");
+
+ NestedNameSpecifier *Qualifier
+ = static_cast<NestedNameSpecifier *>(SS.getScopeRep());
return TemplateTy::make(Context.getDependentTemplateName(Qualifier, &Name));
}
OwningExprResult Base = move(BaseE);
tok::TokenKind OpKind = IsArrow? tok::arrow : tok::period;
CXXScopeSpec SS;
- Base = SemaRef.ActOnCXXEnterMemberScope(0, SS, move(Base), OpKind);
+ Sema::TypeTy *ObjectType = 0;
+
+ Base = SemaRef.ActOnStartCXXMemberReference(0, move(Base), OperatorLoc,
+ OpKind, ObjectType);
if (Base.isInvalid())
return SemaRef.ExprError();
MemberLoc,
Name,
/*FIXME?*/Sema::DeclPtrTy::make((Decl*)0));
- SemaRef.ActOnCXXExitMemberScope(0, SS);
return move(Base);
}
return static_cast<NestedNameSpecifier *>(
SemaRef.ActOnCXXNestedNameSpecifier(0, SS, Range.getEnd(),
Range.getEnd(), II,
+ /*FIXME:ObjectType=*/0,
false));
}
SS.setRange(SourceRange(getDerived().getBaseLocation()));
SS.setScopeRep(Qualifier);
Sema::TemplateTy Template;
- TemplateNameKind TNK = SemaRef.isTemplateName(II, 0, &SS, false, Template);
+ TemplateNameKind TNK = SemaRef.isTemplateName(0, II,
+ /*FIXME:*/getDerived().getBaseLocation(),
+ &SS,
+ /*FIXME:ObjectType=*/0, false,
+ Template);
if (TNK == TNK_Non_template) {
SemaRef.Diag(getDerived().getBaseLocation(),
diag::err_template_kw_refers_to_non_template)
}
A:: ; // expected-error {{expected unqualified-id}}
-::A::ax::undef ex3; // expected-error {{expected a class or namespace}} expected-error {{unknown type name 'undef'}}
+::A::ax::undef ex3; // expected-error {{no member named}} expected-error {{unknown type name 'undef'}}
A::undef1::undef2 ex4; // expected-error {{no member named 'undef1'}} expected-error {{unknown type name 'undef2'}}
int A::C::Ag1() { return 0; }
{
namespace B
{
- struct base
+ struct base // expected-note{{object type}}
{
void x() {}
void y() {}
i.foo(); // expected-error{{member reference base type 'int' is not a structure or union}}
}
+ void fun4a() {
+ A::sub *a;
+
+ typedef A::member base; // expected-note{{current scope}}
+ a->base::x(); // expected-error{{ambiguous}}
+ }
+
+ void fun4b() {
+ A::sub *a;
+
+ typedef A::B::base base;
+ a->base::x();
+ }
+
template<typename T>
- void fun4()
+ void fun5()
{
T a;
a.x();
a->foo();
+#if 0
+ // FIXME: We need the notion of identifiers as dependent
+ // nested-name-specifiers without a prefix for this code to work.
+
// Things that work for the wrong reason
a.A::sub::x();
a.A::B::base::x();
a.bad::x();
// Things that fail, but shouldn't
- a.sub::x(); // expected-error{{use of undeclared identifier 'sub'}}
- a.base::x(); // expected-error{{use of undeclared identifier 'base'}}
- a.B::base::x(); // expected-error{{use of undeclared identifier 'B'}}
- a->member::foo(); // expected-error{{use of undeclared identifier 'member'}}
+ a.sub::x(); // xpected-error{{use of undeclared identifier 'sub'}}
+ a.base::x(); // xpected-error{{use of undeclared identifier 'base'}}
+ a.B::base::x(); // xpected-error{{use of undeclared identifier 'B'}}
+ a->member::foo(); // xpected-error{{use of undeclared identifier 'member'}}
+#endif
}
}
+
+// PR4703
+struct a {
+ int a;
+ static int sa;
+};
+
+a a;
+
+int a::sa = a.a;
// PR4608
class A { template <class x> x a(x z) { return z+y; } int y; };
+