Skip to content

Commit

Permalink
[Clang] Implement resolution for CWG1835 (#92957)
Browse files Browse the repository at this point in the history
CWG1835 was one of the many core issues resolved by P1787R6: "Declarations and where to
find them" (http://wg21.link/p1787r6). Its resolution changes how
member-qualified names (as defined by [basic.lookup.qual.general] p2) are looked
up. This patch implementation that resolution.

Previously, an _identifier_ following `.` or `->` would be first looked
up in the type of the object expression (i.e. qualified lookup), and
then in the context of the _postfix-expression_ (i.e. unqualified
lookup) if nothing was found; the result of the second lookup was
required to name a class template. Notably, this second lookup would
occur even when the object expression was dependent, and its result
would be used to determine whether a `<` token is the start of a
_template-argument_list_.

The new wording in [basic.lookup.qual.general] p2 states:
> A member-qualified name is the (unique) component name, if any, of
> - an _unqualified-id_ or
> - a _nested-name-specifier_ of the form _`type-name ::`_ or
_`namespace-name ::`​_
>
> in the id-expression of a class member access expression. A
***qualified name*** is
> - a member-qualified name or
> - the terminal name of
>     - a _qualified-id_,
>     - a _using-declarator_,
>     - a _typename-specifier_,
>     - a _qualified-namespace-specifier_, or
> - a _nested-name-specifier_, _elaborated-type-specifier_, or
_class-or-decltype_ that has a _nested-name-specifier_.
>
> The _lookup context_ of a member-qualified name is the type of its
associated object expression (considered dependent if the object
expression is type-dependent). The lookup context of any other qualified
name is the type, template, or namespace nominated by the preceding
_nested-name-specifier_.

And [basic.lookup.qual.general] p3 now states:
> _Qualified name lookup_ in a class, namespace, or enumeration performs
a search of the scope associated with it except as specified below.
Unless otherwise specified, a qualified name undergoes qualified name
lookup in its lookup context from the point where it appears unless the
lookup context either is dependent and is not the current instantiation
or is not a class or class template. If nothing is found by qualified
lookup for a member-qualified name that is the terminal name of a
_nested-name-specifier_ and is not dependent, it undergoes unqualified
lookup.

In non-standardese terms, these two paragraphs essentially state the
following:
- A name that immediately follows `.` or `->` in a class member access
expression is a member-qualified name
- A member-qualified name will be first looked up in the type of the
object expression `T` unless `T` is a dependent type that is _not_ the
current instantiation, e.g.
```
template<typename T>
struct A
{
    void f(T* t)
    {
        this->x; // type of the object expression is 'A<T>'. although 'A<T>' is dependent, it is the
                 // current instantiation so we look up 'x' in the template definition context.
        
        t->y; // type of the object expression is 'T' ('->' is transformed to '.' per [expr.ref]). 
              // 'T' is dependent and is *not* the current instantiation, so we lookup 'y' in the 
              // template instantiation context.
    }
};
```
- If the first lookup finds nothing and:
- the member-qualified name is the first component of a
_nested-name-specifier_ (which could be an _identifier_ or a
_simple-template-id_), and either:
- the type of the object expression is the current instantiation and it
has no dependent base classes, or
        - the type of the object expression is not dependent

  then we lookup the name again, this time via unqualified lookup.

Although the second (unqualified) lookup is stated not to occur when the
member-qualified name is dependent, a dependent name will _not_ be
dependent once the template is instantiated, so the second lookup must
"occur" during instantiation if qualified lookup does not find anything.
This means that we must perform the second (unqualified) lookup during
parsing even when the type of the object expression is dependent, but
those results are _not_ used to determine whether a `<` token is the
start of a _template-argument_list_; they are stored so we can replicate
the second lookup during instantiation.

In even simpler terms (paraphrasing the meeting minutes from the review of P1787; see https://wiki.edg.com/bin/view/Wg21summer2020/P1787%28Lookup%29Review2020-06-15Through2020-06-18):
- Unqualified lookup always happens for the first name in a
_nested-name-specifier_ that follows `.` or `->`
- The result of that lookup is only used to determine whether `<` is the
start of a _template-argument-list_ if the first (qualified) lookup
found nothing and the lookup context:
    - is not dependent, or 
    - is the current instantiation and has no dependent base classes.

An example:
```
struct A 
{
     void f();
};

template<typename T>
using B = A;

template<typename T>
struct C : A
{
    template<typename U>
    void g();

    void h(T* t)
    {
        this->g<int>(); // ok, '<' is the start of a template-argument-list ('g' was found via qualified lookup in the current instantiation)
        this->B<void>::f(); // ok, '<' is the start of a template-argument-list (current instantiation has no dependent bases, 'B' was found via unqualified lookup)
        t->g<int>(); // error: '<' means less than (unqualified lookup does not occur for a member-qualified name that isn't the first component of a nested-name-specifier)
        t->B<void>::f(); // error: '<' means less than (unqualified lookup does not occur if the name is dependent)
        t->template B<void>::f(); // ok: '<' is the start of a template-argument-list ('template' keyword used)
    }
};
```

Some additional notes:
- Per [basic.lookup.qual.general] p1, lookup for a
member-qualified name only considers namespaces, types, and templates
whose specializations are types if it's an _identifier_ followed by
`::`; lookup for the component name of a _simple-template-id_ followed
by `::` is _not_ subject to this rule.
- The wording which specifies when the second unqualified lookup occurs
appears to be paradoxical. We are supposed to do it only for the first
component name of a _nested-name-specifier_ that follows `.` or `->`
when qualified lookup finds nothing. However, when that name is followed
by `<` (potentially starting a _simple-template-id_) we don't _know_
whether it will be the start of a _nested-name-specifier_ until we do
the lookup -- but we aren't supposed to do the lookup until we know it's
part of a _nested-name-specifier_! ***However***, since we only do the
second lookup when the first lookup finds nothing (and the name isn't
dependent), ***and*** since neither lookup is type-only, the only valid
option is for the name to be the _template-name_ in a
_simple-template-id_ that is followed by `::` (it can't be an
_unqualified-id_ naming a member because we already determined that the
lookup context doesn't have a member with that name). Thus, we can lock
into the _nested-name-specifier_ interpretation and do the second lookup
without having to know whether the _simple-template-id_ will be followed
by `::` yet.
  • Loading branch information
sdkrystian authored Jul 9, 2024
1 parent afbd7d1 commit 7bfb98c
Show file tree
Hide file tree
Showing 46 changed files with 1,113 additions and 811 deletions.
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,9 @@ Resolutions to C++ Defect Reports
- Clang now considers ``noexcept(typeid(expr))`` more carefully, instead of always assuming that ``std::bad_typeid`` can be thrown.
(`CWG2191: Incorrect result for noexcept(typeid(v)) <https://cplusplus.github.io/CWG/issues/2191.html>`_).

- Clang now correctly implements lookup for the terminal name of a member-qualified nested-name-specifier.
(`CWG1835: Dependent member lookup before < <https://cplusplus.github.io/CWG/issues/1835.html>`_).

C Language Changes
------------------

Expand Down
92 changes: 49 additions & 43 deletions clang/include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -3676,9 +3676,9 @@ class CXXUnresolvedConstructExpr final
/// an implicit access if a qualifier is provided.
class CXXDependentScopeMemberExpr final
: public Expr,
private llvm::TrailingObjects<CXXDependentScopeMemberExpr,
ASTTemplateKWAndArgsInfo,
TemplateArgumentLoc, NamedDecl *> {
private llvm::TrailingObjects<
CXXDependentScopeMemberExpr, NestedNameSpecifierLoc, DeclAccessPair,
ASTTemplateKWAndArgsInfo, TemplateArgumentLoc> {
friend class ASTStmtReader;
friend class ASTStmtWriter;
friend TrailingObjects;
Expand All @@ -3691,17 +3691,15 @@ class CXXDependentScopeMemberExpr final
/// implicit accesses.
QualType BaseType;

/// The nested-name-specifier that precedes the member name, if any.
/// FIXME: This could be in principle store as a trailing object.
/// However the performance impact of doing so should be investigated first.
NestedNameSpecifierLoc QualifierLoc;

/// The member to which this member expression refers, which
/// can be name, overloaded operator, or destructor.
///
/// FIXME: could also be a template-id
DeclarationNameInfo MemberNameInfo;

/// The location of the '->' or '.' operator.
SourceLocation OperatorLoc;

// CXXDependentScopeMemberExpr is followed by several trailing objects,
// some of which optional. They are in order:
//
Expand All @@ -3721,8 +3719,16 @@ class CXXDependentScopeMemberExpr final
return CXXDependentScopeMemberExprBits.HasTemplateKWAndArgsInfo;
}

bool hasFirstQualifierFoundInScope() const {
return CXXDependentScopeMemberExprBits.HasFirstQualifierFoundInScope;
unsigned getNumUnqualifiedLookups() const {
return CXXDependentScopeMemberExprBits.NumUnqualifiedLookups;
}

unsigned numTrailingObjects(OverloadToken<NestedNameSpecifierLoc>) const {
return hasQualifier();
}

unsigned numTrailingObjects(OverloadToken<DeclAccessPair>) const {
return getNumUnqualifiedLookups();
}

unsigned numTrailingObjects(OverloadToken<ASTTemplateKWAndArgsInfo>) const {
Expand All @@ -3733,33 +3739,32 @@ class CXXDependentScopeMemberExpr final
return getNumTemplateArgs();
}

unsigned numTrailingObjects(OverloadToken<NamedDecl *>) const {
return hasFirstQualifierFoundInScope();
}

CXXDependentScopeMemberExpr(const ASTContext &Ctx, Expr *Base,
QualType BaseType, bool IsArrow,
SourceLocation OperatorLoc,
NestedNameSpecifierLoc QualifierLoc,
SourceLocation TemplateKWLoc,
NamedDecl *FirstQualifierFoundInScope,
ArrayRef<DeclAccessPair> UnqualifiedLookups,
DeclarationNameInfo MemberNameInfo,
const TemplateArgumentListInfo *TemplateArgs);

CXXDependentScopeMemberExpr(EmptyShell Empty, bool HasTemplateKWAndArgsInfo,
bool HasFirstQualifierFoundInScope);
CXXDependentScopeMemberExpr(EmptyShell Empty, bool HasQualifier,
unsigned NumUnqualifiedLookups,
bool HasTemplateKWAndArgsInfo);

public:
static CXXDependentScopeMemberExpr *
Create(const ASTContext &Ctx, Expr *Base, QualType BaseType, bool IsArrow,
SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc,
SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope,
SourceLocation TemplateKWLoc,
ArrayRef<DeclAccessPair> UnqualifiedLookups,
DeclarationNameInfo MemberNameInfo,
const TemplateArgumentListInfo *TemplateArgs);

static CXXDependentScopeMemberExpr *
CreateEmpty(const ASTContext &Ctx, bool HasTemplateKWAndArgsInfo,
unsigned NumTemplateArgs, bool HasFirstQualifierFoundInScope);
CreateEmpty(const ASTContext &Ctx, bool HasQualifier,
unsigned NumUnqualifiedLookups, bool HasTemplateKWAndArgsInfo,
unsigned NumTemplateArgs);

/// True if this is an implicit access, i.e. one in which the
/// member being accessed was not written in the source. The source
Expand All @@ -3784,34 +3789,35 @@ class CXXDependentScopeMemberExpr final
bool isArrow() const { return CXXDependentScopeMemberExprBits.IsArrow; }

/// Retrieve the location of the '->' or '.' operator.
SourceLocation getOperatorLoc() const {
return CXXDependentScopeMemberExprBits.OperatorLoc;
SourceLocation getOperatorLoc() const { return OperatorLoc; }

/// Determines whether this member expression had a nested-name-specifier
/// prior to the name of the member, e.g., x->Base::foo.
bool hasQualifier() const {
return CXXDependentScopeMemberExprBits.HasQualifier;
}

/// Retrieve the nested-name-specifier that qualifies the member name.
NestedNameSpecifier *getQualifier() const {
return QualifierLoc.getNestedNameSpecifier();
/// If the member name was qualified, retrieves the nested-name-specifier
/// that precedes the member name, with source-location information.
NestedNameSpecifierLoc getQualifierLoc() const {
if (!hasQualifier())
return NestedNameSpecifierLoc();
return *getTrailingObjects<NestedNameSpecifierLoc>();
}

/// Retrieve the nested-name-specifier that qualifies the member
/// name, with source location information.
NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; }
/// If the member name was qualified, retrieves the
/// nested-name-specifier that precedes the member name. Otherwise, returns
/// NULL.
NestedNameSpecifier *getQualifier() const {
return getQualifierLoc().getNestedNameSpecifier();
}

/// Retrieve the first part of the nested-name-specifier that was
/// found in the scope of the member access expression when the member access
/// was initially parsed.
///
/// This function only returns a useful result when member access expression
/// uses a qualified member name, e.g., "x.Base::f". Here, the declaration
/// returned by this function describes what was found by unqualified name
/// lookup for the identifier "Base" within the scope of the member access
/// expression itself. At template instantiation time, this information is
/// combined with the results of name lookup into the type of the object
/// expression itself (the class type of x).
NamedDecl *getFirstQualifierFoundInScope() const {
if (!hasFirstQualifierFoundInScope())
return nullptr;
return *getTrailingObjects<NamedDecl *>();
/// Retrieve the declarations found by unqualified lookup for the first
/// component name of the nested-name-specifier, if any.
ArrayRef<DeclAccessPair> unqualified_lookups() const {
if (!getNumUnqualifiedLookups())
return std::nullopt;
return {getTrailingObjects<DeclAccessPair>(), getNumUnqualifiedLookups()};
}

/// Retrieve the name of the member that this expression refers to.
Expand Down
15 changes: 8 additions & 7 deletions clang/include/clang/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -1020,18 +1020,19 @@ class alignas(void *) Stmt {
LLVM_PREFERRED_TYPE(bool)
unsigned IsArrow : 1;

/// True if this member expression used a nested-name-specifier to
/// refer to the member, e.g., "x->Base::f".
LLVM_PREFERRED_TYPE(bool)
unsigned HasQualifier : 1;

/// Whether this member expression has info for explicit template
/// keyword and arguments.
LLVM_PREFERRED_TYPE(bool)
unsigned HasTemplateKWAndArgsInfo : 1;

/// See getFirstQualifierFoundInScope() and the comment listing
/// the trailing objects.
LLVM_PREFERRED_TYPE(bool)
unsigned HasFirstQualifierFoundInScope : 1;

/// The location of the '->' or '.' operator.
SourceLocation OperatorLoc;
/// Number of declarations found by unqualified lookup for the
/// first component name of the nested-name-specifier.
unsigned NumUnqualifiedLookups;
};

class OverloadExprBitfields {
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/UnresolvedSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ class UnresolvedSetImpl {
decls().push_back(DeclAccessPair::make(D, AS));
}

void addAllDecls(ArrayRef<DeclAccessPair> Other) {
append(iterator(Other.begin()), iterator(Other.end()));
}

/// Replaces the given declaration with the new one, once.
///
/// \return true if the set changed
Expand Down
4 changes: 1 addition & 3 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -895,9 +895,7 @@ def missing_template_arg_list_after_template_kw : Extension<
"keyword">, InGroup<DiagGroup<"missing-template-arg-list-after-template-kw">>,
DefaultError;

def err_missing_dependent_template_keyword : Error<
"use 'template' keyword to treat '%0' as a dependent template name">;
def warn_missing_dependent_template_keyword : ExtWarn<
def ext_missing_dependent_template_keyword : ExtWarn<
"use 'template' keyword to treat '%0' as a dependent template name">;

def ext_extern_template : Extension<
Expand Down
14 changes: 5 additions & 9 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -3368,15 +3368,11 @@ class Parser : public CodeCompletionHandler {
BaseResult ParseBaseSpecifier(Decl *ClassDecl);
AccessSpecifier getAccessSpecifierIfPresent() const;

bool ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
ParsedType ObjectType,
bool ObjectHadErrors,
SourceLocation TemplateKWLoc,
IdentifierInfo *Name,
SourceLocation NameLoc,
bool EnteringContext,
UnqualifiedId &Id,
bool AssumeTemplateId);
bool ParseUnqualifiedIdTemplateId(
CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
SourceLocation TemplateKWLoc, SourceLocation TildeLoc,
IdentifierInfo *Name, SourceLocation NameLoc, bool EnteringContext,
UnqualifiedId &Id, bool AssumeTemplateId);
bool ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
ParsedType ObjectType,
UnqualifiedId &Result);
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Sema/DeclSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class CXXScopeSpec {
SourceRange Range;
NestedNameSpecifierLocBuilder Builder;
ArrayRef<TemplateParameterList *> TemplateParamLists;
ArrayRef<DeclAccessPair> UnqualifiedLookups;

public:
SourceRange getRange() const { return Range; }
Expand All @@ -91,6 +92,13 @@ class CXXScopeSpec {
return TemplateParamLists;
}

void setUnqualifiedLookups(ArrayRef<DeclAccessPair> Found) {
UnqualifiedLookups = Found;
}
ArrayRef<DeclAccessPair> getUnqualifiedLookups() const {
return UnqualifiedLookups;
}

/// Retrieve the representation of the nested-name-specifier.
NestedNameSpecifier *getScopeRep() const {
return Builder.getRepresentation();
Expand Down
8 changes: 6 additions & 2 deletions clang/include/clang/Sema/Lookup.h
Original file line number Diff line number Diff line change
Expand Up @@ -483,11 +483,15 @@ class LookupResult {
ResultKind = Found;
}

void addAllDecls(ArrayRef<DeclAccessPair> Other) {
Decls.addAllDecls(Other);
ResultKind = Found;
}

/// Add all the declarations from another set of lookup
/// results.
void addAllDecls(const LookupResult &Other) {
Decls.append(Other.Decls.begin(), Other.Decls.end());
ResultKind = Found;
addAllDecls(Other.Decls.pairs());
}

/// Determine whether no result was found because we could not
Expand Down
54 changes: 22 additions & 32 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -2802,7 +2802,8 @@ class Sema final : public SemaBase {
/// (e.g., Base::), perform name lookup for that identifier as a
/// nested-name-specifier within the given scope, and return the result of
/// that name lookup.
NamedDecl *FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS);
bool LookupFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS,
UnresolvedSetImpl &R);

/// Keeps information about an identifier in a nested-name-spec.
///
Expand Down Expand Up @@ -2842,9 +2843,6 @@ class Sema final : public SemaBase {
/// \param EnteringContext If true, enter the context specified by the
/// nested-name-specifier.
/// \param SS Optional nested name specifier preceding the identifier.
/// \param ScopeLookupResult Provides the result of name lookup within the
/// scope of the nested-name-specifier that was computed at template
/// definition time.
/// \param ErrorRecoveryLookup Specifies if the method is called to improve
/// error recovery and what kind of recovery is performed.
/// \param IsCorrectedToColon If not null, suggestion of replace '::' -> ':'
Expand All @@ -2853,11 +2851,6 @@ class Sema final : public SemaBase {
/// not '::'.
/// \param OnlyNamespace If true, only considers namespaces in lookup.
///
/// This routine differs only slightly from ActOnCXXNestedNameSpecifier, in
/// that it contains an extra parameter \p ScopeLookupResult, which provides
/// the result of name lookup within the scope of the nested-name-specifier
/// that was computed at template definition time.
///
/// If ErrorRecoveryLookup is true, then this call is used to improve error
/// recovery. This means that it should not emit diagnostics, it should
/// just return true on failure. It also means it should only return a valid
Expand All @@ -2866,7 +2859,6 @@ class Sema final : public SemaBase {
/// specifier.
bool BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
bool EnteringContext, CXXScopeSpec &SS,
NamedDecl *ScopeLookupResult,
bool ErrorRecoveryLookup,
bool *IsCorrectedToColon = nullptr,
bool OnlyNamespace = false);
Expand Down Expand Up @@ -8566,11 +8558,12 @@ class Sema final : public SemaBase {
const TemplateArgumentListInfo *TemplateArgs,
bool IsDefiniteInstance, const Scope *S);

ExprResult ActOnDependentMemberExpr(
Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OpLoc,
const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo,
const TemplateArgumentListInfo *TemplateArgs);
ExprResult
ActOnDependentMemberExpr(Expr *Base, QualType BaseType, bool IsArrow,
SourceLocation OpLoc, const CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
const DeclarationNameInfo &NameInfo,
const TemplateArgumentListInfo *TemplateArgs);

/// The main callback when the parser finds something like
/// expression . [nested-name-specifier] identifier
Expand Down Expand Up @@ -8626,15 +8619,14 @@ class Sema final : public SemaBase {
ExprResult BuildMemberReferenceExpr(
Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow,
CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo,
const DeclarationNameInfo &NameInfo,
const TemplateArgumentListInfo *TemplateArgs, const Scope *S,
ActOnMemberAccessExtraArgs *ExtraArgs = nullptr);

ExprResult
BuildMemberReferenceExpr(Expr *Base, QualType BaseType, SourceLocation OpLoc,
bool IsArrow, const CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
NamedDecl *FirstQualifierInScope, LookupResult &R,
SourceLocation TemplateKWLoc, LookupResult &R,
const TemplateArgumentListInfo *TemplateArgs,
const Scope *S, bool SuppressQualifierCheck = false,
ActOnMemberAccessExtraArgs *ExtraArgs = nullptr);
Expand Down Expand Up @@ -11122,15 +11114,14 @@ class Sema final : public SemaBase {
QualType ObjectType, bool EnteringContext,
RequiredTemplateKind RequiredTemplate = SourceLocation(),
AssumedTemplateKind *ATK = nullptr,
bool AllowTypoCorrection = true);
bool AllowTypoCorrection = true, bool MayBeNNS = false);

TemplateNameKind isTemplateName(Scope *S, CXXScopeSpec &SS,
bool hasTemplateKeyword,
const UnqualifiedId &Name,
ParsedType ObjectType, bool EnteringContext,
TemplateTy &Template,
bool &MemberOfUnknownSpecialization,
bool Disambiguation = false);
TemplateNameKind
isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword,
const UnqualifiedId &Name, ParsedType ObjectType,
bool EnteringContext, TemplateTy &Template,
bool &MemberOfUnknownSpecialization,
bool Disambiguation = false, bool MayBeNNS = false);

/// Try to resolve an undeclared template name as a type template.
///
Expand Down Expand Up @@ -11459,12 +11450,11 @@ class Sema final : public SemaBase {
/// For example, given "x.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.
TemplateNameKind ActOnTemplateName(Scope *S, CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
const UnqualifiedId &Name,
ParsedType ObjectType,
bool EnteringContext, TemplateTy &Template,
bool AllowInjectedClassName = false);
TemplateNameKind
ActOnTemplateName(Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
const UnqualifiedId &Name, ParsedType ObjectType,
bool EnteringContext, TemplateTy &Template,
bool AllowInjectedClassName = false, bool MayBeNNS = false);

DeclResult ActOnClassTemplateSpecialization(
Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
Expand Down
Loading

0 comments on commit 7bfb98c

Please sign in to comment.