forked from OSchip/llvm-project
[c++20] Implement P0846R0: allow (ADL-only) calls to template-ids whose
template name is not visible to unqualified lookup. In order to support this without a severe degradation in our ability to diagnose typos in template names, this change significantly restructures the way we handle template-id-shaped syntax for which lookup of the template name finds nothing. Instead of eagerly diagnosing an undeclared template name, we now form a placeholder template-name representing a name that is known to not find any templates. When the parser sees such a name, it attempts to disambiguate whether we have a less-than comparison or a template-id. Any diagnostics or typo-correction for the name are delayed until its point of use. The upshot should be a small improvement of our diagostic quality overall: we now take more syntactic context into account when trying to resolve an undeclared identifier on the left hand side of a '<'. In fact, this works well enough that the backwards-compatible portion (for an undeclared identifier rather than a lookup that finds functions but no function templates) is enabled in all language modes. llvm-svn: 360308
This commit is contained in:
parent
a438a898b0
commit
b23c5e8c3d
|
@ -1977,6 +1977,7 @@ public:
|
|||
|
||||
TemplateName getOverloadedTemplateName(UnresolvedSetIterator Begin,
|
||||
UnresolvedSetIterator End) const;
|
||||
TemplateName getAssumedTemplateName(DeclarationName Name) const;
|
||||
|
||||
TemplateName getQualifiedTemplateName(NestedNameSpecifier *NNS,
|
||||
bool TemplateKeyword,
|
||||
|
|
|
@ -863,4 +863,24 @@ struct DenseMapInfo<clang::DeclarationName> {
|
|||
|
||||
} // namespace llvm
|
||||
|
||||
// The definition of AssumedTemplateStorage is factored out of TemplateName to
|
||||
// resolve a cyclic dependency between it and DeclarationName (via Type).
|
||||
namespace clang {
|
||||
|
||||
/// A structure for storing the information associated with a name that has
|
||||
/// been assumed to be a template name (despite finding no TemplateDecls).
|
||||
class AssumedTemplateStorage : public UncommonTemplateNameStorage {
|
||||
friend class ASTContext;
|
||||
|
||||
AssumedTemplateStorage(DeclarationName Name)
|
||||
: UncommonTemplateNameStorage(Assumed, 0), Name(Name) {}
|
||||
DeclarationName Name;
|
||||
|
||||
public:
|
||||
/// Get the name of the template.
|
||||
DeclarationName getDeclName() const { return Name; }
|
||||
};
|
||||
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_AST_DECLARATIONNAME_H
|
||||
|
|
|
@ -31,6 +31,7 @@ class NamedDecl;
|
|||
class NestedNameSpecifier;
|
||||
enum OverloadedOperatorKind : int;
|
||||
class OverloadedTemplateStorage;
|
||||
class AssumedTemplateStorage;
|
||||
class PartialDiagnostic;
|
||||
struct PrintingPolicy;
|
||||
class QualifiedTemplateName;
|
||||
|
@ -46,6 +47,7 @@ class UncommonTemplateNameStorage {
|
|||
protected:
|
||||
enum Kind {
|
||||
Overloaded,
|
||||
Assumed, // defined in DeclarationName.h
|
||||
SubstTemplateTemplateParm,
|
||||
SubstTemplateTemplateParmPack
|
||||
};
|
||||
|
@ -78,6 +80,12 @@ public:
|
|||
: nullptr;
|
||||
}
|
||||
|
||||
AssumedTemplateStorage *getAsAssumedTemplateName() {
|
||||
return Bits.Kind == Assumed
|
||||
? reinterpret_cast<AssumedTemplateStorage *>(this)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
SubstTemplateTemplateParmStorage *getAsSubstTemplateTemplateParm() {
|
||||
return Bits.Kind == SubstTemplateTemplateParm
|
||||
? reinterpret_cast<SubstTemplateTemplateParmStorage *>(this)
|
||||
|
@ -194,6 +202,10 @@ public:
|
|||
/// A set of overloaded template declarations.
|
||||
OverloadedTemplate,
|
||||
|
||||
/// An unqualified-id that has been assumed to name a function template
|
||||
/// that will be found by ADL.
|
||||
AssumedTemplate,
|
||||
|
||||
/// A qualified template name, where the qualification is kept
|
||||
/// to describe the source code as written.
|
||||
QualifiedTemplate,
|
||||
|
@ -215,6 +227,7 @@ public:
|
|||
TemplateName() = default;
|
||||
explicit TemplateName(TemplateDecl *Template);
|
||||
explicit TemplateName(OverloadedTemplateStorage *Storage);
|
||||
explicit TemplateName(AssumedTemplateStorage *Storage);
|
||||
explicit TemplateName(SubstTemplateTemplateParmStorage *Storage);
|
||||
explicit TemplateName(SubstTemplateTemplateParmPackStorage *Storage);
|
||||
explicit TemplateName(QualifiedTemplateName *Qual);
|
||||
|
@ -236,7 +249,7 @@ public:
|
|||
TemplateDecl *getAsTemplateDecl() const;
|
||||
|
||||
/// Retrieve the underlying, overloaded function template
|
||||
// declarations that this template name refers to, if known.
|
||||
/// declarations that this template name refers to, if known.
|
||||
///
|
||||
/// \returns The set of overloaded function templates that this template
|
||||
/// name refers to, if known. If the template name does not refer to a
|
||||
|
@ -244,6 +257,10 @@ public:
|
|||
/// refers to a single template, returns NULL.
|
||||
OverloadedTemplateStorage *getAsOverloadedTemplate() const;
|
||||
|
||||
/// Retrieve information on a name that has been assumed to be a
|
||||
/// template-name in order to permit a call via ADL.
|
||||
AssumedTemplateStorage *getAsAssumedTemplateName() const;
|
||||
|
||||
/// Retrieve the substituted template template parameter, if
|
||||
/// known.
|
||||
///
|
||||
|
|
|
@ -744,7 +744,8 @@ def err_typename_refers_to_non_type_template : Error<
|
|||
def err_expected_type_name_after_typename : Error<
|
||||
"expected an identifier or template-id after '::'">;
|
||||
def err_explicit_spec_non_template : Error<
|
||||
"explicit %select{specialization|instantiation}0 of non-template %1 %2">;
|
||||
"explicit %select{specialization|instantiation}0 of "
|
||||
"%select{non-|undeclared }3template %1 %2">;
|
||||
|
||||
def err_default_template_template_parameter_not_template : Error<
|
||||
"default template argument for a template template parameter must be a class "
|
||||
|
|
|
@ -3963,6 +3963,15 @@ def err_template_member_noparams : Error<
|
|||
def err_template_tag_noparams : Error<
|
||||
"extraneous 'template<>' in declaration of %0 %1">;
|
||||
|
||||
def warn_cxx17_compat_adl_only_template_id : Warning<
|
||||
"use of function template name with no prior function template "
|
||||
"declaration in function call with explicit template arguments "
|
||||
"is incompatible with C++ standards before C++2a">,
|
||||
InGroup<CXXPre2aCompat>, DefaultIgnore;
|
||||
def ext_adl_only_template_id : ExtWarn<
|
||||
"use of function template name with no prior declaration in function call "
|
||||
"with explicit template arguments is a C++2a extension">, InGroup<CXX2a>;
|
||||
|
||||
// C++ Template Argument Lists
|
||||
def err_template_missing_args : Error<
|
||||
"use of "
|
||||
|
|
|
@ -21,7 +21,8 @@ enum TemplateNameKind {
|
|||
/// The name does not refer to a template.
|
||||
TNK_Non_template = 0,
|
||||
/// The name refers to a function template or a set of overloaded
|
||||
/// functions that includes at least one function template.
|
||||
/// functions that includes at least one function template, or (in C++20)
|
||||
/// refers to a set of non-template functions but is followed by a '<'.
|
||||
TNK_Function_template,
|
||||
/// The name refers to a template whose specialization produces a
|
||||
/// type. The template itself could be a class template, template
|
||||
|
@ -42,7 +43,11 @@ enum TemplateNameKind {
|
|||
/// whether the template name is assumed to refer to a type template or a
|
||||
/// function template depends on the context in which the template
|
||||
/// name occurs.
|
||||
TNK_Dependent_template_name
|
||||
TNK_Dependent_template_name,
|
||||
/// Lookup for the name failed, but we're assuming it was a template name
|
||||
/// anyway. In C++20, this is mandatory in order to parse ADL-only function
|
||||
/// template specialization calls.
|
||||
TNK_Undeclared_template,
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -2303,13 +2303,18 @@ private:
|
|||
/// Doesn't consume tokens.
|
||||
TPResult
|
||||
isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False,
|
||||
bool *HasMissingTypename = nullptr);
|
||||
bool *InvalidAsDeclSpec = nullptr);
|
||||
|
||||
/// Given that isCXXDeclarationSpecifier returns \c TPResult::True or
|
||||
/// \c TPResult::Ambiguous, determine whether the decl-specifier would be
|
||||
/// a type-specifier other than a cv-qualifier.
|
||||
bool isCXXDeclarationSpecifierAType();
|
||||
|
||||
/// Determine whether the current token sequence might be
|
||||
/// '<' template-argument-list '>'
|
||||
/// rather than a less-than expression.
|
||||
TPResult isTemplateArgumentList(unsigned TokensToSkip);
|
||||
|
||||
/// Determine whether an identifier has been tentatively declared as a
|
||||
/// non-type. Such tentative declarations should not be found to name a type
|
||||
/// during a tentative parse, but also should not be annotated as a non-type.
|
||||
|
@ -2994,7 +2999,6 @@ private:
|
|||
UnqualifiedId &TemplateName,
|
||||
bool AllowTypeAnnotation = true);
|
||||
void AnnotateTemplateIdTokenAsType(bool IsClassName = false);
|
||||
bool IsTemplateArgumentList(unsigned Skip = 0);
|
||||
bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs);
|
||||
ParsedTemplateArgument ParseTemplateTemplateArgument();
|
||||
ParsedTemplateArgument ParseTemplateArgument();
|
||||
|
|
|
@ -1823,7 +1823,8 @@ public:
|
|||
NC_NestedNameSpecifier,
|
||||
NC_TypeTemplate,
|
||||
NC_VarTemplate,
|
||||
NC_FunctionTemplate
|
||||
NC_FunctionTemplate,
|
||||
NC_UndeclaredTemplate,
|
||||
};
|
||||
|
||||
class NameClassification {
|
||||
|
@ -1871,6 +1872,12 @@ public:
|
|||
return Result;
|
||||
}
|
||||
|
||||
static NameClassification UndeclaredTemplate(TemplateName Name) {
|
||||
NameClassification Result(NC_UndeclaredTemplate);
|
||||
Result.Template = Name;
|
||||
return Result;
|
||||
}
|
||||
|
||||
NameClassificationKind getKind() const { return Kind; }
|
||||
|
||||
ParsedType getType() const {
|
||||
|
@ -1885,7 +1892,7 @@ public:
|
|||
|
||||
TemplateName getTemplateName() const {
|
||||
assert(Kind == NC_TypeTemplate || Kind == NC_FunctionTemplate ||
|
||||
Kind == NC_VarTemplate);
|
||||
Kind == NC_VarTemplate || Kind == NC_UndeclaredTemplate);
|
||||
return Template;
|
||||
}
|
||||
|
||||
|
@ -1897,6 +1904,8 @@ public:
|
|||
return TNK_Function_template;
|
||||
case NC_VarTemplate:
|
||||
return TNK_Var_template;
|
||||
case NC_UndeclaredTemplate:
|
||||
return TNK_Undeclared_template;
|
||||
default:
|
||||
llvm_unreachable("unsupported name classification.");
|
||||
}
|
||||
|
@ -6256,7 +6265,8 @@ public:
|
|||
bool AllowDependent = true);
|
||||
bool hasAnyAcceptableTemplateNames(LookupResult &R,
|
||||
bool AllowFunctionTemplates = true,
|
||||
bool AllowDependent = true);
|
||||
bool AllowDependent = true,
|
||||
bool AllowNonTemplateFunctions = false);
|
||||
/// Try to interpret the lookup result D as a template-name.
|
||||
///
|
||||
/// \param D A declaration found by name lookup.
|
||||
|
@ -6268,10 +6278,20 @@ public:
|
|||
bool AllowFunctionTemplates = true,
|
||||
bool AllowDependent = true);
|
||||
|
||||
enum class AssumedTemplateKind {
|
||||
/// This is not assumed to be a template name.
|
||||
None,
|
||||
/// This is assumed to be a template name because lookup found nothing.
|
||||
FoundNothing,
|
||||
/// This is assumed to be a template name because lookup found one or more
|
||||
/// functions (but no function templates).
|
||||
FoundFunctions,
|
||||
};
|
||||
bool LookupTemplateName(LookupResult &R, Scope *S, CXXScopeSpec &SS,
|
||||
QualType ObjectType, bool EnteringContext,
|
||||
bool &MemberOfUnknownSpecialization,
|
||||
SourceLocation TemplateKWLoc = SourceLocation());
|
||||
SourceLocation TemplateKWLoc = SourceLocation(),
|
||||
AssumedTemplateKind *ATK = nullptr);
|
||||
|
||||
TemplateNameKind isTemplateName(Scope *S,
|
||||
CXXScopeSpec &SS,
|
||||
|
@ -6282,6 +6302,20 @@ public:
|
|||
TemplateTy &Template,
|
||||
bool &MemberOfUnknownSpecialization);
|
||||
|
||||
/// Try to resolve an undeclared template name as a type template.
|
||||
///
|
||||
/// Sets II to the identifier corresponding to the template name, and updates
|
||||
/// Name to a corresponding (typo-corrected) type template name and TNK to
|
||||
/// the corresponding kind, if possible.
|
||||
void ActOnUndeclaredTypeTemplateName(Scope *S, TemplateTy &Name,
|
||||
TemplateNameKind &TNK,
|
||||
SourceLocation NameLoc,
|
||||
IdentifierInfo *&II);
|
||||
|
||||
bool resolveAssumedTemplateNameAsType(Scope *S, TemplateName &Name,
|
||||
SourceLocation NameLoc,
|
||||
bool Diagnose = true);
|
||||
|
||||
/// Determine whether a particular identifier might be the name in a C++1z
|
||||
/// deduction-guide declaration.
|
||||
bool isDeductionGuideName(Scope *S, const IdentifierInfo &Name,
|
||||
|
@ -6391,14 +6425,11 @@ public:
|
|||
TemplateArgumentListInfo &TemplateArgs);
|
||||
|
||||
TypeResult
|
||||
ActOnTemplateIdType(CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
|
||||
ActOnTemplateIdType(Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
|
||||
TemplateTy Template, IdentifierInfo *TemplateII,
|
||||
SourceLocation TemplateIILoc,
|
||||
SourceLocation LAngleLoc,
|
||||
ASTTemplateArgsPtr TemplateArgs,
|
||||
SourceLocation RAngleLoc,
|
||||
bool IsCtorOrDtorName = false,
|
||||
bool IsClassName = false);
|
||||
SourceLocation TemplateIILoc, SourceLocation LAngleLoc,
|
||||
ASTTemplateArgsPtr TemplateArgs, SourceLocation RAngleLoc,
|
||||
bool IsCtorOrDtorName = false, bool IsClassName = false);
|
||||
|
||||
/// Parsed an elaborated-type-specifier that refers to a template-id,
|
||||
/// such as \c class T::template apply<U>.
|
||||
|
|
|
@ -5226,6 +5226,11 @@ ASTContext::getNameForTemplate(TemplateName Name,
|
|||
return DeclarationNameInfo((*Storage->begin())->getDeclName(), NameLoc);
|
||||
}
|
||||
|
||||
case TemplateName::AssumedTemplate: {
|
||||
AssumedTemplateStorage *Storage = Name.getAsAssumedTemplateName();
|
||||
return DeclarationNameInfo(Storage->getDeclName(), NameLoc);
|
||||
}
|
||||
|
||||
case TemplateName::DependentTemplate: {
|
||||
DependentTemplateName *DTN = Name.getAsDependentTemplateName();
|
||||
DeclarationName DName;
|
||||
|
@ -5273,7 +5278,8 @@ TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name) const {
|
|||
}
|
||||
|
||||
case TemplateName::OverloadedTemplate:
|
||||
llvm_unreachable("cannot canonicalize overloaded template");
|
||||
case TemplateName::AssumedTemplate:
|
||||
llvm_unreachable("cannot canonicalize unresolved template");
|
||||
|
||||
case TemplateName::DependentTemplate: {
|
||||
DependentTemplateName *DTN = Name.getAsDependentTemplateName();
|
||||
|
@ -7620,6 +7626,13 @@ ASTContext::getOverloadedTemplateName(UnresolvedSetIterator Begin,
|
|||
return TemplateName(OT);
|
||||
}
|
||||
|
||||
/// Retrieve a template name representing an unqualified-id that has been
|
||||
/// assumed to name a template for ADL purposes.
|
||||
TemplateName ASTContext::getAssumedTemplateName(DeclarationName Name) const {
|
||||
auto *OT = new (*this) AssumedTemplateStorage(Name);
|
||||
return TemplateName(OT);
|
||||
}
|
||||
|
||||
/// Retrieve the template name that represents a qualified
|
||||
/// template name such as \c std::vector.
|
||||
TemplateName
|
||||
|
|
|
@ -8126,6 +8126,14 @@ Expected<TemplateName> ASTImporter::Import_New(TemplateName From) {
|
|||
ToTemplates.end());
|
||||
}
|
||||
|
||||
case TemplateName::AssumedTemplate: {
|
||||
AssumedTemplateStorage *FromStorage = From.getAsAssumedTemplateName();
|
||||
auto DeclNameOrErr = Import_New(FromStorage->getDeclName());
|
||||
if (!DeclNameOrErr)
|
||||
return DeclNameOrErr.takeError();
|
||||
return ToContext.getAssumedTemplateName(*DeclNameOrErr);
|
||||
}
|
||||
|
||||
case TemplateName::QualifiedTemplate: {
|
||||
QualifiedTemplateName *QTN = From.getAsQualifiedTemplateName();
|
||||
auto QualifierOrErr = Import_New(QTN->getQualifier());
|
||||
|
|
|
@ -180,6 +180,12 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
|
|||
return I1 == E1 && I2 == E2;
|
||||
}
|
||||
|
||||
case TemplateName::AssumedTemplate: {
|
||||
AssumedTemplateStorage *TN1 = N1.getAsAssumedTemplateName(),
|
||||
*TN2 = N1.getAsAssumedTemplateName();
|
||||
return TN1->getDeclName() == TN2->getDeclName();
|
||||
}
|
||||
|
||||
case TemplateName::QualifiedTemplate: {
|
||||
QualifiedTemplateName *QN1 = N1.getAsQualifiedTemplateName(),
|
||||
*QN2 = N2.getAsQualifiedTemplateName();
|
||||
|
|
|
@ -1891,6 +1891,7 @@ void CXXNameMangler::mangleType(TemplateName TN) {
|
|||
break;
|
||||
|
||||
case TemplateName::OverloadedTemplate:
|
||||
case TemplateName::AssumedTemplate:
|
||||
llvm_unreachable("can't mangle an overloaded template name as a <type>");
|
||||
|
||||
case TemplateName::DependentTemplate: {
|
||||
|
@ -2030,6 +2031,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty,
|
|||
}
|
||||
|
||||
case TemplateName::OverloadedTemplate:
|
||||
case TemplateName::AssumedTemplate:
|
||||
case TemplateName::DependentTemplate:
|
||||
llvm_unreachable("invalid base for a template specialization type");
|
||||
|
||||
|
|
|
@ -140,6 +140,7 @@ void ODRHash::AddTemplateName(TemplateName Name) {
|
|||
break;
|
||||
// TODO: Support these cases.
|
||||
case TemplateName::OverloadedTemplate:
|
||||
case TemplateName::AssumedTemplate:
|
||||
case TemplateName::QualifiedTemplate:
|
||||
case TemplateName::DependentTemplate:
|
||||
case TemplateName::SubstTemplateTemplateParm:
|
||||
|
|
|
@ -66,6 +66,8 @@ TemplateName::TemplateName(void *Ptr) {
|
|||
TemplateName::TemplateName(TemplateDecl *Template) : Storage(Template) {}
|
||||
TemplateName::TemplateName(OverloadedTemplateStorage *Storage)
|
||||
: Storage(Storage) {}
|
||||
TemplateName::TemplateName(AssumedTemplateStorage *Storage)
|
||||
: Storage(Storage) {}
|
||||
TemplateName::TemplateName(SubstTemplateTemplateParmStorage *Storage)
|
||||
: Storage(Storage) {}
|
||||
TemplateName::TemplateName(SubstTemplateTemplateParmPackStorage *Storage)
|
||||
|
@ -87,6 +89,8 @@ TemplateName::NameKind TemplateName::getKind() const {
|
|||
= Storage.get<UncommonTemplateNameStorage*>();
|
||||
if (uncommon->getAsOverloadedStorage())
|
||||
return OverloadedTemplate;
|
||||
if (uncommon->getAsAssumedTemplateName())
|
||||
return AssumedTemplate;
|
||||
if (uncommon->getAsSubstTemplateTemplateParm())
|
||||
return SubstTemplateTemplateParm;
|
||||
return SubstTemplateTemplateParmPack;
|
||||
|
@ -113,6 +117,14 @@ OverloadedTemplateStorage *TemplateName::getAsOverloadedTemplate() const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
AssumedTemplateStorage *TemplateName::getAsAssumedTemplateName() const {
|
||||
if (UncommonTemplateNameStorage *Uncommon =
|
||||
Storage.dyn_cast<UncommonTemplateNameStorage *>())
|
||||
return Uncommon->getAsAssumedTemplateName();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SubstTemplateTemplateParmStorage *
|
||||
TemplateName::getAsSubstTemplateTemplateParm() const {
|
||||
if (UncommonTemplateNameStorage *uncommon =
|
||||
|
@ -230,7 +242,9 @@ TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy,
|
|||
} else if (SubstTemplateTemplateParmPackStorage *SubstPack
|
||||
= getAsSubstTemplateTemplateParmPack())
|
||||
OS << *SubstPack->getParameterPack();
|
||||
else {
|
||||
else if (AssumedTemplateStorage *Assumed = getAsAssumedTemplateName()) {
|
||||
Assumed->getDeclName().print(OS, Policy);
|
||||
} else {
|
||||
OverloadedTemplateStorage *OTS = getAsOverloadedTemplate();
|
||||
(*OTS->begin())->printName(OS);
|
||||
}
|
||||
|
|
|
@ -2916,6 +2916,7 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS,
|
|||
case Sema::NC_Expression:
|
||||
case Sema::NC_VarTemplate:
|
||||
case Sema::NC_FunctionTemplate:
|
||||
case Sema::NC_UndeclaredTemplate:
|
||||
// Might be a redeclaration of a prior entity.
|
||||
break;
|
||||
}
|
||||
|
@ -3386,7 +3387,8 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
|
|||
// type-name
|
||||
case tok::annot_template_id: {
|
||||
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
|
||||
if (TemplateId->Kind != TNK_Type_template) {
|
||||
if (TemplateId->Kind != TNK_Type_template &&
|
||||
TemplateId->Kind != TNK_Undeclared_template) {
|
||||
// This template-id does not refer to a type name, so we're
|
||||
// done with the type-specifiers.
|
||||
goto DoneWithDeclSpec;
|
||||
|
|
|
@ -597,10 +597,11 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
|
|||
|
||||
// Parse nested-name-specifier.
|
||||
IdentifierInfo *LastII = nullptr;
|
||||
ParseOptionalCXXScopeSpecifier(D.SS, nullptr, /*EnteringContext=*/false,
|
||||
/*MayBePseudoDtor=*/nullptr,
|
||||
/*IsTypename=*/false,
|
||||
/*LastII=*/&LastII);
|
||||
if (ParseOptionalCXXScopeSpecifier(D.SS, nullptr, /*EnteringContext=*/false,
|
||||
/*MayBePseudoDtor=*/nullptr,
|
||||
/*IsTypename=*/false,
|
||||
/*LastII=*/&LastII))
|
||||
return true;
|
||||
if (D.SS.isInvalid())
|
||||
return true;
|
||||
|
||||
|
@ -1111,7 +1112,8 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
|
|||
|
||||
// Parse optional nested-name-specifier
|
||||
CXXScopeSpec SS;
|
||||
ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false);
|
||||
if (ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false))
|
||||
return true;
|
||||
|
||||
BaseLoc = Tok.getLocation();
|
||||
|
||||
|
@ -1135,7 +1137,8 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
|
|||
if (Tok.is(tok::annot_template_id)) {
|
||||
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
|
||||
if (TemplateId->Kind == TNK_Type_template ||
|
||||
TemplateId->Kind == TNK_Dependent_template_name) {
|
||||
TemplateId->Kind == TNK_Dependent_template_name ||
|
||||
TemplateId->Kind == TNK_Undeclared_template) {
|
||||
AnnotateTemplateIdTokenAsType(/*IsClassName*/true);
|
||||
|
||||
assert(Tok.is(tok::annot_typename) && "template-id -> type failed");
|
||||
|
@ -1554,6 +1557,36 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
|
|||
|
||||
TemplateParameterLists *TemplateParams = TemplateInfo.TemplateParams;
|
||||
|
||||
auto RecoverFromUndeclaredTemplateName = [&](IdentifierInfo *Name,
|
||||
SourceLocation NameLoc,
|
||||
SourceRange TemplateArgRange,
|
||||
bool KnownUndeclared) {
|
||||
Diag(NameLoc, diag::err_explicit_spec_non_template)
|
||||
<< (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation)
|
||||
<< TagTokKind << Name << TemplateArgRange << KnownUndeclared;
|
||||
|
||||
// Strip off the last template parameter list if it was empty, since
|
||||
// we've removed its template argument list.
|
||||
if (TemplateParams && TemplateInfo.LastParameterListWasEmpty) {
|
||||
if (TemplateParams->size() > 1) {
|
||||
TemplateParams->pop_back();
|
||||
} else {
|
||||
TemplateParams = nullptr;
|
||||
const_cast<ParsedTemplateInfo &>(TemplateInfo).Kind =
|
||||
ParsedTemplateInfo::NonTemplate;
|
||||
}
|
||||
} else if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) {
|
||||
// Pretend this is just a forward declaration.
|
||||
TemplateParams = nullptr;
|
||||
const_cast<ParsedTemplateInfo &>(TemplateInfo).Kind =
|
||||
ParsedTemplateInfo::NonTemplate;
|
||||
const_cast<ParsedTemplateInfo &>(TemplateInfo).TemplateLoc =
|
||||
SourceLocation();
|
||||
const_cast<ParsedTemplateInfo &>(TemplateInfo).ExternLoc =
|
||||
SourceLocation();
|
||||
}
|
||||
};
|
||||
|
||||
// Parse the (optional) class name or simple-template-id.
|
||||
IdentifierInfo *Name = nullptr;
|
||||
SourceLocation NameLoc;
|
||||
|
@ -1574,38 +1607,26 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
|
|||
// try to give any location information for the list.
|
||||
LAngleLoc = RAngleLoc = SourceLocation();
|
||||
}
|
||||
|
||||
Diag(NameLoc, diag::err_explicit_spec_non_template)
|
||||
<< (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation)
|
||||
<< TagTokKind << Name << SourceRange(LAngleLoc, RAngleLoc);
|
||||
|
||||
// Strip off the last template parameter list if it was empty, since
|
||||
// we've removed its template argument list.
|
||||
if (TemplateParams && TemplateInfo.LastParameterListWasEmpty) {
|
||||
if (TemplateParams->size() > 1) {
|
||||
TemplateParams->pop_back();
|
||||
} else {
|
||||
TemplateParams = nullptr;
|
||||
const_cast<ParsedTemplateInfo&>(TemplateInfo).Kind
|
||||
= ParsedTemplateInfo::NonTemplate;
|
||||
}
|
||||
} else if (TemplateInfo.Kind
|
||||
== ParsedTemplateInfo::ExplicitInstantiation) {
|
||||
// Pretend this is just a forward declaration.
|
||||
TemplateParams = nullptr;
|
||||
const_cast<ParsedTemplateInfo&>(TemplateInfo).Kind
|
||||
= ParsedTemplateInfo::NonTemplate;
|
||||
const_cast<ParsedTemplateInfo&>(TemplateInfo).TemplateLoc
|
||||
= SourceLocation();
|
||||
const_cast<ParsedTemplateInfo&>(TemplateInfo).ExternLoc
|
||||
= SourceLocation();
|
||||
}
|
||||
RecoverFromUndeclaredTemplateName(
|
||||
Name, NameLoc, SourceRange(LAngleLoc, RAngleLoc), false);
|
||||
}
|
||||
} else if (Tok.is(tok::annot_template_id)) {
|
||||
TemplateId = takeTemplateIdAnnotation(Tok);
|
||||
NameLoc = ConsumeAnnotationToken();
|
||||
|
||||
if (TemplateId->Kind != TNK_Type_template &&
|
||||
if (TemplateId->Kind == TNK_Undeclared_template) {
|
||||
// Try to resolve the template name to a type template.
|
||||
Actions.ActOnUndeclaredTypeTemplateName(getCurScope(), TemplateId->Template,
|
||||
TemplateId->Kind, NameLoc, Name);
|
||||
if (TemplateId->Kind == TNK_Undeclared_template) {
|
||||
RecoverFromUndeclaredTemplateName(
|
||||
Name, NameLoc,
|
||||
SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc), true);
|
||||
TemplateId = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (TemplateId && TemplateId->Kind != TNK_Type_template &&
|
||||
TemplateId->Kind != TNK_Dependent_template_name) {
|
||||
// The template-name in the simple-template-id refers to
|
||||
// something other than a class template. Give an appropriate
|
||||
|
@ -1616,7 +1637,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
|
|||
|
||||
// FIXME: Name may be null here.
|
||||
Diag(TemplateId->LAngleLoc, diag::err_template_spec_syntax_non_template)
|
||||
<< TemplateId->Name << static_cast<int>(TemplateId->Kind) << Range;
|
||||
<< TemplateId->Name << static_cast<int>(TemplateId->Kind) << Range;
|
||||
|
||||
DS.SetTypeSpecError();
|
||||
SkipUntil(tok::semi, StopBeforeMatch);
|
||||
|
@ -3451,7 +3472,8 @@ void Parser::ParseConstructorInitializer(Decl *ConstructorDecl) {
|
|||
MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) {
|
||||
// parse '::'[opt] nested-name-specifier[opt]
|
||||
CXXScopeSpec SS;
|
||||
ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false);
|
||||
if (ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false))
|
||||
return true;
|
||||
|
||||
// : identifier
|
||||
IdentifierInfo *II = nullptr;
|
||||
|
@ -3477,11 +3499,14 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) {
|
|||
? takeTemplateIdAnnotation(Tok)
|
||||
: nullptr;
|
||||
if (TemplateId && (TemplateId->Kind == TNK_Type_template ||
|
||||
TemplateId->Kind == TNK_Dependent_template_name)) {
|
||||
TemplateId->Kind == TNK_Dependent_template_name ||
|
||||
TemplateId->Kind == TNK_Undeclared_template)) {
|
||||
AnnotateTemplateIdTokenAsType(/*IsClassName*/true);
|
||||
assert(Tok.is(tok::annot_typename) && "template-id -> type failed");
|
||||
TemplateTypeTy = getTypeAnnotation(Tok);
|
||||
ConsumeAnnotationToken();
|
||||
if (!TemplateTypeTy)
|
||||
return true;
|
||||
} else {
|
||||
Diag(Tok, diag::err_expected_member_or_base_name);
|
||||
return true;
|
||||
|
|
|
@ -487,6 +487,14 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
|
|||
EnteringContext,
|
||||
Template,
|
||||
MemberOfUnknownSpecialization)) {
|
||||
// If lookup didn't find anything, we treat the name as a template-name
|
||||
// anyway. C++20 requires this, and in prior language modes it improves
|
||||
// error recovery. But before we commit to this, check that we actually
|
||||
// have something that looks like a template-argument-list next.
|
||||
if (!IsTypename && TNK == TNK_Undeclared_template &&
|
||||
isTemplateArgumentList(1) == TPResult::False)
|
||||
break;
|
||||
|
||||
// We have found a template name, so annotate this token
|
||||
// with a template-id annotation. We do not permit the
|
||||
// template-id to be translated into a type annotation,
|
||||
|
@ -501,7 +509,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
|
|||
}
|
||||
|
||||
if (MemberOfUnknownSpecialization && (ObjectType || SS.isSet()) &&
|
||||
(IsTypename || IsTemplateArgumentList(1))) {
|
||||
(IsTypename || isTemplateArgumentList(1) == TPResult::True)) {
|
||||
// We have something like t::getAs<T>, where getAs is a
|
||||
// member of an unknown specialization. However, this will only
|
||||
// parse correctly as a template, so suggest the keyword 'template'
|
||||
|
@ -2138,9 +2146,15 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
|
|||
TemplateKWLoc.isValid(), Id,
|
||||
ObjectType, EnteringContext, Template,
|
||||
MemberOfUnknownSpecialization);
|
||||
// If lookup found nothing but we're assuming that this is a template
|
||||
// name, double-check that makes sense syntactically before committing
|
||||
// to it.
|
||||
if (TNK == TNK_Undeclared_template &&
|
||||
isTemplateArgumentList(0) == TPResult::False)
|
||||
return false;
|
||||
|
||||
if (TNK == TNK_Non_template && MemberOfUnknownSpecialization &&
|
||||
ObjectType && IsTemplateArgumentList()) {
|
||||
ObjectType && isTemplateArgumentList(0) == TPResult::True) {
|
||||
// We have something like t->getAs<T>(), where getAs is a
|
||||
// member of an unknown specialization. However, this will only
|
||||
// parse correctly as a template, so suggest the keyword 'template'
|
||||
|
@ -2244,11 +2258,9 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
|
|||
ASTTemplateArgsPtr TemplateArgsPtr(TemplateArgs);
|
||||
|
||||
// Constructor and destructor names.
|
||||
TypeResult Type
|
||||
= Actions.ActOnTemplateIdType(SS, TemplateKWLoc,
|
||||
Template, Name, NameLoc,
|
||||
LAngleLoc, TemplateArgsPtr, RAngleLoc,
|
||||
/*IsCtorOrDtorName=*/true);
|
||||
TypeResult Type = Actions.ActOnTemplateIdType(
|
||||
getCurScope(), SS, TemplateKWLoc, Template, Name, NameLoc, LAngleLoc,
|
||||
TemplateArgsPtr, RAngleLoc, /*IsCtorOrDtorName=*/true);
|
||||
if (Type.isInvalid())
|
||||
return true;
|
||||
|
||||
|
|
|
@ -1031,6 +1031,8 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
|
|||
// If we failed to parse the template ID but skipped ahead to a >, we're not
|
||||
// going to be able to form a token annotation. Eat the '>' if present.
|
||||
TryConsumeToken(tok::greater);
|
||||
// FIXME: Annotate the token stream so we don't produce the same errors
|
||||
// again if we're doing this annotation as part of a tentative parse.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1039,13 +1041,15 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
|
|||
// Build the annotation token.
|
||||
if (TNK == TNK_Type_template && AllowTypeAnnotation) {
|
||||
TypeResult Type = Actions.ActOnTemplateIdType(
|
||||
SS, TemplateKWLoc, Template, TemplateName.Identifier,
|
||||
getCurScope(), SS, TemplateKWLoc, Template, TemplateName.Identifier,
|
||||
TemplateNameLoc, LAngleLoc, TemplateArgsPtr, RAngleLoc);
|
||||
if (Type.isInvalid()) {
|
||||
// If we failed to parse the template ID but skipped ahead to a >, we're
|
||||
// not going to be able to form a token annotation. Eat the '>' if
|
||||
// present.
|
||||
TryConsumeToken(tok::greater);
|
||||
// FIXME: Annotate the token stream so we don't produce the same errors
|
||||
// again if we're doing this annotation as part of a tentative parse.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1108,14 +1112,16 @@ void Parser::AnnotateTemplateIdTokenAsType(bool IsClassName) {
|
|||
|
||||
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
|
||||
assert((TemplateId->Kind == TNK_Type_template ||
|
||||
TemplateId->Kind == TNK_Dependent_template_name) &&
|
||||
TemplateId->Kind == TNK_Dependent_template_name ||
|
||||
TemplateId->Kind == TNK_Undeclared_template) &&
|
||||
"Only works for type and dependent templates");
|
||||
|
||||
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
|
||||
TemplateId->NumArgs);
|
||||
|
||||
TypeResult Type
|
||||
= Actions.ActOnTemplateIdType(TemplateId->SS,
|
||||
= Actions.ActOnTemplateIdType(getCurScope(),
|
||||
TemplateId->SS,
|
||||
TemplateId->TemplateKWLoc,
|
||||
TemplateId->Template,
|
||||
TemplateId->Name,
|
||||
|
@ -1272,36 +1278,6 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() {
|
|||
ExprArg.get(), Loc);
|
||||
}
|
||||
|
||||
/// Determine whether the current tokens can only be parsed as a
|
||||
/// template argument list (starting with the '<') and never as a '<'
|
||||
/// expression.
|
||||
bool Parser::IsTemplateArgumentList(unsigned Skip) {
|
||||
struct AlwaysRevertAction : TentativeParsingAction {
|
||||
AlwaysRevertAction(Parser &P) : TentativeParsingAction(P) { }
|
||||
~AlwaysRevertAction() { Revert(); }
|
||||
} Tentative(*this);
|
||||
|
||||
while (Skip) {
|
||||
ConsumeAnyToken();
|
||||
--Skip;
|
||||
}
|
||||
|
||||
// '<'
|
||||
if (!TryConsumeToken(tok::less))
|
||||
return false;
|
||||
|
||||
// An empty template argument list.
|
||||
if (Tok.is(tok::greater))
|
||||
return true;
|
||||
|
||||
// See whether we have declaration specifiers, which indicate a type.
|
||||
while (isCXXDeclarationSpecifier() == TPResult::True)
|
||||
ConsumeAnyToken();
|
||||
|
||||
// If we have a '>' or a ',' then this is a template argument list.
|
||||
return Tok.isOneOf(tok::greater, tok::comma);
|
||||
}
|
||||
|
||||
/// ParseTemplateArgumentList - Parse a C++ template-argument-list
|
||||
/// (C++ [temp.names]). Returns true if there was an error.
|
||||
///
|
||||
|
|
|
@ -1178,12 +1178,17 @@ public:
|
|||
/// be either a decl-specifier or a function-style cast, and TPResult::Error
|
||||
/// if a parsing error was found and reported.
|
||||
///
|
||||
/// If HasMissingTypename is provided, a name with a dependent scope specifier
|
||||
/// will be treated as ambiguous if the 'typename' keyword is missing. If this
|
||||
/// happens, *HasMissingTypename will be set to 'true'. This will also be used
|
||||
/// as an indicator that undeclared identifiers (which will trigger a later
|
||||
/// parse error) should be treated as types. Returns TPResult::Ambiguous in
|
||||
/// such cases.
|
||||
/// If InvalidAsDeclSpec is not null, some cases that would be ill-formed as
|
||||
/// declaration specifiers but possibly valid as some other kind of construct
|
||||
/// return TPResult::Ambiguous instead of TPResult::False. When this happens,
|
||||
/// the intent is to keep trying to disambiguate, on the basis that we might
|
||||
/// find a better reason to treat this construct as a declaration later on.
|
||||
/// When this happens and the name could possibly be valid in some other
|
||||
/// syntactic context, *InvalidAsDeclSpec is set to 'true'. The current cases
|
||||
/// that trigger this are:
|
||||
///
|
||||
/// * When parsing X::Y (with no 'typename') where X is dependent
|
||||
/// * When parsing X<Y> where X is undeclared
|
||||
///
|
||||
/// decl-specifier:
|
||||
/// storage-class-specifier
|
||||
|
@ -1281,7 +1286,7 @@ public:
|
|||
///
|
||||
Parser::TPResult
|
||||
Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
||||
bool *HasMissingTypename) {
|
||||
bool *InvalidAsDeclSpec) {
|
||||
switch (Tok.getKind()) {
|
||||
case tok::identifier: {
|
||||
// Check for need to substitute AltiVec __vector keyword
|
||||
|
@ -1321,7 +1326,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
// argument is an error, and was probably intended to be a type.
|
||||
return GreaterThanIsOperator ? TPResult::True : TPResult::False;
|
||||
case ANK_Unresolved:
|
||||
return HasMissingTypename ? TPResult::Ambiguous : TPResult::False;
|
||||
return InvalidAsDeclSpec ? TPResult::Ambiguous : TPResult::False;
|
||||
case ANK_Success:
|
||||
break;
|
||||
}
|
||||
|
@ -1342,7 +1347,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
}
|
||||
|
||||
// We annotated this token as something. Recurse to handle whatever we got.
|
||||
return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename);
|
||||
return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
|
||||
}
|
||||
|
||||
case tok::kw_typename: // typename T::type
|
||||
|
@ -1350,7 +1355,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
// recurse to handle whatever we get.
|
||||
if (TryAnnotateTypeOrScopeToken())
|
||||
return TPResult::Error;
|
||||
return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename);
|
||||
return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
|
||||
|
||||
case tok::coloncolon: { // ::foo::bar
|
||||
const Token &Next = NextToken();
|
||||
|
@ -1365,7 +1370,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
// recurse to handle whatever we get.
|
||||
if (TryAnnotateTypeOrScopeToken())
|
||||
return TPResult::Error;
|
||||
return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename);
|
||||
return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
|
||||
|
||||
// decl-specifier:
|
||||
// storage-class-specifier
|
||||
|
@ -1471,6 +1476,16 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
|
||||
case tok::annot_template_id: {
|
||||
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
|
||||
// If lookup for the template-name found nothing, don't assume we have a
|
||||
// definitive disambiguation result yet.
|
||||
if (TemplateId->Kind == TNK_Undeclared_template && InvalidAsDeclSpec) {
|
||||
// 'template-id(' can be a valid expression but not a valid decl spec if
|
||||
// the template-name is not declared, but we don't consider this to be a
|
||||
// definitive disambiguation. In any other context, it's an error either
|
||||
// way.
|
||||
*InvalidAsDeclSpec = NextToken().is(tok::l_paren);
|
||||
return TPResult::Ambiguous;
|
||||
}
|
||||
if (TemplateId->Kind != TNK_Type_template)
|
||||
return TPResult::False;
|
||||
CXXScopeSpec SS;
|
||||
|
@ -1499,19 +1514,19 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
TPResult TPR = TPResult::False;
|
||||
if (!isIdentifier)
|
||||
TPR = isCXXDeclarationSpecifier(BracedCastResult,
|
||||
HasMissingTypename);
|
||||
InvalidAsDeclSpec);
|
||||
|
||||
if (isIdentifier ||
|
||||
TPR == TPResult::True || TPR == TPResult::Error)
|
||||
return TPResult::Error;
|
||||
|
||||
if (HasMissingTypename) {
|
||||
if (InvalidAsDeclSpec) {
|
||||
// We can't tell whether this is a missing 'typename' or a valid
|
||||
// expression.
|
||||
*HasMissingTypename = true;
|
||||
*InvalidAsDeclSpec = true;
|
||||
return TPResult::Ambiguous;
|
||||
} else {
|
||||
// In MS mode, if HasMissingTypename is not provided, and the tokens
|
||||
// In MS mode, if InvalidAsDeclSpec is not provided, and the tokens
|
||||
// are or the form *) or &) *> or &> &&>, this can't be an expression.
|
||||
// The typename must be missing.
|
||||
if (getLangOpts().MSVCCompat) {
|
||||
|
@ -1547,8 +1562,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
? TPResult::True
|
||||
: TPResult::False;
|
||||
case ANK_Unresolved:
|
||||
return HasMissingTypename ? TPResult::Ambiguous
|
||||
: TPResult::False;
|
||||
return InvalidAsDeclSpec ? TPResult::Ambiguous : TPResult::False;
|
||||
case ANK_Success:
|
||||
break;
|
||||
}
|
||||
|
@ -1556,8 +1570,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
// Annotated it, check again.
|
||||
assert(Tok.isNot(tok::annot_cxxscope) ||
|
||||
NextToken().isNot(tok::identifier));
|
||||
return isCXXDeclarationSpecifier(BracedCastResult,
|
||||
HasMissingTypename);
|
||||
return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
|
||||
}
|
||||
}
|
||||
return TPResult::False;
|
||||
|
@ -2029,3 +2042,56 @@ Parser::TPResult Parser::TryParseBracketDeclarator() {
|
|||
|
||||
return TPResult::Ambiguous;
|
||||
}
|
||||
|
||||
/// Determine whether we might be looking at the '<' template-argument-list '>'
|
||||
/// of a template-id or simple-template-id, rather than a less-than comparison.
|
||||
/// This will often fail and produce an ambiguity, but should never be wrong
|
||||
/// if it returns True or False.
|
||||
Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) {
|
||||
if (!TokensToSkip) {
|
||||
if (Tok.isNot(tok::less))
|
||||
return TPResult::False;
|
||||
if (NextToken().is(tok::greater))
|
||||
return TPResult::True;
|
||||
}
|
||||
|
||||
RevertingTentativeParsingAction PA(*this);
|
||||
|
||||
while (TokensToSkip) {
|
||||
ConsumeAnyToken();
|
||||
--TokensToSkip;
|
||||
}
|
||||
|
||||
if (!TryConsumeToken(tok::less))
|
||||
return TPResult::False;
|
||||
|
||||
bool InvalidAsTemplateArgumentList = false;
|
||||
while (true) {
|
||||
// We can't do much to tell an expression apart from a template-argument,
|
||||
// but one good distinguishing factor is that a "decl-specifier" not
|
||||
// followed by '(' or '{' can't appear in an expression.
|
||||
if (isCXXDeclarationSpecifier(
|
||||
TPResult::False, &InvalidAsTemplateArgumentList) == TPResult::True)
|
||||
return TPResult::True;
|
||||
|
||||
// That didn't help, try the next template-argument.
|
||||
SkipUntil({tok::comma, tok::greater, tok::greatergreater,
|
||||
tok::greatergreatergreater},
|
||||
StopAtSemi | StopBeforeMatch);
|
||||
switch (Tok.getKind()) {
|
||||
case tok::comma:
|
||||
ConsumeToken();
|
||||
break;
|
||||
|
||||
case tok::greater:
|
||||
case tok::greatergreater:
|
||||
case tok::greatergreatergreater:
|
||||
if (InvalidAsTemplateArgumentList)
|
||||
return TPResult::False;
|
||||
return TPResult::Ambiguous;
|
||||
|
||||
default:
|
||||
return TPResult::False;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -315,6 +315,14 @@ bool Parser::SkipUntil(ArrayRef<tok::TokenKind> Toks, SkipUntilFlags Flags) {
|
|||
else
|
||||
SkipUntil(tok::r_brace);
|
||||
break;
|
||||
case tok::question:
|
||||
// Recursively skip ? ... : pairs; these function as brackets. But
|
||||
// still stop at a semicolon if requested.
|
||||
ConsumeToken();
|
||||
SkipUntil(tok::colon,
|
||||
SkipUntilFlags(unsigned(Flags) &
|
||||
unsigned(StopAtCodeCompletion | StopAtSemi)));
|
||||
break;
|
||||
|
||||
// Okay, we found a ']' or '}' or ')', which we think should be balanced.
|
||||
// Since the user wasn't looking for this token (if they were, it would
|
||||
|
@ -1600,6 +1608,20 @@ Parser::TryAnnotateName(bool IsAddressOfOperand,
|
|||
Actions.ClassifyName(getCurScope(), SS, Name, NameLoc, Next,
|
||||
IsAddressOfOperand, SS.isEmpty() ? CCC : nullptr);
|
||||
|
||||
// If name lookup found nothing and we guessed that this was a template name,
|
||||
// double-check before committing to that interpretation. C++20 requires that
|
||||
// we interpret this as a template-id if it can be, but if it can't be, then
|
||||
// this is an error recovery case.
|
||||
if (Classification.getKind() == Sema::NC_UndeclaredTemplate &&
|
||||
isTemplateArgumentList(1) == TPResult::False) {
|
||||
// It's not a template-id; re-classify without the '<' as a hint.
|
||||
Token FakeNext = Next;
|
||||
FakeNext.setKind(tok::unknown);
|
||||
Classification =
|
||||
Actions.ClassifyName(getCurScope(), SS, Name, NameLoc, FakeNext,
|
||||
IsAddressOfOperand, SS.isEmpty() ? CCC : nullptr);
|
||||
}
|
||||
|
||||
switch (Classification.getKind()) {
|
||||
case Sema::NC_Error:
|
||||
return ANK_Error;
|
||||
|
@ -1668,7 +1690,8 @@ Parser::TryAnnotateName(bool IsAddressOfOperand,
|
|||
}
|
||||
LLVM_FALLTHROUGH;
|
||||
case Sema::NC_VarTemplate:
|
||||
case Sema::NC_FunctionTemplate: {
|
||||
case Sema::NC_FunctionTemplate:
|
||||
case Sema::NC_UndeclaredTemplate: {
|
||||
// We have a type, variable or function template followed by '<'.
|
||||
ConsumeToken();
|
||||
UnqualifiedId Id;
|
||||
|
@ -1791,7 +1814,8 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
|
|||
} else if (Tok.is(tok::annot_template_id)) {
|
||||
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
|
||||
if (TemplateId->Kind != TNK_Type_template &&
|
||||
TemplateId->Kind != TNK_Dependent_template_name) {
|
||||
TemplateId->Kind != TNK_Dependent_template_name &&
|
||||
TemplateId->Kind != TNK_Undeclared_template) {
|
||||
Diag(Tok, diag::err_typename_refers_to_non_type_template)
|
||||
<< Tok.getAnnotationRange();
|
||||
return true;
|
||||
|
@ -1890,6 +1914,8 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS,
|
|||
}
|
||||
|
||||
// If this is a template-id, annotate with a template-id or type token.
|
||||
// FIXME: This appears to be dead code. We already have formed template-id
|
||||
// tokens when parsing the scope specifier; this can never form a new one.
|
||||
if (NextToken().is(tok::less)) {
|
||||
TemplateTy Template;
|
||||
UnqualifiedId TemplateName;
|
||||
|
@ -1900,14 +1926,19 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS,
|
|||
/*hasTemplateKeyword=*/false, TemplateName,
|
||||
/*ObjectType=*/nullptr, /*EnteringContext*/false, Template,
|
||||
MemberOfUnknownSpecialization)) {
|
||||
// Consume the identifier.
|
||||
ConsumeToken();
|
||||
if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(),
|
||||
TemplateName)) {
|
||||
// If an unrecoverable error occurred, we need to return true here,
|
||||
// because the token stream is in a damaged state. We may not return
|
||||
// a valid identifier.
|
||||
return true;
|
||||
// Only annotate an undeclared template name as a template-id if the
|
||||
// following tokens have the form of a template argument list.
|
||||
if (TNK != TNK_Undeclared_template ||
|
||||
isTemplateArgumentList(1) != TPResult::False) {
|
||||
// Consume the identifier.
|
||||
ConsumeToken();
|
||||
if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(),
|
||||
TemplateName)) {
|
||||
// If an unrecoverable error occurred, we need to return true here,
|
||||
// because the token stream is in a damaged state. We may not
|
||||
// return a valid identifier.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -888,7 +888,7 @@ bool Sema::IsInvalidUnlessNestedName(Scope *S, CXXScopeSpec &SS,
|
|||
bool Sema::ActOnCXXNestedNameSpecifier(Scope *S,
|
||||
CXXScopeSpec &SS,
|
||||
SourceLocation TemplateKWLoc,
|
||||
TemplateTy Template,
|
||||
TemplateTy OpaqueTemplate,
|
||||
SourceLocation TemplateNameLoc,
|
||||
SourceLocation LAngleLoc,
|
||||
ASTTemplateArgsPtr TemplateArgsIn,
|
||||
|
@ -898,11 +898,13 @@ bool Sema::ActOnCXXNestedNameSpecifier(Scope *S,
|
|||
if (SS.isInvalid())
|
||||
return true;
|
||||
|
||||
TemplateName Template = OpaqueTemplate.get();
|
||||
|
||||
// Translate the parser's template argument list in our AST format.
|
||||
TemplateArgumentListInfo TemplateArgs(LAngleLoc, RAngleLoc);
|
||||
translateTemplateArguments(TemplateArgsIn, TemplateArgs);
|
||||
|
||||
DependentTemplateName *DTN = Template.get().getAsDependentTemplateName();
|
||||
DependentTemplateName *DTN = Template.getAsDependentTemplateName();
|
||||
if (DTN && DTN->isIdentifier()) {
|
||||
// Handle a dependent template specialization for which we cannot resolve
|
||||
// the template name.
|
||||
|
@ -930,23 +932,28 @@ bool Sema::ActOnCXXNestedNameSpecifier(Scope *S,
|
|||
return false;
|
||||
}
|
||||
|
||||
TemplateDecl *TD = Template.get().getAsTemplateDecl();
|
||||
if (Template.get().getAsOverloadedTemplate() || DTN ||
|
||||
// If we assumed an undeclared identifier was a template name, try to
|
||||
// typo-correct it now.
|
||||
if (Template.getAsAssumedTemplateName() &&
|
||||
resolveAssumedTemplateNameAsType(S, Template, TemplateNameLoc))
|
||||
return true;
|
||||
|
||||
TemplateDecl *TD = Template.getAsTemplateDecl();
|
||||
if (Template.getAsOverloadedTemplate() || DTN ||
|
||||
isa<FunctionTemplateDecl>(TD) || isa<VarTemplateDecl>(TD)) {
|
||||
SourceRange R(TemplateNameLoc, RAngleLoc);
|
||||
if (SS.getRange().isValid())
|
||||
R.setBegin(SS.getRange().getBegin());
|
||||
|
||||
Diag(CCLoc, diag::err_non_type_template_in_nested_name_specifier)
|
||||
<< (TD && isa<VarTemplateDecl>(TD)) << Template.get() << R;
|
||||
NoteAllFoundTemplates(Template.get());
|
||||
<< (TD && isa<VarTemplateDecl>(TD)) << Template << R;
|
||||
NoteAllFoundTemplates(Template);
|
||||
return true;
|
||||
}
|
||||
|
||||
// We were able to resolve the template name to an actual template.
|
||||
// Build an appropriate nested-name-specifier.
|
||||
QualType T =
|
||||
CheckTemplateIdType(Template.get(), TemplateNameLoc, TemplateArgs);
|
||||
QualType T = CheckTemplateIdType(Template, TemplateNameLoc, TemplateArgs);
|
||||
if (T.isNull())
|
||||
return true;
|
||||
|
||||
|
@ -954,7 +961,7 @@ bool Sema::ActOnCXXNestedNameSpecifier(Scope *S,
|
|||
// nested name specifiers.
|
||||
if (!T->isDependentType() && !T->getAs<TagType>()) {
|
||||
Diag(TemplateNameLoc, diag::err_nested_name_spec_non_tag) << T;
|
||||
NoteAllFoundTemplates(Template.get());
|
||||
NoteAllFoundTemplates(Template);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -917,6 +917,16 @@ Corrected:
|
|||
}
|
||||
}
|
||||
|
||||
if (getLangOpts().CPlusPlus2a && !SS.isSet() && NextToken.is(tok::less)) {
|
||||
// In C++20 onwards, this could be an ADL-only call to a function
|
||||
// template, and we're required to assume that this is a template name.
|
||||
//
|
||||
// FIXME: Find a way to still do typo correction in this case.
|
||||
TemplateName Template =
|
||||
Context.getAssumedTemplateName(NameInfo.getName());
|
||||
return NameClassification::UndeclaredTemplate(Template);
|
||||
}
|
||||
|
||||
// In C, we first see whether there is a tag type by the same name, in
|
||||
// which case it's likely that the user just forgot to write "enum",
|
||||
// "struct", or "union".
|
||||
|
@ -1045,52 +1055,62 @@ Corrected:
|
|||
|
||||
if (getLangOpts().CPlusPlus && NextToken.is(tok::less) &&
|
||||
(IsFilteredTemplateName ||
|
||||
hasAnyAcceptableTemplateNames(Result, /*AllowFunctionTemplates=*/true,
|
||||
/*AllowDependent=*/false))) {
|
||||
hasAnyAcceptableTemplateNames(
|
||||
Result, /*AllowFunctionTemplates=*/true,
|
||||
/*AllowDependent=*/false,
|
||||
/*AllowNonTemplateFunctions*/ !SS.isSet() &&
|
||||
getLangOpts().CPlusPlus2a))) {
|
||||
// C++ [temp.names]p3:
|
||||
// After name lookup (3.4) finds that a name is a template-name or that
|
||||
// an operator-function-id or a literal- operator-id refers to a set of
|
||||
// overloaded functions any member of which is a function template if
|
||||
// this is followed by a <, the < is always taken as the delimiter of a
|
||||
// template-argument-list and never as the less-than operator.
|
||||
// C++2a [temp.names]p2:
|
||||
// A name is also considered to refer to a template if it is an
|
||||
// unqualified-id followed by a < and name lookup finds either one
|
||||
// or more functions or finds nothing.
|
||||
if (!IsFilteredTemplateName)
|
||||
FilterAcceptableTemplateNames(Result);
|
||||
|
||||
if (!Result.empty()) {
|
||||
bool IsFunctionTemplate;
|
||||
bool IsVarTemplate;
|
||||
TemplateName Template;
|
||||
if (Result.end() - Result.begin() > 1) {
|
||||
IsFunctionTemplate = true;
|
||||
Template = Context.getOverloadedTemplateName(Result.begin(),
|
||||
Result.end());
|
||||
} else {
|
||||
auto *TD = cast<TemplateDecl>(getAsTemplateNameDecl(
|
||||
*Result.begin(), /*AllowFunctionTemplates=*/true,
|
||||
/*AllowDependent=*/false));
|
||||
IsFunctionTemplate = isa<FunctionTemplateDecl>(TD);
|
||||
IsVarTemplate = isa<VarTemplateDecl>(TD);
|
||||
bool IsFunctionTemplate;
|
||||
bool IsVarTemplate;
|
||||
TemplateName Template;
|
||||
if (Result.end() - Result.begin() > 1) {
|
||||
IsFunctionTemplate = true;
|
||||
Template = Context.getOverloadedTemplateName(Result.begin(),
|
||||
Result.end());
|
||||
} else if (!Result.empty()) {
|
||||
auto *TD = cast<TemplateDecl>(getAsTemplateNameDecl(
|
||||
*Result.begin(), /*AllowFunctionTemplates=*/true,
|
||||
/*AllowDependent=*/false));
|
||||
IsFunctionTemplate = isa<FunctionTemplateDecl>(TD);
|
||||
IsVarTemplate = isa<VarTemplateDecl>(TD);
|
||||
|
||||
if (SS.isSet() && !SS.isInvalid())
|
||||
Template =
|
||||
Context.getQualifiedTemplateName(SS.getScopeRep(),
|
||||
/*TemplateKeyword=*/false, TD);
|
||||
else
|
||||
Template = TemplateName(TD);
|
||||
}
|
||||
|
||||
if (IsFunctionTemplate) {
|
||||
// Function templates always go through overload resolution, at which
|
||||
// point we'll perform the various checks (e.g., accessibility) we need
|
||||
// to based on which function we selected.
|
||||
Result.suppressDiagnostics();
|
||||
|
||||
return NameClassification::FunctionTemplate(Template);
|
||||
}
|
||||
|
||||
return IsVarTemplate ? NameClassification::VarTemplate(Template)
|
||||
: NameClassification::TypeTemplate(Template);
|
||||
if (SS.isSet() && !SS.isInvalid())
|
||||
Template =
|
||||
Context.getQualifiedTemplateName(SS.getScopeRep(),
|
||||
/*TemplateKeyword=*/false, TD);
|
||||
else
|
||||
Template = TemplateName(TD);
|
||||
} else {
|
||||
// All results were non-template functions. This is a function template
|
||||
// name.
|
||||
IsFunctionTemplate = true;
|
||||
Template = Context.getAssumedTemplateName(NameInfo.getName());
|
||||
}
|
||||
|
||||
if (IsFunctionTemplate) {
|
||||
// Function templates always go through overload resolution, at which
|
||||
// point we'll perform the various checks (e.g., accessibility) we need
|
||||
// to based on which function we selected.
|
||||
Result.suppressDiagnostics();
|
||||
|
||||
return NameClassification::FunctionTemplate(Template);
|
||||
}
|
||||
|
||||
return IsVarTemplate ? NameClassification::VarTemplate(Template)
|
||||
: NameClassification::TypeTemplate(Template);
|
||||
}
|
||||
|
||||
NamedDecl *FirstDecl = (*Result.begin())->getUnderlyingDecl();
|
||||
|
|
|
@ -3881,6 +3881,8 @@ Sema::BuildMemInitializer(Decl *ConstructorD,
|
|||
|
||||
if (TemplateTypeTy) {
|
||||
BaseType = GetTypeFromParser(TemplateTypeTy, &TInfo);
|
||||
if (BaseType.isNull())
|
||||
return true;
|
||||
} else if (DS.getTypeSpecType() == TST_decltype) {
|
||||
BaseType = BuildDecltypeType(DS.getRepAsExpr(), DS.getTypeSpecTypeLoc());
|
||||
} else if (DS.getTypeSpecType() == TST_decltype_auto) {
|
||||
|
|
|
@ -2065,11 +2065,12 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
|
|||
// is in the wrong place to recover. Suggest the typo
|
||||
// correction, but don't make it a fix-it since we're not going
|
||||
// to recover well anyway.
|
||||
AcceptableWithoutRecovery =
|
||||
isa<TypeDecl>(UnderlyingND) || isa<ObjCInterfaceDecl>(UnderlyingND);
|
||||
AcceptableWithoutRecovery = isa<TypeDecl>(UnderlyingND) ||
|
||||
getAsTypeTemplateDecl(UnderlyingND) ||
|
||||
isa<ObjCInterfaceDecl>(UnderlyingND);
|
||||
} else {
|
||||
// FIXME: We found a keyword or a type. Suggest it, but don't provide a
|
||||
// fix-it because we aren't able to recover.
|
||||
// FIXME: We found a keyword. Suggest it, but don't provide a fix-it
|
||||
// because we aren't able to recover.
|
||||
AcceptableWithoutRecovery = true;
|
||||
}
|
||||
|
||||
|
@ -2221,8 +2222,10 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
|
|||
// this becomes a performance hit, we can work harder to preserve those
|
||||
// results until we get here but it's likely not worth it.
|
||||
bool MemberOfUnknownSpecialization;
|
||||
AssumedTemplateKind AssumedTemplate;
|
||||
if (LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false,
|
||||
MemberOfUnknownSpecialization, TemplateKWLoc))
|
||||
MemberOfUnknownSpecialization, TemplateKWLoc,
|
||||
&AssumedTemplate))
|
||||
return ExprError();
|
||||
|
||||
if (MemberOfUnknownSpecialization ||
|
||||
|
@ -5518,7 +5521,24 @@ tryImplicitlyCaptureThisIfImplicitMemberFunctionAccessWithDependentArgs(
|
|||
ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
|
||||
MultiExprArg ArgExprs, SourceLocation RParenLoc,
|
||||
Expr *ExecConfig) {
|
||||
return BuildCallExpr(Scope, Fn, LParenLoc, ArgExprs, RParenLoc, ExecConfig);
|
||||
ExprResult Call =
|
||||
BuildCallExpr(Scope, Fn, LParenLoc, ArgExprs, RParenLoc, ExecConfig);
|
||||
if (Call.isInvalid())
|
||||
return Call;
|
||||
|
||||
// Diagnose uses of the C++20 "ADL-only template-id call" feature in earlier
|
||||
// language modes.
|
||||
if (auto *ULE = dyn_cast<UnresolvedLookupExpr>(Fn)) {
|
||||
if (ULE->hasExplicitTemplateArgs() &&
|
||||
ULE->decls_begin() == ULE->decls_end()) {
|
||||
Diag(Fn->getExprLoc(), getLangOpts().CPlusPlus2a
|
||||
? diag::warn_cxx17_compat_adl_only_template_id
|
||||
: diag::ext_adl_only_template_id)
|
||||
<< ULE->getName();
|
||||
}
|
||||
}
|
||||
|
||||
return Call;
|
||||
}
|
||||
|
||||
/// BuildCallExpr - Handle a call to Fn with the specified array of arguments.
|
||||
|
|
|
@ -7074,7 +7074,8 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base,
|
|||
TemplateIdAnnotation *TemplateId = SecondTypeName.TemplateId;
|
||||
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
|
||||
TemplateId->NumArgs);
|
||||
TypeResult T = ActOnTemplateIdType(TemplateId->SS,
|
||||
TypeResult T = ActOnTemplateIdType(S,
|
||||
TemplateId->SS,
|
||||
TemplateId->TemplateKWLoc,
|
||||
TemplateId->Template,
|
||||
TemplateId->Name,
|
||||
|
@ -7126,7 +7127,8 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base,
|
|||
TemplateIdAnnotation *TemplateId = FirstTypeName.TemplateId;
|
||||
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
|
||||
TemplateId->NumArgs);
|
||||
TypeResult T = ActOnTemplateIdType(TemplateId->SS,
|
||||
TypeResult T = ActOnTemplateIdType(S,
|
||||
TemplateId->SS,
|
||||
TemplateId->TemplateKWLoc,
|
||||
TemplateId->Template,
|
||||
TemplateId->Name,
|
||||
|
|
|
@ -12017,7 +12017,8 @@ bool Sema::buildOverloadedCallSet(Scope *S, Expr *Fn,
|
|||
// We don't perform ADL for implicit declarations of builtins.
|
||||
// Verify that this was correctly set up.
|
||||
FunctionDecl *F;
|
||||
if (ULE->decls_begin() + 1 == ULE->decls_end() &&
|
||||
if (ULE->decls_begin() != ULE->decls_end() &&
|
||||
ULE->decls_begin() + 1 == ULE->decls_end() &&
|
||||
(F = dyn_cast<FunctionDecl>(*ULE->decls_begin())) &&
|
||||
F->getBuiltinID() && F->isImplicit())
|
||||
llvm_unreachable("performing ADL for builtin");
|
||||
|
@ -12201,10 +12202,9 @@ ExprResult Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn,
|
|||
OverloadingResult OverloadResult =
|
||||
CandidateSet.BestViableFunction(*this, Fn->getBeginLoc(), Best);
|
||||
|
||||
return FinishOverloadedCallExpr(*this, S, Fn, ULE, LParenLoc, Args,
|
||||
RParenLoc, ExecConfig, &CandidateSet,
|
||||
&Best, OverloadResult,
|
||||
AllowTypoCorrection);
|
||||
return FinishOverloadedCallExpr(*this, S, Fn, ULE, LParenLoc, Args, RParenLoc,
|
||||
ExecConfig, &CandidateSet, &Best,
|
||||
OverloadResult, AllowTypoCorrection);
|
||||
}
|
||||
|
||||
static bool IsOverloaded(const UnresolvedSetImpl &Functions) {
|
||||
|
|
|
@ -130,10 +130,15 @@ void Sema::FilterAcceptableTemplateNames(LookupResult &R,
|
|||
|
||||
bool Sema::hasAnyAcceptableTemplateNames(LookupResult &R,
|
||||
bool AllowFunctionTemplates,
|
||||
bool AllowDependent) {
|
||||
for (LookupResult::iterator I = R.begin(), IEnd = R.end(); I != IEnd; ++I)
|
||||
bool AllowDependent,
|
||||
bool AllowNonTemplateFunctions) {
|
||||
for (LookupResult::iterator I = R.begin(), IEnd = R.end(); I != IEnd; ++I) {
|
||||
if (getAsTemplateNameDecl(*I, AllowFunctionTemplates, AllowDependent))
|
||||
return true;
|
||||
if (AllowNonTemplateFunctions &&
|
||||
isa<FunctionDecl>((*I)->getUnderlyingDecl()))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -171,11 +176,25 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
|
|||
|
||||
QualType ObjectType = ObjectTypePtr.get();
|
||||
|
||||
AssumedTemplateKind AssumedTemplate;
|
||||
LookupResult R(*this, TName, Name.getBeginLoc(), LookupOrdinaryName);
|
||||
if (LookupTemplateName(R, S, SS, ObjectType, EnteringContext,
|
||||
MemberOfUnknownSpecialization))
|
||||
MemberOfUnknownSpecialization, SourceLocation(),
|
||||
&AssumedTemplate))
|
||||
return TNK_Non_template;
|
||||
|
||||
if (AssumedTemplate != AssumedTemplateKind::None) {
|
||||
TemplateResult = TemplateTy::make(Context.getAssumedTemplateName(TName));
|
||||
// Let the parser know whether we found nothing or found functions; if we
|
||||
// found nothing, we want to more carefully check whether this is actually
|
||||
// a function template name versus some other kind of undeclared identifier.
|
||||
return AssumedTemplate == AssumedTemplateKind::FoundNothing
|
||||
? TNK_Undeclared_template
|
||||
: TNK_Function_template;
|
||||
}
|
||||
|
||||
if (R.empty())
|
||||
return TNK_Non_template;
|
||||
if (R.empty()) return TNK_Non_template;
|
||||
|
||||
NamedDecl *D = nullptr;
|
||||
if (R.isAmbiguous()) {
|
||||
|
@ -325,7 +344,11 @@ bool Sema::LookupTemplateName(LookupResult &Found,
|
|||
QualType ObjectType,
|
||||
bool EnteringContext,
|
||||
bool &MemberOfUnknownSpecialization,
|
||||
SourceLocation TemplateKWLoc) {
|
||||
SourceLocation TemplateKWLoc,
|
||||
AssumedTemplateKind *ATK) {
|
||||
if (ATK)
|
||||
*ATK = AssumedTemplateKind::None;
|
||||
|
||||
Found.setTemplateNameLookup(true);
|
||||
|
||||
// Determine where to perform name lookup
|
||||
|
@ -405,6 +428,32 @@ bool Sema::LookupTemplateName(LookupResult &Found,
|
|||
if (Found.isAmbiguous())
|
||||
return false;
|
||||
|
||||
if (ATK && !SS.isSet() && ObjectType.isNull() && TemplateKWLoc.isInvalid()) {
|
||||
// C++2a [temp.names]p2:
|
||||
// A name is also considered to refer to a template if it is an
|
||||
// unqualified-id followed by a < and name lookup finds either one or more
|
||||
// functions or finds nothing.
|
||||
//
|
||||
// To keep our behavior consistent, we apply the "finds nothing" part in
|
||||
// all language modes, and diagnose the empty lookup in ActOnCallExpr if we
|
||||
// successfully form a call to an undeclared template-id.
|
||||
bool AllFunctions =
|
||||
getLangOpts().CPlusPlus2a &&
|
||||
std::all_of(Found.begin(), Found.end(), [](NamedDecl *ND) {
|
||||
return isa<FunctionDecl>(ND->getUnderlyingDecl());
|
||||
});
|
||||
if (AllFunctions || (Found.empty() && !IsDependent)) {
|
||||
// If lookup found any functions, or if this is a name that can only be
|
||||
// used for a function, then strongly assume this is a function
|
||||
// template-id.
|
||||
*ATK = (Found.empty() && Found.getLookupName().isIdentifier())
|
||||
? AssumedTemplateKind::FoundNothing
|
||||
: AssumedTemplateKind::FoundFunctions;
|
||||
Found.clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Found.empty() && !IsDependent) {
|
||||
// If we did not find any names, attempt to correct any typos.
|
||||
DeclarationName Name = Found.getLookupName();
|
||||
|
@ -418,13 +467,13 @@ bool Sema::LookupTemplateName(LookupResult &Found,
|
|||
if (TypoCorrection Corrected =
|
||||
CorrectTypo(Found.getLookupNameInfo(), Found.getLookupKind(), S,
|
||||
&SS, FilterCCC, CTK_ErrorRecovery, LookupCtx)) {
|
||||
Found.setLookupName(Corrected.getCorrection());
|
||||
if (auto *ND = Corrected.getFoundDecl())
|
||||
Found.addDecl(ND);
|
||||
FilterAcceptableTemplateNames(Found);
|
||||
if (Found.isAmbiguous()) {
|
||||
Found.clear();
|
||||
} else if (!Found.empty()) {
|
||||
Found.setLookupName(Corrected.getCorrection());
|
||||
if (LookupCtx) {
|
||||
std::string CorrectedStr(Corrected.getAsString(getLangOpts()));
|
||||
bool DroppedSpecifier = Corrected.WillReplaceSpecifier() &&
|
||||
|
@ -436,8 +485,6 @@ bool Sema::LookupTemplateName(LookupResult &Found,
|
|||
diagnoseTypo(Corrected, PDiag(diag::err_no_template_suggest) << Name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Found.setLookupName(Name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3348,14 +3395,65 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
|
|||
return Context.getTemplateSpecializationType(Name, TemplateArgs, CanonType);
|
||||
}
|
||||
|
||||
TypeResult
|
||||
Sema::ActOnTemplateIdType(CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
|
||||
TemplateTy TemplateD, IdentifierInfo *TemplateII,
|
||||
SourceLocation TemplateIILoc,
|
||||
SourceLocation LAngleLoc,
|
||||
ASTTemplateArgsPtr TemplateArgsIn,
|
||||
SourceLocation RAngleLoc,
|
||||
bool IsCtorOrDtorName, bool IsClassName) {
|
||||
void Sema::ActOnUndeclaredTypeTemplateName(Scope *S, TemplateTy &ParsedName,
|
||||
TemplateNameKind &TNK,
|
||||
SourceLocation NameLoc,
|
||||
IdentifierInfo *&II) {
|
||||
assert(TNK == TNK_Undeclared_template && "not an undeclared template name");
|
||||
|
||||
TemplateName Name = ParsedName.get();
|
||||
auto *ATN = Name.getAsAssumedTemplateName();
|
||||
assert(ATN && "not an assumed template name");
|
||||
II = ATN->getDeclName().getAsIdentifierInfo();
|
||||
|
||||
if (!resolveAssumedTemplateNameAsType(S, Name, NameLoc, /*Diagnose*/false)) {
|
||||
// Resolved to a type template name.
|
||||
ParsedName = TemplateTy::make(Name);
|
||||
TNK = TNK_Type_template;
|
||||
}
|
||||
}
|
||||
|
||||
bool Sema::resolveAssumedTemplateNameAsType(Scope *S, TemplateName &Name,
|
||||
SourceLocation NameLoc,
|
||||
bool Diagnose) {
|
||||
// We assumed this undeclared identifier to be an (ADL-only) function
|
||||
// template name, but it was used in a context where a type was required.
|
||||
// Try to typo-correct it now.
|
||||
AssumedTemplateStorage *ATN = Name.getAsAssumedTemplateName();
|
||||
assert(ATN && "not an assumed template name");
|
||||
|
||||
LookupResult R(*this, ATN->getDeclName(), NameLoc, LookupOrdinaryName);
|
||||
struct CandidateCallback : CorrectionCandidateCallback {
|
||||
bool ValidateCandidate(const TypoCorrection &TC) override {
|
||||
return TC.getCorrectionDecl() &&
|
||||
getAsTypeTemplateDecl(TC.getCorrectionDecl());
|
||||
}
|
||||
std::unique_ptr<CorrectionCandidateCallback> clone() override {
|
||||
return llvm::make_unique<CandidateCallback>(*this);
|
||||
}
|
||||
} FilterCCC;
|
||||
|
||||
TypoCorrection Corrected =
|
||||
CorrectTypo(R.getLookupNameInfo(), R.getLookupKind(), S, nullptr,
|
||||
FilterCCC, CTK_ErrorRecovery);
|
||||
if (Corrected && Corrected.getFoundDecl()) {
|
||||
diagnoseTypo(Corrected, PDiag(diag::err_no_template_suggest)
|
||||
<< ATN->getDeclName());
|
||||
Name = TemplateName(Corrected.getCorrectionDeclAs<TemplateDecl>());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Diagnose)
|
||||
Diag(R.getNameLoc(), diag::err_no_template) << R.getLookupName();
|
||||
return true;
|
||||
}
|
||||
|
||||
TypeResult Sema::ActOnTemplateIdType(
|
||||
Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
|
||||
TemplateTy TemplateD, IdentifierInfo *TemplateII,
|
||||
SourceLocation TemplateIILoc, SourceLocation LAngleLoc,
|
||||
ASTTemplateArgsPtr TemplateArgsIn, SourceLocation RAngleLoc,
|
||||
bool IsCtorOrDtorName, bool IsClassName) {
|
||||
if (SS.isInvalid())
|
||||
return true;
|
||||
|
||||
|
@ -3396,6 +3494,9 @@ Sema::ActOnTemplateIdType(CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
|
|||
}
|
||||
|
||||
TemplateName Template = TemplateD.get();
|
||||
if (Template.getAsAssumedTemplateName() &&
|
||||
resolveAssumedTemplateNameAsType(S, Template, TemplateIILoc))
|
||||
return true;
|
||||
|
||||
// Translate the parser's template argument list in our AST format.
|
||||
TemplateArgumentListInfo TemplateArgs(LAngleLoc, RAngleLoc);
|
||||
|
@ -4141,7 +4242,6 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
|
|||
// vs template<class T, class U> void f(U);
|
||||
|
||||
// These should be filtered out by our callers.
|
||||
assert(!R.empty() && "empty lookup results when building templateid");
|
||||
assert(!R.isAmbiguous() && "ambiguous lookup when building templateid");
|
||||
|
||||
// Non-function templates require a template argument list.
|
||||
|
|
|
@ -8745,6 +8745,11 @@ ASTReader::ReadTemplateName(ModuleFile &F, const RecordData &Record,
|
|||
return Context.getOverloadedTemplateName(Decls.begin(), Decls.end());
|
||||
}
|
||||
|
||||
case TemplateName::AssumedTemplate: {
|
||||
DeclarationName Name = ReadDeclarationName(F, Record, Idx);
|
||||
return Context.getAssumedTemplateName(Name);
|
||||
}
|
||||
|
||||
case TemplateName::QualifiedTemplate: {
|
||||
NestedNameSpecifier *NNS = ReadNestedNameSpecifier(F, Record, Idx);
|
||||
bool hasTemplKeyword = Record[Idx++];
|
||||
|
|
|
@ -5877,6 +5877,12 @@ void ASTRecordWriter::AddTemplateName(TemplateName Name) {
|
|||
break;
|
||||
}
|
||||
|
||||
case TemplateName::AssumedTemplate: {
|
||||
AssumedTemplateStorage *ADLT = Name.getAsAssumedTemplateName();
|
||||
AddDeclarationName(ADLT->getDeclName());
|
||||
break;
|
||||
}
|
||||
|
||||
case TemplateName::QualifiedTemplate: {
|
||||
QualifiedTemplateName *QualT = Name.getAsQualifiedTemplateName();
|
||||
AddNestedNameSpecifier(QualT->getQualifier());
|
||||
|
|
|
@ -1,25 +1,67 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
// expected-no-diagnostics
|
||||
// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s
|
||||
// RUN: %clang_cc1 -std=c++2a -fsyntax-only -verify %s
|
||||
|
||||
typedef int f;
|
||||
typedef int fn;
|
||||
|
||||
namespace N0 {
|
||||
struct A {
|
||||
friend void f();
|
||||
struct A {
|
||||
friend void fn();
|
||||
void g() {
|
||||
int i = f(1);
|
||||
int i = fn(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace N1 {
|
||||
struct A {
|
||||
friend void f(A &);
|
||||
struct A {
|
||||
friend void fn(A &);
|
||||
operator int();
|
||||
void g(A a) {
|
||||
// ADL should not apply to the lookup of 'f', it refers to the typedef
|
||||
// ADL should not apply to the lookup of 'fn', it refers to the typedef
|
||||
// above.
|
||||
int i = f(a);
|
||||
int i = fn(a);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace std_example {
|
||||
int h; // expected-note {{non-template declaration}}
|
||||
void g();
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-note@-2 {{non-template declaration}}
|
||||
#endif
|
||||
namespace N {
|
||||
struct A {};
|
||||
template<class T> int f(T);
|
||||
template<class T> int g(T);
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-note@-2 {{here}}
|
||||
#endif
|
||||
template<class T> int h(T); // expected-note {{here}}
|
||||
}
|
||||
|
||||
int x = f<N::A>(N::A());
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-warning@-2 {{C++2a extension}}
|
||||
#endif
|
||||
int y = g<N::A>(N::A());
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-error@-2 {{'g' does not name a template but is followed by template arguments; did you mean 'N::g'?}}
|
||||
#endif
|
||||
int z = h<N::A>(N::A()); // expected-error {{'h' does not name a template but is followed by template arguments; did you mean 'N::h'?}}
|
||||
}
|
||||
|
||||
namespace AnnexD_example {
|
||||
struct A {};
|
||||
void operator<(void (*fp)(), A);
|
||||
void f() {}
|
||||
int main() {
|
||||
A a;
|
||||
f < a;
|
||||
#if __cplusplus > 201703L
|
||||
// expected-error@-2 {{expected '>'}}
|
||||
#endif
|
||||
(f) < a;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
// RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
|
||||
// PR13819 -- __SIZE_TYPE__ is incompatible.
|
||||
typedef __SIZE_TYPE__ size_t; // expected-error 0-1 {{extension}}
|
||||
|
@ -434,7 +435,7 @@ namespace dr239 { // dr239: yes
|
|||
namespace dr241 { // dr241: yes
|
||||
namespace A {
|
||||
struct B {};
|
||||
template <int X> void f(); // expected-note 2{{candidate}}
|
||||
template <int X> void f(); // expected-note 3{{candidate}}
|
||||
template <int X> void g(B);
|
||||
}
|
||||
namespace C {
|
||||
|
@ -442,8 +443,8 @@ namespace dr241 { // dr241: yes
|
|||
template <class T> void g(T t); // expected-note {{candidate}}
|
||||
}
|
||||
void h(A::B b) {
|
||||
f<3>(b); // expected-error {{undeclared identifier}}
|
||||
g<3>(b); // expected-error {{undeclared identifier}}
|
||||
f<3>(b); // expected-error 0-1{{C++2a extension}} expected-error {{no matching}}
|
||||
g<3>(b); // expected-error 0-1{{C++2a extension}}
|
||||
A::f<3>(b); // expected-error {{no matching}}
|
||||
A::g<3>(b);
|
||||
C::f<3>(b); // expected-error {{no matching}}
|
||||
|
|
|
@ -1048,10 +1048,13 @@ namespace dr686 { // dr686: yes
|
|||
template<struct R {} *> struct Y; // expected-error {{cannot be defined in a type specifier}}
|
||||
}
|
||||
|
||||
namespace dr687 { // dr687 still open
|
||||
namespace dr687 { // dr687 (9 c++20, but the issue is still considered open)
|
||||
template<typename T> void f(T a) {
|
||||
// FIXME: This is valid in C++20.
|
||||
g<int>(a); // expected-error {{undeclared}} expected-error {{'('}}
|
||||
// This is valid in C++20.
|
||||
g<int>(a);
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-error@-2 {{C++2a extension}}
|
||||
#endif
|
||||
|
||||
// This is not.
|
||||
template g<int>(a); // expected-error {{expected expression}}
|
||||
|
|
|
@ -8,7 +8,10 @@ namespace N {
|
|||
};
|
||||
}
|
||||
|
||||
template class Z<int>; // expected-error{{explicit instantiation of non-template class 'Z'}}
|
||||
template class Z<int>; // expected-error{{explicit instantiation of undeclared template class 'Z'}}
|
||||
|
||||
struct Q;
|
||||
template class Q<int>; // expected-error{{explicit instantiation of non-template class 'Q'}}
|
||||
|
||||
// FIXME: This example from the standard is wrong; note posted to CWG reflector
|
||||
// on 10/27/2009
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
|
||||
// FIXME: The diagnostics and recovery here are very, very poor.
|
||||
|
||||
// PR10355
|
||||
template<typename T> void template_id1() { // expected-note {{'template_id1' declared here}} \
|
||||
// expected-note {{possible target for call}}
|
||||
template_id2<> t; // expected-error {{no template named 'template_id2'; did you mean 'template_id1'?}} \
|
||||
// expected-error {{expected ';' after expression}} \
|
||||
// expected-error {{reference to overloaded function could not be resolved; did you mean to call it?}} \
|
||||
// expected-error {{use of undeclared identifier 't'}}
|
||||
template<typename T> void template_id1() {
|
||||
template_id2<> t; // expected-error-re {{no template named 'template_id2'{{$}}}}
|
||||
}
|
||||
|
||||
// FIXME: It would be nice if we could get this correction right.
|
||||
|
|
|
@ -394,7 +394,7 @@ template<double* ...A> class class_ptrs {};
|
|||
void set13(class_ptrs<&a13, &b13>) {}
|
||||
void test13() {
|
||||
set13(class_ptrs<&c13>());
|
||||
set13(class_ptrss<&a13, &b13, &d13>());
|
||||
set13(class_ptrs<&a13, &b13, &d13>());
|
||||
}
|
||||
// CHECK-ELIDE-NOTREE: no matching function for call to 'set13'
|
||||
// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_ptrs<&c13, (no argument)>' to 'class_ptrs<&a13, &b13>' for 1st argument
|
||||
|
|
|
@ -18,9 +18,7 @@ int test_broken() {
|
|||
int *ip = 0;
|
||||
f1(ip); // expected-error{{use of undeclared identifier 'f1'}}
|
||||
|
||||
vector<int> vec; // expected-error{{use of undeclared identifier 'vector'}} \
|
||||
// expected-error{{expected '(' for function-style cast or type construction}} \
|
||||
// expected-error{{use of undeclared identifier 'vec'}}
|
||||
vector<int> vec; // expected-error{{no template named 'vector'}}
|
||||
|
||||
VisibleStruct vs;
|
||||
vs.field = 0; // expected-error{{no member named 'field' in 'VisibleStruct'}}
|
||||
|
|
|
@ -29,7 +29,7 @@ B::Inner2 pre_bi; // expected-error +{{must be imported}}
|
|||
// expected-note@defs.h:4 +{{here}}
|
||||
// expected-note@defs.h:17 +{{here}}
|
||||
void pre_bfi(B b) { // expected-error +{{must be imported}}
|
||||
b.f<int>(); // expected-error +{{}}
|
||||
b.f<int>();
|
||||
}
|
||||
|
||||
C_Base<1> pre_cb1; // expected-error +{{must be imported}}
|
||||
|
@ -48,7 +48,7 @@ int pre_e = E(0); // expected-error {{must be imported}}
|
|||
// expected-note@defs.h:32 +{{here}}
|
||||
|
||||
int pre_ff = F<int>().f(); // expected-error +{{must be imported}}
|
||||
int pre_fg = F<int>().g<int>(); // expected-error +{{must be imported}} expected-error 2{{expected}}
|
||||
int pre_fg = F<int>().g<int>(); // expected-error +{{must be imported}}
|
||||
// expected-note@defs.h:34 +{{here}}
|
||||
|
||||
G::A pre_ga // expected-error +{{must be imported}}
|
||||
|
|
|
@ -31,7 +31,7 @@ struct S {
|
|||
|
||||
void f2a(
|
||||
// T3<int> here is a parameter type, so must be declared before it is used.
|
||||
int k1 = c < b, T3 < int > x = 0 // expected-error {{unexpected end of default argument expression}}
|
||||
int k1 = c < b, T3 < int > x = 0 // expected-error {{no template named 'T3'}}
|
||||
);
|
||||
|
||||
template<typename, int=0> struct T3 { T3(int); operator int(); };
|
||||
|
|
|
@ -60,33 +60,32 @@ namespace pr16225add {
|
|||
template<int N1, int N2> struct ABC2 {};
|
||||
|
||||
template<class T1, typename T2> struct foo :
|
||||
UnknownBase<T1,T2> // expected-error {{unknown template name 'UnknownBase'}}
|
||||
UnknownBase<T1,T2> // expected-error {{no template named 'UnknownBase'}}
|
||||
{ };
|
||||
|
||||
template<class T1, typename T2> struct foo2 :
|
||||
UnknownBase<T1,T2>, // expected-error {{unknown template name 'UnknownBase'}}
|
||||
UnknownBase<T1,T2>, // expected-error {{no template named 'UnknownBase'}}
|
||||
Known<T1> // expected-error {{too few template arguments for class template 'Known'}}
|
||||
{ };
|
||||
|
||||
template<class T1, typename T2> struct foo3 :
|
||||
UnknownBase<T1,T2,ABC<T2,T1> > // expected-error {{unknown template name 'UnknownBase'}}
|
||||
UnknownBase<T1,T2,ABC<T2,T1> > // expected-error {{no template named 'UnknownBase'}}
|
||||
{ };
|
||||
|
||||
template<class T1, typename T2> struct foo4 :
|
||||
UnknownBase<T1,ABC<T2> >, // expected-error {{unknown template name 'UnknownBase'}} \
|
||||
// expected-error {{too few template arguments for class template 'ABC'}}
|
||||
UnknownBase<T1,ABC<T2> >, // expected-error {{too few template arguments for class template 'ABC'}}
|
||||
Known<T1> // expected-error {{too few template arguments for class template 'Known'}}
|
||||
{ };
|
||||
|
||||
template<class T1, typename T2> struct foo5 :
|
||||
UnknownBase<T1,T2,ABC<T2,T1>> // expected-error {{unknown template name 'UnknownBase'}}
|
||||
UnknownBase<T1,T2,ABC<T2,T1>> // expected-error {{no template named 'UnknownBase'}}
|
||||
#if __cplusplus <= 199711L
|
||||
// expected-error@-2 {{use '> >'}}
|
||||
#endif
|
||||
{ };
|
||||
|
||||
template<class T1, typename T2> struct foo6 :
|
||||
UnknownBase<T1,ABC<T2,T1>>, // expected-error {{unknown template name 'UnknownBase'}}
|
||||
UnknownBase<T1,ABC<T2,T1>>, // expected-error {{no template named 'UnknownBase'}}
|
||||
#if __cplusplus <= 199711L
|
||||
// expected-error@-2 {{use '> >'}}
|
||||
#endif
|
||||
|
@ -94,32 +93,32 @@ namespace pr16225add {
|
|||
{ };
|
||||
|
||||
template<class T1, typename T2, int N> struct foo7 :
|
||||
UnknownBase<T1,T2,(N>1)> // expected-error {{unknown template name 'UnknownBase'}}
|
||||
UnknownBase<T1,T2,(N>1)> // expected-error {{no template named 'UnknownBase'}}
|
||||
{ };
|
||||
|
||||
template<class T1, typename T2> struct foo8 :
|
||||
UnknownBase<X<int,int>,X<int,int>> // expected-error {{unknown template name 'UnknownBase'}}
|
||||
UnknownBase<X<int,int>,X<int,int>> // expected-error {{no template named 'UnknownBase'}}
|
||||
#if __cplusplus <= 199711L
|
||||
// expected-error@-2 {{use '> >'}}
|
||||
#endif
|
||||
{ };
|
||||
|
||||
template<class T1, typename T2> struct foo9 :
|
||||
UnknownBase<Known<int,int>,X<int,int>> // expected-error {{unknown template name 'UnknownBase'}}
|
||||
UnknownBase<Known<int,int>,X<int,int>> // expected-error {{no template named 'UnknownBase'}}
|
||||
#if __cplusplus <= 199711L
|
||||
// expected-error@-2 {{use '> >'}}
|
||||
#endif
|
||||
{ };
|
||||
|
||||
template<class T1, typename T2> struct foo10 :
|
||||
UnknownBase<Known<int,int>,X<int,X<int,int>>> // expected-error {{unknown template name 'UnknownBase'}}
|
||||
UnknownBase<Known<int,int>,X<int,X<int,int>>> // expected-error {{no template named 'UnknownBase'}}
|
||||
#if __cplusplus <= 199711L
|
||||
// expected-error@-2 {{use '> >'}}
|
||||
#endif
|
||||
{ };
|
||||
|
||||
template<int N1, int N2> struct foo11 :
|
||||
UnknownBase<2<N1,N2<4> // expected-error {{unknown template name 'UnknownBase'}}
|
||||
UnknownBase<2<N1,N2<4> // expected-error {{no template named 'UnknownBase'}}
|
||||
{ };
|
||||
|
||||
}
|
||||
|
|
|
@ -128,8 +128,13 @@ void f2() {
|
|||
|
||||
|
||||
// PR3844
|
||||
template <> struct S<int> { }; // expected-error{{explicit specialization of non-template struct 'S'}}
|
||||
template <> union U<int> { }; // expected-error{{explicit specialization of non-template union 'U'}}
|
||||
template <> struct S<int> { }; // expected-error{{explicit specialization of undeclared template struct 'S'}}
|
||||
template <> union U<int> { }; // expected-error{{explicit specialization of undeclared template union 'U'}}
|
||||
|
||||
struct SS;
|
||||
union UU;
|
||||
template <> struct SS<int> { }; // expected-error{{explicit specialization of non-template struct 'SS'}}
|
||||
template <> union UU<int> { }; // expected-error{{explicit specialization of non-template union 'UU'}}
|
||||
|
||||
namespace PR6184 {
|
||||
namespace N {
|
||||
|
@ -230,11 +235,11 @@ struct base { };
|
|||
|
||||
struct t1 : base<int,
|
||||
public: // expected-error {{expected expression}}
|
||||
}; // expected-error {{expected class name}}
|
||||
};
|
||||
// expected-error@-1 {{expected '{' after base class list}}
|
||||
struct t2 : base<int,
|
||||
public // expected-error {{expected expression}}
|
||||
}; // expected-error {{expected class name}}
|
||||
};
|
||||
// expected-error@-1 {{expected '{' after base class list}}
|
||||
|
||||
}
|
||||
|
|
|
@ -112,11 +112,8 @@ namespace StdExample {
|
|||
template<typename T> using handler_t = void (*)(T);
|
||||
extern handler_t<int> ignore;
|
||||
extern void (*ignore)(int);
|
||||
// FIXME: we recover as if cell is an undeclared variable. the diagnostics are terrible!
|
||||
template<typename T> using cell = pair<T*, cell<T>*>; // expected-error {{use of undeclared identifier 'cell'}} \
|
||||
expected-error {{'T' does not refer to a value}} \
|
||||
expected-note {{declared here}} \
|
||||
expected-error {{expected ';' after alias declaration}}
|
||||
// FIXME: we recover as if cell is an undeclared variable template
|
||||
template<typename T> using cell = pair<T*, cell<T>*>; // expected-error {{use of undeclared identifier 'cell'}} expected-error {{expected expression}}
|
||||
}
|
||||
|
||||
namespace Access {
|
||||
|
|
|
@ -138,7 +138,7 @@ struct S
|
|||
// Don't crash on this bogus code.
|
||||
namespace pr6629 {
|
||||
template<class T1, class T2> struct foo :
|
||||
bogus<foo<T1,T2> > // expected-error {{unknown template name 'bogus'}}
|
||||
bogus<foo<T1,T2> > // expected-error {{no template named 'bogus'}}
|
||||
{ };
|
||||
|
||||
template<> struct foo<unknown,unknown> { // expected-error {{undeclared identifier 'unknown'}}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -verify %s
|
||||
|
||||
namespace N {
|
||||
struct Q {};
|
||||
template<typename> int f(Q);
|
||||
template<int> int f(Q);
|
||||
template<typename> int g(Q);
|
||||
template<int> int g(Q);
|
||||
|
||||
template<int> int some_long_name(Q); // expected-note {{here}}
|
||||
}
|
||||
N::Q q;
|
||||
int g();
|
||||
|
||||
int h();
|
||||
template<int> int h(...);
|
||||
int h(int);
|
||||
|
||||
// OK, these find the above functions by ADL.
|
||||
int a = f<int>(q);
|
||||
int b(f<int>(q));
|
||||
int c(f<0>(q));
|
||||
int d = g<int>(q);
|
||||
|
||||
int e = h<0>(q); // ok, found by unqualified lookup
|
||||
|
||||
void fn() {
|
||||
f<0>(q);
|
||||
int f;
|
||||
f<0>(q); // expected-error {{invalid operands to binary expression}}
|
||||
}
|
||||
|
||||
void disambig() {
|
||||
// FIXME: It's unclear whether ending the template argument at the > inside the ?: is correct here (see DR579).
|
||||
f<true ? 1 > 2 : 3>(q); // expected-error {{expected ':'}} expected-note {{to match}} expected-error {{expected expression}}
|
||||
|
||||
f < 1 + 3 > (q); // ok, function call
|
||||
}
|
||||
|
||||
bool typo(int something) { // expected-note 4{{declared here}}
|
||||
// FIXME: We shouldn't suggest the N:: for an ADL call if the candidate can be found by ADL.
|
||||
some_logn_name<3>(q); // expected-error {{did you mean 'N::some_long_name'?}}
|
||||
somethign < 3 ? h() > 4 : h(0); // expected-error {{did you mean 'something'}}
|
||||
// This is parsed as a comparison on the left of a ?: expression.
|
||||
somethign < 3 ? h() + 4 : h(0); // expected-error {{did you mean 'something'}}
|
||||
// This is parsed as an ADL-only template-id call.
|
||||
somethign < 3 ? h() + 4 : h(0) >(0); // expected-error {{undeclared identifier 'somethign'}}
|
||||
bool k(somethign < 3); // expected-error {{did you mean 'something'}}
|
||||
return somethign < 3; // expected-error {{did you mean 'something'}}
|
||||
}
|
||||
|
||||
// Ensure that treating undeclared identifiers as template names doesn't cause
|
||||
// problems.
|
||||
struct W<int> {}; // expected-error {{undeclared template struct 'W'}}
|
||||
X<int>::Y xy; // expected-error {{no template named 'X'}}
|
||||
void xf(X<int> x); // expected-error {{no template named 'X'}}
|
||||
struct A : X<int> { // expected-error {{no template named 'X'}}
|
||||
A() : X<int>() {} // expected-error {{no template named 'X'}}
|
||||
};
|
||||
|
||||
// Similarly for treating overload sets of functions as template names.
|
||||
struct g<int> {}; // expected-error {{'g' refers to a function template}}
|
||||
g<int>::Y xy; // expected-error {{no template named 'g'}} FIXME lies
|
||||
void xf(g<int> x); // expected-error {{variable has incomplete type 'void'}} expected-error 1+{{}} expected-note {{}}
|
||||
struct B : g<int> { // expected-error {{expected class name}}
|
||||
B() : g<int>() {} // expected-error {{expected class member or base class name}}
|
||||
};
|
|
@ -99,7 +99,7 @@ struct Y {
|
|||
namespace PR6421 {
|
||||
class T; // expected-note{{forward declaration}}
|
||||
|
||||
class QGenericArgument // expected-note{{declared here}}
|
||||
class QGenericArgument
|
||||
{
|
||||
template<typename U>
|
||||
void foo(T t) // expected-error{{variable has incomplete type}}
|
||||
|
@ -108,8 +108,7 @@ namespace PR6421 {
|
|||
void disconnect()
|
||||
{
|
||||
T* t;
|
||||
bob<QGenericArgument>(t); // expected-error{{undeclared identifier 'bob'}} \
|
||||
// expected-error{{does not refer to a value}}
|
||||
bob<QGenericArgument>(t); // expected-error{{undeclared identifier 'bob'}}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -53,7 +53,10 @@ namespace test3 {
|
|||
namespace rdar11293995 {
|
||||
|
||||
struct Length {
|
||||
explicit Length(PassRefPtr<CalculationValue>); // expected-error {{no template named 'PassRefPtr}} expected-error {{undeclared identifier 'CalculationValue'}}
|
||||
// FIXME: We try to annotate the template-id here during tentative parsing,
|
||||
// and fail, then try again during the actual parse. This results in the same
|
||||
// diagnostic being produced twice. :(
|
||||
explicit Length(PassRefPtr<CalculationValue>); // expected-error 2{{undeclared identifier 'CalculationValue'}}
|
||||
};
|
||||
|
||||
struct LengthSize {
|
||||
|
|
|
@ -438,7 +438,7 @@ namespace PR17394 {
|
|||
long zzzzzzzzzz;
|
||||
};
|
||||
class B : private A {};
|
||||
B zzzzzzzzzy<>; // expected-error {{expected ';' after top level declarator}}{}
|
||||
B zzzzzzzzzy<>; // expected-error {{template specialization requires 'template<>'}} expected-error {{no variable template matches specialization}}
|
||||
}
|
||||
|
||||
namespace correct_fields_in_member_funcs {
|
||||
|
|
|
@ -9,7 +9,7 @@ template<typename T, typename U>
|
|||
struct X1 : T::apply<U> { }; // expected-error{{use 'template' keyword to treat 'apply' as a dependent template name}}
|
||||
|
||||
template<typename T>
|
||||
struct X2 : vector<T> { }; // expected-error{{unknown template name 'vector'}}
|
||||
struct X2 : vector<T> { }; // expected-error{{no template named 'vector'}}
|
||||
|
||||
namespace PR6031 {
|
||||
template<typename T>
|
||||
|
|
|
@ -7,7 +7,7 @@ struct X {
|
|||
|
||||
t->operator+<U const, 1>(1); // expected-error{{use 'template' keyword to treat 'operator +' as a dependent template name}}
|
||||
t->f1<int const, 2>(1); // expected-error{{use 'template' keyword to treat 'f1' as a dependent template name}}
|
||||
t->f1<3, int const>(1); // expected-error{{missing 'template' keyword prior to dependent template name 'f1'}}
|
||||
t->f1<3, int const>(1); // expected-error{{use 'template' keyword to treat 'f1' as a dependent template name}}
|
||||
|
||||
T::getAs<U>(); // expected-error{{use 'template' keyword to treat 'getAs' as a dependent template name}}
|
||||
t->T::getAs<U>(); // expected-error{{use 'template' keyword to treat 'getAs' as a dependent template name}}
|
||||
|
@ -15,7 +15,7 @@ struct X {
|
|||
(*t).f2<N>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}}
|
||||
(*t).f2<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}}
|
||||
T::f2<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}}
|
||||
T::f2<0, int>(0); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}}
|
||||
T::f2<0, int>(0); // expected-error{{use 'template' keyword to treat 'f2' as a dependent template name}}
|
||||
|
||||
T::foo<N < 2 || N >= 4>(); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
|
||||
|
||||
|
@ -24,7 +24,7 @@ struct X {
|
|||
T::foo<T::bar < 1>(); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
|
||||
T::foo < T::bar<1>(); // expected-error{{missing 'template' keyword prior to dependent template name 'bar'}}
|
||||
|
||||
// Prefer to diagonse a missing 'template' keyword rather than finding a non-template name.
|
||||
// Prefer to diagnose a missing 'template' keyword rather than finding a non-template name.
|
||||
xyz < T::foo < 1 > (); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
|
||||
T::foo < xyz < 1 > (); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
|
||||
|
||||
|
@ -83,12 +83,12 @@ template<int N, typename T> void f(T t) {
|
|||
T::g<mb>(0);
|
||||
|
||||
// ... but this one must be a template-id.
|
||||
T::g<mb, int>(0); // expected-error {{missing 'template' keyword prior to dependent template name 'g'}}
|
||||
T::g<mb, int>(0); // expected-error {{use 'template' keyword to treat 'g' as a dependent template name}} expected-error {{no matching function}}
|
||||
}
|
||||
|
||||
struct Y {
|
||||
template <int> void f(int);
|
||||
template <int = 0> static void g(int); // expected-warning 0-1{{extension}}
|
||||
template <int = 0> static void g(int); // expected-warning 0-1{{extension}} expected-note {{candidate}}
|
||||
};
|
||||
void q() { void (*p)(int) = Y::g; }
|
||||
template void f<0>(Y); // expected-note {{in instantiation of}}
|
||||
|
|
|
@ -2,5 +2,8 @@
|
|||
|
||||
// <rdar://problem/9173693>
|
||||
template< bool C > struct assert { };
|
||||
template< bool > struct assert_arg_pred_impl { }; // expected-note 3 {{declared here}}
|
||||
template< typename Pred > assert<false> assert_not_arg( void (*)(Pred), typename assert_arg_pred<Pred>::type ); // expected-error 5 {{}}
|
||||
// FIXME: We diagnose the same problem multiple times here because we have no
|
||||
// way to indicate in the token stream that we already tried to annotate a
|
||||
// template-id and we failed.
|
||||
template< bool > struct assert_arg_pred_impl { }; // expected-note 4 {{declared here}}
|
||||
template< typename Pred > assert<false> assert_not_arg( void (*)(Pred), typename assert_arg_pred<Pred>::type ); // expected-error 6 {{}}
|
||||
|
|
|
@ -28,12 +28,12 @@ namespace PR16134 {
|
|||
namespace PR16225 {
|
||||
template <typename T> void f();
|
||||
template <typename C> void g(C*) {
|
||||
struct LocalStruct : UnknownBase<Mumble, C> { }; // expected-error {{unknown template name 'UnknownBase'}} \
|
||||
// expected-error {{use of undeclared identifier 'Mumble'}}
|
||||
struct LocalStruct : UnknownBase<Mumble, C> { }; // expected-error {{use of undeclared identifier 'Mumble'}}
|
||||
f<LocalStruct>();
|
||||
#if __cplusplus <= 199711L
|
||||
// expected-warning@-2 {{template argument uses local type 'LocalStruct'}}
|
||||
#endif
|
||||
struct LocalStruct2 : UnknownBase<C> { }; // expected-error {{no template named 'UnknownBase'}}
|
||||
}
|
||||
struct S;
|
||||
void h() {
|
||||
|
|
|
@ -1430,6 +1430,10 @@ bool CursorVisitor::VisitTemplateName(TemplateName Name, SourceLocation Loc) {
|
|||
|
||||
return false;
|
||||
|
||||
case TemplateName::AssumedTemplate:
|
||||
// FIXME: Visit DeclarationName?
|
||||
return false;
|
||||
|
||||
case TemplateName::DependentTemplate:
|
||||
// FIXME: Visit nested-name-specifier.
|
||||
return false;
|
||||
|
|
|
@ -893,7 +893,7 @@ as the draft C++2a standard evolves.
|
|||
<tr>
|
||||
<td>ADL and function templates that are not visible</td>
|
||||
<td><a href="http://wg21.link/p0846r0">P0846R0</a></td>
|
||||
<td class="none" align="center">No</td>
|
||||
<td class="svn" align="center">SVN</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><tt>const</tt> mismatch with defaulted copy constructor</td>
|
||||
|
|
Loading…
Reference in New Issue