зеркало из https://github.com/microsoft/clang-1.git
Parsing and semantic analysis for template-ids that name overloaded
operators, e.g., operator+<int> which now works in declarators, id-expressions, and member access expressions. This commit only implements the non-dependent case, where we can resolve the template-id to an actual declaration. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@85966 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
f758dc0812
Коммит
014e88d94f
|
@ -295,8 +295,6 @@ def err_variadic_templates : Error<
|
|||
// C++ declarations
|
||||
def err_friend_decl_defines_class : Error<
|
||||
"cannot define a type in a friend declaration">;
|
||||
def warn_operator_template_id_ignores_args : Error<
|
||||
"clang bug: ignoring template arguments provided for operator">;
|
||||
|
||||
// Language specific pragmas
|
||||
// - Generic warnings
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "clang/Basic/TemplateKinds.h"
|
||||
#include "clang/Basic/TokenKinds.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/OperatorKinds.h"
|
||||
#include <cstdlib>
|
||||
|
||||
namespace clang {
|
||||
|
@ -261,6 +262,9 @@ struct TemplateIdAnnotation {
|
|||
/// FIXME: Temporarily stores the name of a specialization
|
||||
IdentifierInfo *Name;
|
||||
|
||||
/// FIXME: Temporarily stores the overloaded operator kind.
|
||||
OverloadedOperatorKind Operator;
|
||||
|
||||
/// The declaration of the template corresponding to the
|
||||
/// template-name. This is an Action::DeclTy*.
|
||||
void *Template;
|
||||
|
|
|
@ -236,30 +236,36 @@ public:
|
|||
virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S,
|
||||
const CXXScopeSpec *SS = 0) = 0;
|
||||
|
||||
/// \brief Determine whether the given identifier refers to the name of a
|
||||
/// \brief Determine whether the given name refers to a template.
|
||||
///
|
||||
/// This callback is used by the parser after it has seen a '<' to determine
|
||||
/// whether the given name refers to a template and, if so, what kind of
|
||||
/// template.
|
||||
///
|
||||
/// \param S the scope in which name lookup occurs
|
||||
/// \param S the scope in which the name occurs.
|
||||
///
|
||||
/// \param II the identifier that we are querying to determine whether it
|
||||
/// is a template.
|
||||
/// \param SS the C++ nested-name-specifier that precedes the template name,
|
||||
/// if any.
|
||||
///
|
||||
/// \param IdLoc the source location of the identifier
|
||||
/// \param Name the name that we are querying to determine whether it is
|
||||
/// a template.
|
||||
///
|
||||
/// \param SS the C++ scope specifier that precedes the template name, if
|
||||
/// any.
|
||||
/// \param ObjectType if we are determining whether the given name is a
|
||||
/// template name in the context of a member access expression (e.g.,
|
||||
/// \c p->X<int>), this is the type of the object referred to by the
|
||||
/// member access (e.g., \c p).
|
||||
///
|
||||
/// \param EnteringContext whether we are potentially entering the context
|
||||
/// referred to by the scope specifier \p SS
|
||||
/// referred to by the nested-name-specifier \p SS, which allows semantic
|
||||
/// analysis to look into uninstantiated templates.
|
||||
///
|
||||
/// \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(Scope *S,
|
||||
const IdentifierInfo &II,
|
||||
SourceLocation IdLoc,
|
||||
const CXXScopeSpec *SS,
|
||||
const CXXScopeSpec &SS,
|
||||
UnqualifiedId &Name,
|
||||
TypeTy *ObjectType,
|
||||
bool EnteringContext,
|
||||
TemplateTy &Template) = 0;
|
||||
|
@ -1616,22 +1622,19 @@ public:
|
|||
///
|
||||
/// \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
|
||||
/// or the template name. 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 Name the name of the template.
|
||||
///
|
||||
/// \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,
|
||||
UnqualifiedId &Name,
|
||||
TypeTy *ObjectType) {
|
||||
return TemplateTy();
|
||||
}
|
||||
|
@ -2333,9 +2336,8 @@ public:
|
|||
const CXXScopeSpec *SS);
|
||||
|
||||
virtual TemplateNameKind isTemplateName(Scope *S,
|
||||
const IdentifierInfo &II,
|
||||
SourceLocation IdLoc,
|
||||
const CXXScopeSpec *SS,
|
||||
const CXXScopeSpec &SS,
|
||||
UnqualifiedId &Name,
|
||||
TypeTy *ObjectType,
|
||||
bool EnteringContext,
|
||||
TemplateTy &Template);
|
||||
|
|
|
@ -561,9 +561,9 @@ public:
|
|||
///
|
||||
/// \param Id the parsed identifier.
|
||||
/// \param IdLoc the location of the parsed identifier.
|
||||
void setIdentifier(IdentifierInfo *Id, SourceLocation IdLoc) {
|
||||
void setIdentifier(const IdentifierInfo *Id, SourceLocation IdLoc) {
|
||||
Kind = IK_Identifier;
|
||||
Identifier = Id;
|
||||
Identifier = const_cast<IdentifierInfo *>(Id);
|
||||
StartLocation = EndLocation = IdLoc;
|
||||
}
|
||||
|
||||
|
|
|
@ -161,9 +161,8 @@ bool MinimalAction::isCurrentClassName(const IdentifierInfo &, Scope *,
|
|||
|
||||
TemplateNameKind
|
||||
MinimalAction::isTemplateName(Scope *S,
|
||||
const IdentifierInfo &II,
|
||||
SourceLocation IdLoc,
|
||||
const CXXScopeSpec *SS,
|
||||
const CXXScopeSpec &SS,
|
||||
UnqualifiedId &Name,
|
||||
TypeTy *ObjectType,
|
||||
bool EnteringScope,
|
||||
TemplateTy &TemplateDecl) {
|
||||
|
|
|
@ -125,10 +125,10 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
|
|||
break;
|
||||
}
|
||||
|
||||
UnqualifiedId TemplateName;
|
||||
TemplateName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
|
||||
TemplateTy Template
|
||||
= Actions.ActOnDependentTemplateName(TemplateKWLoc,
|
||||
*Tok.getIdentifierInfo(),
|
||||
Tok.getLocation(), SS,
|
||||
= Actions.ActOnDependentTemplateName(TemplateKWLoc, SS, TemplateName,
|
||||
ObjectType);
|
||||
if (!Template)
|
||||
break;
|
||||
|
@ -220,9 +220,10 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
|
|||
// type-name '<'
|
||||
if (Next.is(tok::less)) {
|
||||
TemplateTy Template;
|
||||
if (TemplateNameKind TNK = Actions.isTemplateName(CurScope, II,
|
||||
Tok.getLocation(),
|
||||
&SS,
|
||||
UnqualifiedId TemplateName;
|
||||
TemplateName.setIdentifier(&II, Tok.getLocation());
|
||||
if (TemplateNameKind TNK = Actions.isTemplateName(CurScope, SS,
|
||||
TemplateName,
|
||||
ObjectType,
|
||||
EnteringContext,
|
||||
Template)) {
|
||||
|
@ -741,45 +742,30 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
|
|||
TemplateNameKind TNK = TNK_Non_template;
|
||||
switch (Id.getKind()) {
|
||||
case UnqualifiedId::IK_Identifier:
|
||||
TNK = Actions.isTemplateName(CurScope, *Id.Identifier, Id.StartLocation,
|
||||
&SS, ObjectType, EnteringContext, Template);
|
||||
case UnqualifiedId::IK_OperatorFunctionId:
|
||||
TNK = Actions.isTemplateName(CurScope, SS, Id, ObjectType, EnteringContext,
|
||||
Template);
|
||||
break;
|
||||
|
||||
case UnqualifiedId::IK_OperatorFunctionId: {
|
||||
// FIXME: Temporary hack: warn that we are completely ignoring the
|
||||
// template arguments for now.
|
||||
// Parse the enclosed template argument list and throw it away.
|
||||
SourceLocation LAngleLoc, RAngleLoc;
|
||||
TemplateArgList TemplateArgs;
|
||||
TemplateArgIsTypeList TemplateArgIsType;
|
||||
TemplateArgLocationList TemplateArgLocations;
|
||||
if (ParseTemplateIdAfterTemplateName(Template, Id.StartLocation,
|
||||
&SS, true, LAngleLoc,
|
||||
TemplateArgs,
|
||||
TemplateArgIsType,
|
||||
TemplateArgLocations,
|
||||
RAngleLoc))
|
||||
return true;
|
||||
|
||||
Diag(Id.StartLocation, diag::warn_operator_template_id_ignores_args)
|
||||
<< SourceRange(LAngleLoc, RAngleLoc);
|
||||
case UnqualifiedId::IK_ConstructorName: {
|
||||
UnqualifiedId TemplateName;
|
||||
TemplateName.setIdentifier(Name, NameLoc);
|
||||
TNK = Actions.isTemplateName(CurScope, SS, TemplateName, ObjectType,
|
||||
EnteringContext, Template);
|
||||
break;
|
||||
}
|
||||
|
||||
case UnqualifiedId::IK_ConstructorName:
|
||||
TNK = Actions.isTemplateName(CurScope, *Name, NameLoc, &SS, ObjectType,
|
||||
EnteringContext, Template);
|
||||
break;
|
||||
|
||||
case UnqualifiedId::IK_DestructorName:
|
||||
case UnqualifiedId::IK_DestructorName: {
|
||||
UnqualifiedId TemplateName;
|
||||
TemplateName.setIdentifier(Name, NameLoc);
|
||||
if (ObjectType) {
|
||||
Template = Actions.ActOnDependentTemplateName(SourceLocation(), *Name,
|
||||
NameLoc, SS, ObjectType);
|
||||
Template = Actions.ActOnDependentTemplateName(SourceLocation(), SS,
|
||||
TemplateName, ObjectType);
|
||||
TNK = TNK_Dependent_template_name;
|
||||
if (!Template.get())
|
||||
return true;
|
||||
} else {
|
||||
TNK = Actions.isTemplateName(CurScope, *Name, NameLoc, &SS, ObjectType,
|
||||
TNK = Actions.isTemplateName(CurScope, SS, TemplateName, ObjectType,
|
||||
EnteringContext, Template);
|
||||
|
||||
if (TNK == TNK_Non_template && Id.DestructorName == 0) {
|
||||
|
@ -794,6 +780,7 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
|
|||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
|
@ -824,9 +811,12 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
|
|||
|
||||
if (Id.getKind() == UnqualifiedId::IK_Identifier) {
|
||||
TemplateId->Name = Id.Identifier;
|
||||
TemplateId->Operator = OO_None;
|
||||
TemplateId->TemplateNameLoc = Id.StartLocation;
|
||||
} else {
|
||||
// FIXME: Handle IK_OperatorFunctionId
|
||||
TemplateId->Name = 0;
|
||||
TemplateId->Operator = Id.OperatorFunctionId.Operator;
|
||||
TemplateId->TemplateNameLoc = Id.StartLocation;
|
||||
}
|
||||
|
||||
TemplateId->Template = Template.getAs<void*>();
|
||||
|
|
|
@ -935,9 +935,10 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) {
|
|||
// If this is a template-id, annotate with a template-id or type token.
|
||||
if (NextToken().is(tok::less)) {
|
||||
TemplateTy Template;
|
||||
UnqualifiedId TemplateName;
|
||||
TemplateName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
|
||||
if (TemplateNameKind TNK
|
||||
= Actions.isTemplateName(CurScope, *Tok.getIdentifierInfo(),
|
||||
Tok.getLocation(), &SS,
|
||||
= Actions.isTemplateName(CurScope, SS, TemplateName,
|
||||
/*ObjectType=*/0, EnteringContext,
|
||||
Template))
|
||||
if (AnnotateTemplateIdToken(Template, TNK, &SS)) {
|
||||
|
|
|
@ -2397,9 +2397,8 @@ public:
|
|||
// C++ Templates [C++ 14]
|
||||
//
|
||||
virtual TemplateNameKind isTemplateName(Scope *S,
|
||||
const IdentifierInfo &II,
|
||||
SourceLocation IdLoc,
|
||||
const CXXScopeSpec *SS,
|
||||
const CXXScopeSpec &SS,
|
||||
UnqualifiedId &Name,
|
||||
TypeTy *ObjectType,
|
||||
bool EnteringContext,
|
||||
TemplateTy &Template);
|
||||
|
@ -2499,9 +2498,8 @@ public:
|
|||
SourceLocation RAngleLoc);
|
||||
|
||||
virtual TemplateTy ActOnDependentTemplateName(SourceLocation TemplateKWLoc,
|
||||
const IdentifierInfo &Name,
|
||||
SourceLocation NameLoc,
|
||||
const CXXScopeSpec &SS,
|
||||
UnqualifiedId &Name,
|
||||
TypeTy *ObjectType);
|
||||
|
||||
bool CheckClassTemplatePartialSpecializationArgs(
|
||||
|
|
|
@ -98,29 +98,43 @@ static NamedDecl *isAcceptableTemplateName(ASTContext &Context, NamedDecl *D) {
|
|||
}
|
||||
|
||||
TemplateNameKind Sema::isTemplateName(Scope *S,
|
||||
const IdentifierInfo &II,
|
||||
SourceLocation IdLoc,
|
||||
const CXXScopeSpec *SS,
|
||||
const CXXScopeSpec &SS,
|
||||
UnqualifiedId &Name,
|
||||
TypeTy *ObjectTypePtr,
|
||||
bool EnteringContext,
|
||||
TemplateTy &TemplateResult) {
|
||||
DeclarationName TName;
|
||||
|
||||
switch (Name.getKind()) {
|
||||
case UnqualifiedId::IK_Identifier:
|
||||
TName = DeclarationName(Name.Identifier);
|
||||
break;
|
||||
|
||||
case UnqualifiedId::IK_OperatorFunctionId:
|
||||
TName = Context.DeclarationNames.getCXXOperatorName(
|
||||
Name.OperatorFunctionId.Operator);
|
||||
break;
|
||||
|
||||
default:
|
||||
return TNK_Non_template;
|
||||
}
|
||||
|
||||
// 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");
|
||||
assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist");
|
||||
QualType ObjectType = QualType::getFromOpaquePtr(ObjectTypePtr);
|
||||
LookupCtx = computeDeclContext(ObjectType);
|
||||
isDependent = ObjectType->isDependentType();
|
||||
} else if (SS && SS->isSet()) {
|
||||
} 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);
|
||||
LookupCtx = computeDeclContext(SS, EnteringContext);
|
||||
isDependent = isDependentScopeSpecifier(SS);
|
||||
}
|
||||
|
||||
LookupResult Found;
|
||||
|
@ -132,10 +146,10 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
|
|||
// nested-name-specifier.
|
||||
|
||||
// The declaration context must be complete.
|
||||
if (!LookupCtx->isDependentContext() && RequireCompleteDeclContext(*SS))
|
||||
if (!LookupCtx->isDependentContext() && RequireCompleteDeclContext(SS))
|
||||
return TNK_Non_template;
|
||||
|
||||
LookupQualifiedName(Found, LookupCtx, &II, LookupOrdinaryName);
|
||||
LookupQualifiedName(Found, LookupCtx, TName, LookupOrdinaryName);
|
||||
|
||||
if (ObjectTypePtr && Found.getKind() == LookupResult::NotFound) {
|
||||
// C++ [basic.lookup.classref]p1:
|
||||
|
@ -150,7 +164,7 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
|
|||
//
|
||||
// FIXME: When we're instantiating a template, do we actually have to
|
||||
// look in the scope of the template? Seems fishy...
|
||||
LookupName(Found, S, &II, LookupOrdinaryName);
|
||||
LookupName(Found, S, TName, LookupOrdinaryName);
|
||||
ObjectTypeSearchedInScope = true;
|
||||
}
|
||||
} else if (isDependent) {
|
||||
|
@ -158,7 +172,7 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
|
|||
return TNK_Non_template;
|
||||
} else {
|
||||
// Perform unqualified name lookup in the current scope.
|
||||
LookupName(Found, S, &II, LookupOrdinaryName);
|
||||
LookupName(Found, S, TName, LookupOrdinaryName);
|
||||
}
|
||||
|
||||
// FIXME: Cope with ambiguous name-lookup results.
|
||||
|
@ -177,7 +191,7 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
|
|||
// postfix-expression and [...]
|
||||
//
|
||||
LookupResult FoundOuter;
|
||||
LookupName(FoundOuter, S, &II, LookupOrdinaryName);
|
||||
LookupName(FoundOuter, S, TName, LookupOrdinaryName);
|
||||
// FIXME: Handle ambiguities in this lookup better
|
||||
NamedDecl *OuterTemplate
|
||||
= isAcceptableTemplateName(Context, FoundOuter.getAsSingleDecl(Context));
|
||||
|
@ -194,8 +208,10 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
|
|||
// 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(Name.getSourceRange().getBegin(),
|
||||
diag::err_nested_name_member_ref_lookup_ambiguous)
|
||||
<< TName
|
||||
<< Name.getSourceRange();
|
||||
Diag(Template->getLocation(), diag::note_ambig_member_ref_object_type)
|
||||
<< QualType::getFromOpaquePtr(ObjectTypePtr);
|
||||
Diag(OuterTemplate->getLocation(), diag::note_ambig_member_ref_scope);
|
||||
|
@ -206,9 +222,9 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
|
|||
}
|
||||
}
|
||||
|
||||
if (SS && SS->isSet() && !SS->isInvalid()) {
|
||||
if (SS.isSet() && !SS.isInvalid()) {
|
||||
NestedNameSpecifier *Qualifier
|
||||
= static_cast<NestedNameSpecifier *>(SS->getScopeRep());
|
||||
= static_cast<NestedNameSpecifier *>(SS.getScopeRep());
|
||||
if (OverloadedFunctionDecl *Ovl
|
||||
= dyn_cast<OverloadedFunctionDecl>(Template))
|
||||
TemplateResult
|
||||
|
@ -1345,9 +1361,8 @@ Sema::OwningExprResult Sema::ActOnTemplateIdExpr(const CXXScopeSpec &SS,
|
|||
/// of the "template" keyword, and "apply" is the \p Name.
|
||||
Sema::TemplateTy
|
||||
Sema::ActOnDependentTemplateName(SourceLocation TemplateKWLoc,
|
||||
const IdentifierInfo &Name,
|
||||
SourceLocation NameLoc,
|
||||
const CXXScopeSpec &SS,
|
||||
UnqualifiedId &Name,
|
||||
TypeTy *ObjectType) {
|
||||
if ((ObjectType &&
|
||||
computeDeclContext(QualType::getFromOpaquePtr(ObjectType))) ||
|
||||
|
@ -1369,11 +1384,13 @@ Sema::ActOnDependentTemplateName(SourceLocation TemplateKWLoc,
|
|||
// "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(0, Name, NameLoc, &SS, ObjectType,
|
||||
TemplateNameKind TNK = isTemplateName(0, SS, Name, ObjectType,
|
||||
false, Template);
|
||||
if (TNK == TNK_Non_template) {
|
||||
Diag(NameLoc, diag::err_template_kw_refers_to_non_template)
|
||||
<< &Name;
|
||||
Diag(Name.getSourceRange().getBegin(),
|
||||
diag::err_template_kw_refers_to_non_template)
|
||||
<< GetNameFromUnqualifiedId(Name)
|
||||
<< Name.getSourceRange();
|
||||
return TemplateTy();
|
||||
}
|
||||
|
||||
|
@ -1382,7 +1399,21 @@ Sema::ActOnDependentTemplateName(SourceLocation TemplateKWLoc,
|
|||
|
||||
NestedNameSpecifier *Qualifier
|
||||
= static_cast<NestedNameSpecifier *>(SS.getScopeRep());
|
||||
return TemplateTy::make(Context.getDependentTemplateName(Qualifier, &Name));
|
||||
|
||||
switch (Name.getKind()) {
|
||||
case UnqualifiedId::IK_Identifier:
|
||||
return TemplateTy::make(Context.getDependentTemplateName(Qualifier,
|
||||
Name.Identifier));
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Diag(Name.getSourceRange().getBegin(),
|
||||
diag::err_template_kw_refers_to_non_template)
|
||||
<< GetNameFromUnqualifiedId(Name)
|
||||
<< Name.getSourceRange();
|
||||
return TemplateTy();
|
||||
}
|
||||
|
||||
bool Sema::CheckTemplateTypeArgument(TemplateTypeParmDecl *Param,
|
||||
|
|
|
@ -5184,11 +5184,12 @@ TreeTransform<Derived>::RebuildTemplateName(NestedNameSpecifier *Qualifier,
|
|||
CXXScopeSpec SS;
|
||||
SS.setRange(SourceRange(getDerived().getBaseLocation()));
|
||||
SS.setScopeRep(Qualifier);
|
||||
UnqualifiedId Name;
|
||||
Name.setIdentifier(&II, /*FIXME:*/getDerived().getBaseLocation());
|
||||
return getSema().ActOnDependentTemplateName(
|
||||
/*FIXME:*/getDerived().getBaseLocation(),
|
||||
II,
|
||||
/*FIXME:*/getDerived().getBaseLocation(),
|
||||
SS,
|
||||
Name,
|
||||
ObjectType.getAsOpaquePtr())
|
||||
.template getAsVal<TemplateName>();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// RUN: clang-cc -fsyntax-only -verify %s
|
||||
|
||||
template<typename T>
|
||||
struct A {
|
||||
template<typename U> A<T> operator+(U);
|
||||
};
|
||||
|
||||
template<int Value, typename T> bool operator==(A<T>, A<T>);
|
||||
|
||||
template<> bool operator==<0>(A<int>, A<int>);
|
||||
|
||||
bool test_qualified_id(A<int> ai) {
|
||||
return ::operator==<0, int>(ai, ai);
|
||||
}
|
||||
|
||||
void test_op(A<int> a, int i) {
|
||||
const A<int> &air = a.operator+<int>(i);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void test_op_template(A<T> at, T x) {
|
||||
// FIXME: Not yet implemented.
|
||||
// const A<T> &atr = at.template operator+<T>(x);
|
||||
// const A<T> &atr2 = at.A<T>::template operator+<T>(x);
|
||||
}
|
||||
|
||||
template void test_op_template<float>(A<float>, float);
|
Загрузка…
Ссылка в новой задаче