forked from OSchip/llvm-project
[C++2a] P0634r3: Down with typename!
This patch implements P0634r3 that removes the need for 'typename' in certain contexts. For example, ``` template <typename T> using foo = T::type; // ok ``` This is also allowed in previous language versions as an extension, because I think it's pretty useful. :) Reviewed By: #clang-language-wg, erichkeane Differential Revision: https://reviews.llvm.org/D53847
This commit is contained in:
parent
6f2b347895
commit
4848f3bf2f
|
@ -358,6 +358,8 @@ C++20 Feature Support
|
|||
`Issue 44178 <https://github.com/llvm/llvm-project/issues/44178>`_.
|
||||
- Clang implements DR2621, correcting a defect in ``using enum`` handling. The
|
||||
name is found via ordinary lookup so typedefs are found.
|
||||
- Implemented `P0634r3 <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0634r3.html>`_,
|
||||
which removes the requirement for the ``typename`` keyword in certain contexts.
|
||||
|
||||
C++2b Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -5432,6 +5432,12 @@ def err_typename_refers_to_using_value_decl : Error<
|
|||
"%0 in %1">;
|
||||
def note_using_value_decl_missing_typename : Note<
|
||||
"add 'typename' to treat this using declaration as a type">;
|
||||
def warn_cxx17_compat_implicit_typename : Warning<"use of implicit 'typename' is "
|
||||
"incompatible with C++ standards before C++20">, InGroup<CXX20Compat>,
|
||||
DefaultIgnore;
|
||||
def ext_implicit_typename : ExtWarn<"missing 'typename' prior to dependent "
|
||||
"type name %0%1; implicit 'typename' is a C++20 extension">,
|
||||
InGroup<CXX20>;
|
||||
|
||||
def err_template_kw_refers_to_non_template : Error<
|
||||
"%0%select{| following the 'template' keyword}1 "
|
||||
|
|
|
@ -857,9 +857,12 @@ private:
|
|||
public:
|
||||
// If NeedType is true, then TryAnnotateTypeOrScopeToken will try harder to
|
||||
// find a type name by attempting typo correction.
|
||||
bool TryAnnotateTypeOrScopeToken();
|
||||
bool TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS,
|
||||
bool IsNewScope);
|
||||
bool
|
||||
TryAnnotateTypeOrScopeToken(ImplicitTypenameContext AllowImplicitTypename =
|
||||
ImplicitTypenameContext::No);
|
||||
bool TryAnnotateTypeOrScopeTokenAfterScopeSpec(
|
||||
CXXScopeSpec &SS, bool IsNewScope,
|
||||
ImplicitTypenameContext AllowImplicitTypename);
|
||||
bool TryAnnotateCXXScopeToken(bool EnteringContext = false);
|
||||
|
||||
bool MightBeCXXScopeToken() {
|
||||
|
@ -885,7 +888,11 @@ private:
|
|||
/// Annotation was successful.
|
||||
ANK_Success
|
||||
};
|
||||
AnnotatedNameKind TryAnnotateName(CorrectionCandidateCallback *CCC = nullptr);
|
||||
|
||||
AnnotatedNameKind
|
||||
TryAnnotateName(CorrectionCandidateCallback *CCC = nullptr,
|
||||
ImplicitTypenameContext AllowImplicitTypename =
|
||||
ImplicitTypenameContext::No);
|
||||
|
||||
/// Push a tok::annot_cxxscope token onto the token stream.
|
||||
void AnnotateScopeToken(CXXScopeSpec &SS, bool IsNewAnnotation);
|
||||
|
@ -1979,7 +1986,8 @@ private:
|
|||
/// simple-type-specifier.
|
||||
void ParseCXXSimpleTypeSpecifier(DeclSpec &DS);
|
||||
|
||||
bool ParseCXXTypeSpecifierSeq(DeclSpec &DS);
|
||||
bool ParseCXXTypeSpecifierSeq(
|
||||
DeclSpec &DS, DeclaratorContext Context = DeclaratorContext::TypeName);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// C++ 5.3.4 and 5.3.5: C++ new and delete
|
||||
|
@ -2194,16 +2202,19 @@ private:
|
|||
/// out, there are other significant restrictions on specifiers than
|
||||
/// would be best implemented in the parser.
|
||||
enum class DeclSpecContext {
|
||||
DSC_normal, // normal context
|
||||
DSC_class, // class context, enables 'friend'
|
||||
DSC_normal, // normal context
|
||||
DSC_class, // class context, enables 'friend'
|
||||
DSC_type_specifier, // C++ type-specifier-seq or C specifier-qualifier-list
|
||||
DSC_trailing, // C++11 trailing-type-specifier in a trailing return type
|
||||
DSC_alias_declaration, // C++11 type-specifier-seq in an alias-declaration
|
||||
DSC_top_level, // top-level/namespace declaration context
|
||||
DSC_template_param, // template parameter context
|
||||
DSC_template_type_arg, // template type argument context
|
||||
DSC_objc_method_result, // ObjC method result context, enables 'instancetype'
|
||||
DSC_condition, // condition declaration context
|
||||
DSC_alias_declaration, // C++11 type-specifier-seq in an alias-declaration
|
||||
DSC_conv_operator, // C++ type-specifier-seq in an conversion operator
|
||||
DSC_top_level, // top-level/namespace declaration context
|
||||
DSC_template_param, // template parameter context
|
||||
DSC_template_arg, // template argument context
|
||||
DSC_template_type_arg, // template type argument context
|
||||
DSC_objc_method_result, // ObjC method result context, enables
|
||||
// 'instancetype'
|
||||
DSC_condition, // condition declaration context
|
||||
DSC_association // A _Generic selection expression's type association
|
||||
};
|
||||
|
||||
|
@ -2213,6 +2224,7 @@ private:
|
|||
switch (DSC) {
|
||||
case DeclSpecContext::DSC_normal:
|
||||
case DeclSpecContext::DSC_template_param:
|
||||
case DeclSpecContext::DSC_template_arg:
|
||||
case DeclSpecContext::DSC_class:
|
||||
case DeclSpecContext::DSC_top_level:
|
||||
case DeclSpecContext::DSC_objc_method_result:
|
||||
|
@ -2221,6 +2233,7 @@ private:
|
|||
|
||||
case DeclSpecContext::DSC_template_type_arg:
|
||||
case DeclSpecContext::DSC_type_specifier:
|
||||
case DeclSpecContext::DSC_conv_operator:
|
||||
case DeclSpecContext::DSC_trailing:
|
||||
case DeclSpecContext::DSC_alias_declaration:
|
||||
case DeclSpecContext::DSC_association:
|
||||
|
@ -2270,6 +2283,8 @@ private:
|
|||
: AllowDefiningTypeSpec::Yes;
|
||||
|
||||
case DeclSpecContext::DSC_trailing:
|
||||
case DeclSpecContext::DSC_conv_operator:
|
||||
case DeclSpecContext::DSC_template_arg:
|
||||
return AllowDefiningTypeSpec::No;
|
||||
}
|
||||
llvm_unreachable("Missing DeclSpecContext case");
|
||||
|
@ -2291,6 +2306,9 @@ private:
|
|||
case DeclSpecContext::DSC_type_specifier:
|
||||
case DeclSpecContext::DSC_trailing:
|
||||
case DeclSpecContext::DSC_association:
|
||||
case DeclSpecContext::DSC_conv_operator:
|
||||
case DeclSpecContext::DSC_template_arg:
|
||||
|
||||
return false;
|
||||
}
|
||||
llvm_unreachable("Missing DeclSpecContext case");
|
||||
|
@ -2302,11 +2320,13 @@ private:
|
|||
switch (DSC) {
|
||||
case DeclSpecContext::DSC_normal:
|
||||
case DeclSpecContext::DSC_template_param:
|
||||
case DeclSpecContext::DSC_template_arg:
|
||||
case DeclSpecContext::DSC_class:
|
||||
case DeclSpecContext::DSC_top_level:
|
||||
case DeclSpecContext::DSC_condition:
|
||||
case DeclSpecContext::DSC_type_specifier:
|
||||
case DeclSpecContext::DSC_association:
|
||||
case DeclSpecContext::DSC_conv_operator:
|
||||
return true;
|
||||
|
||||
case DeclSpecContext::DSC_objc_method_result:
|
||||
|
@ -2318,6 +2338,30 @@ private:
|
|||
llvm_unreachable("Missing DeclSpecContext case");
|
||||
}
|
||||
|
||||
// Is this a context in which an implicit 'typename' is allowed?
|
||||
static ImplicitTypenameContext
|
||||
getImplicitTypenameContext(DeclSpecContext DSC) {
|
||||
switch (DSC) {
|
||||
case DeclSpecContext::DSC_class:
|
||||
case DeclSpecContext::DSC_top_level:
|
||||
case DeclSpecContext::DSC_type_specifier:
|
||||
case DeclSpecContext::DSC_template_type_arg:
|
||||
case DeclSpecContext::DSC_trailing:
|
||||
case DeclSpecContext::DSC_alias_declaration:
|
||||
case DeclSpecContext::DSC_template_param:
|
||||
return ImplicitTypenameContext::Yes;
|
||||
|
||||
case DeclSpecContext::DSC_normal:
|
||||
case DeclSpecContext::DSC_objc_method_result:
|
||||
case DeclSpecContext::DSC_condition:
|
||||
case DeclSpecContext::DSC_template_arg:
|
||||
case DeclSpecContext::DSC_conv_operator:
|
||||
case DeclSpecContext::DSC_association:
|
||||
return ImplicitTypenameContext::No;
|
||||
}
|
||||
llvm_unreachable("Missing DeclSpecContext case");
|
||||
}
|
||||
|
||||
/// Information on a C++0x for-range-initializer found while parsing a
|
||||
/// declaration which turns out to be a for-range-declaration.
|
||||
struct ForRangeInit {
|
||||
|
@ -2373,13 +2417,28 @@ private:
|
|||
const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(),
|
||||
AccessSpecifier AS = AS_none,
|
||||
DeclSpecContext DSC = DeclSpecContext::DSC_normal,
|
||||
LateParsedAttrList *LateAttrs = nullptr);
|
||||
LateParsedAttrList *LateAttrs = nullptr) {
|
||||
return ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC, LateAttrs,
|
||||
getImplicitTypenameContext(DSC));
|
||||
}
|
||||
void ParseDeclarationSpecifiers(
|
||||
DeclSpec &DS, const ParsedTemplateInfo &TemplateInfo, AccessSpecifier AS,
|
||||
DeclSpecContext DSC, LateParsedAttrList *LateAttrs,
|
||||
ImplicitTypenameContext AllowImplicitTypename);
|
||||
|
||||
bool DiagnoseMissingSemiAfterTagDefinition(
|
||||
DeclSpec &DS, AccessSpecifier AS, DeclSpecContext DSContext,
|
||||
LateParsedAttrList *LateAttrs = nullptr);
|
||||
|
||||
void ParseSpecifierQualifierList(
|
||||
DeclSpec &DS, AccessSpecifier AS = AS_none,
|
||||
DeclSpecContext DSC = DeclSpecContext::DSC_normal) {
|
||||
ParseSpecifierQualifierList(DS, getImplicitTypenameContext(DSC), AS, DSC);
|
||||
}
|
||||
|
||||
void ParseSpecifierQualifierList(
|
||||
DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename,
|
||||
AccessSpecifier AS = AS_none,
|
||||
DeclSpecContext DSC = DeclSpecContext::DSC_normal);
|
||||
|
||||
void ParseObjCTypeQualifierList(ObjCDeclSpec &DS,
|
||||
|
@ -2396,7 +2455,8 @@ private:
|
|||
ParsingDeclSpec &DS,
|
||||
llvm::function_ref<void(ParsingFieldDeclarator &)> FieldsCallback);
|
||||
|
||||
bool isDeclarationSpecifier(bool DisambiguatingWithExpression = false);
|
||||
bool isDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
|
||||
bool DisambiguatingWithExpression = false);
|
||||
bool isTypeSpecifierQualifier();
|
||||
|
||||
/// isKnownToBeTypeSpecifier - Return true if we know that the specified token
|
||||
|
@ -2409,8 +2469,9 @@ private:
|
|||
/// cast. Return false if it's no a decl-specifier, or we're not sure.
|
||||
bool isKnownToBeDeclarationSpecifier() {
|
||||
if (getLangOpts().CPlusPlus)
|
||||
return isCXXDeclarationSpecifier() == TPResult::True;
|
||||
return isDeclarationSpecifier(true);
|
||||
return isCXXDeclarationSpecifier(ImplicitTypenameContext::No) ==
|
||||
TPResult::True;
|
||||
return isDeclarationSpecifier(ImplicitTypenameContext::No, true);
|
||||
}
|
||||
|
||||
/// isDeclarationStatement - Disambiguates between a declaration or an
|
||||
|
@ -2419,7 +2480,7 @@ private:
|
|||
bool isDeclarationStatement() {
|
||||
if (getLangOpts().CPlusPlus)
|
||||
return isCXXDeclarationStatement();
|
||||
return isDeclarationSpecifier(true);
|
||||
return isDeclarationSpecifier(ImplicitTypenameContext::No, true);
|
||||
}
|
||||
|
||||
/// isForInitDeclaration - Disambiguates between a declaration or an
|
||||
|
@ -2432,7 +2493,7 @@ private:
|
|||
if (getLangOpts().CPlusPlus)
|
||||
return Tok.is(tok::kw_using) ||
|
||||
isCXXSimpleDeclaration(/*AllowForRangeDecl=*/true);
|
||||
return isDeclarationSpecifier(true);
|
||||
return isDeclarationSpecifier(ImplicitTypenameContext::No, true);
|
||||
}
|
||||
|
||||
/// Determine whether this is a C++1z for-range-identifier.
|
||||
|
@ -2445,7 +2506,9 @@ private:
|
|||
/// Starting with a scope specifier, identifier, or
|
||||
/// template-id that refers to the current class, determine whether
|
||||
/// this is a constructor declarator.
|
||||
bool isConstructorDeclarator(bool Unqualified, bool DeductionGuide = false);
|
||||
bool isConstructorDeclarator(
|
||||
bool Unqualified, bool DeductionGuide = false,
|
||||
DeclSpec::FriendSpecified IsFriend = DeclSpec::FriendSpecified::No);
|
||||
|
||||
/// Specifies the context in which type-id/expression
|
||||
/// disambiguation will occur.
|
||||
|
@ -2499,7 +2562,9 @@ private:
|
|||
/// might be a constructor-style initializer.
|
||||
/// If during the disambiguation process a parsing error is encountered,
|
||||
/// the function returns true to let the declaration parsing code handle it.
|
||||
bool isCXXFunctionDeclarator(bool *IsAmbiguous = nullptr);
|
||||
bool isCXXFunctionDeclarator(bool *IsAmbiguous = nullptr,
|
||||
ImplicitTypenameContext AllowImplicitTypename =
|
||||
ImplicitTypenameContext::No);
|
||||
|
||||
struct ConditionDeclarationOrInitStatementState;
|
||||
enum class ConditionOrInitStatement {
|
||||
|
@ -2545,7 +2610,8 @@ private:
|
|||
/// BracedCastResult.
|
||||
/// Doesn't consume tokens.
|
||||
TPResult
|
||||
isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False,
|
||||
isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
|
||||
TPResult BracedCastResult = TPResult::False,
|
||||
bool *InvalidAsDeclSpec = nullptr);
|
||||
|
||||
/// Given that isCXXDeclarationSpecifier returns \c TPResult::True or
|
||||
|
@ -2583,9 +2649,10 @@ private:
|
|||
TPResult TryParseInitDeclaratorList();
|
||||
TPResult TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier = true,
|
||||
bool mayHaveDirectInit = false);
|
||||
TPResult
|
||||
TryParseParameterDeclarationClause(bool *InvalidAsDeclaration = nullptr,
|
||||
bool VersusTemplateArg = false);
|
||||
TPResult TryParseParameterDeclarationClause(
|
||||
bool *InvalidAsDeclaration = nullptr, bool VersusTemplateArg = false,
|
||||
ImplicitTypenameContext AllowImplicitTypename =
|
||||
ImplicitTypenameContext::No);
|
||||
TPResult TryParseFunctionDeclarator();
|
||||
TPResult TryParseBracketDeclarator();
|
||||
TPResult TryConsumeDeclarationSpecifier();
|
||||
|
@ -3002,10 +3069,19 @@ private:
|
|||
Declarator &D,
|
||||
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo);
|
||||
void ParseParameterDeclarationClause(
|
||||
DeclaratorContext DeclaratorContext,
|
||||
ParsedAttributes &attrs,
|
||||
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo,
|
||||
SourceLocation &EllipsisLoc);
|
||||
Declarator &D, ParsedAttributes &attrs,
|
||||
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo,
|
||||
SourceLocation &EllipsisLoc) {
|
||||
return ParseParameterDeclarationClause(
|
||||
D.getContext(), attrs, ParamInfo, EllipsisLoc,
|
||||
D.getCXXScopeSpec().isSet() &&
|
||||
D.isFunctionDeclaratorAFunctionDeclaration());
|
||||
}
|
||||
void ParseParameterDeclarationClause(
|
||||
DeclaratorContext DeclaratorContext, ParsedAttributes &attrs,
|
||||
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo,
|
||||
SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration = false);
|
||||
|
||||
void ParseBracketDeclarator(Declarator &D);
|
||||
void ParseMisplacedBracketDeclarator(Declarator &D);
|
||||
bool MaybeParseTypeTransformTypeSpecifier(DeclSpec &DS);
|
||||
|
@ -3431,8 +3507,10 @@ private:
|
|||
UnqualifiedId &TemplateName,
|
||||
bool AllowTypeAnnotation = true,
|
||||
bool TypeConstraint = false);
|
||||
void AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS,
|
||||
bool IsClassName = false);
|
||||
void
|
||||
AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS,
|
||||
ImplicitTypenameContext AllowImplicitTypename,
|
||||
bool IsClassName = false);
|
||||
bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs,
|
||||
TemplateTy Template, SourceLocation OpenLoc);
|
||||
ParsedTemplateArgument ParseTemplateTemplateArgument();
|
||||
|
|
|
@ -326,6 +326,11 @@ public:
|
|||
// FIXME: Attributes should be included here.
|
||||
};
|
||||
|
||||
enum FriendSpecified : bool {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
private:
|
||||
// storage-class-specifier
|
||||
/*SCS*/unsigned StorageClassSpec : 3;
|
||||
|
@ -757,7 +762,10 @@ public:
|
|||
bool SetConstexprSpec(ConstexprSpecKind ConstexprKind, SourceLocation Loc,
|
||||
const char *&PrevSpec, unsigned &DiagID);
|
||||
|
||||
bool isFriendSpecified() const { return Friend_specified; }
|
||||
FriendSpecified isFriendSpecified() const {
|
||||
return static_cast<FriendSpecified>(Friend_specified);
|
||||
}
|
||||
|
||||
SourceLocation getFriendSpecLoc() const { return FriendLoc; }
|
||||
|
||||
bool isModulePrivateSpecified() const { return ModulePrivateLoc.isValid(); }
|
||||
|
@ -1801,6 +1809,13 @@ enum class DeclaratorContext {
|
|||
Association // C11 _Generic selection expression association.
|
||||
};
|
||||
|
||||
// Describes whether the current context is a context where an implicit
|
||||
// typename is allowed (C++2a [temp.res]p5]).
|
||||
enum class ImplicitTypenameContext {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
/// Information about one declarator, including the parsed type
|
||||
/// information and the identifier.
|
||||
///
|
||||
|
|
|
@ -2573,6 +2573,8 @@ public:
|
|||
bool IsCtorOrDtorName = false,
|
||||
bool WantNontrivialTypeSourceInfo = false,
|
||||
bool IsClassTemplateDeductionContext = true,
|
||||
ImplicitTypenameContext AllowImplicitTypename =
|
||||
ImplicitTypenameContext::No,
|
||||
IdentifierInfo **CorrectedII = nullptr);
|
||||
TypeSpecifierType isTagName(IdentifierInfo &II, Scope *S);
|
||||
bool isMicrosoftMissingTypename(const CXXScopeSpec *SS, Scope *S);
|
||||
|
@ -3073,6 +3075,10 @@ public:
|
|||
/// \c constexpr in C++11 or has an 'auto' return type in C++14).
|
||||
bool canSkipFunctionBody(Decl *D);
|
||||
|
||||
/// Determine whether \param D is function like (function or function
|
||||
/// template) for parsing.
|
||||
bool isDeclaratorFunctionLike(Declarator &D);
|
||||
|
||||
void computeNRVO(Stmt *Body, sema::FunctionScopeInfo *Scope);
|
||||
Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body);
|
||||
Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body, bool IsInstantiation);
|
||||
|
@ -8052,7 +8058,9 @@ public:
|
|||
TemplateTy Template, IdentifierInfo *TemplateII,
|
||||
SourceLocation TemplateIILoc, SourceLocation LAngleLoc,
|
||||
ASTTemplateArgsPtr TemplateArgs, SourceLocation RAngleLoc,
|
||||
bool IsCtorOrDtorName = false, bool IsClassName = false);
|
||||
bool IsCtorOrDtorName = false, bool IsClassName = false,
|
||||
ImplicitTypenameContext AllowImplicitTypename =
|
||||
ImplicitTypenameContext::No);
|
||||
|
||||
/// Parsed an elaborated-type-specifier that refers to a template-id,
|
||||
/// such as \c class T::template apply<U>.
|
||||
|
@ -8321,10 +8329,11 @@ public:
|
|||
/// \param SS the nested-name-specifier following the typename (e.g., 'T::').
|
||||
/// \param II the identifier we're retrieving (e.g., 'type' in the example).
|
||||
/// \param IdLoc the location of the identifier.
|
||||
TypeResult
|
||||
ActOnTypenameType(Scope *S, SourceLocation TypenameLoc,
|
||||
const CXXScopeSpec &SS, const IdentifierInfo &II,
|
||||
SourceLocation IdLoc);
|
||||
/// \param IsImplicitTypename context where T::type refers to a type.
|
||||
TypeResult ActOnTypenameType(
|
||||
Scope *S, SourceLocation TypenameLoc, const CXXScopeSpec &SS,
|
||||
const IdentifierInfo &II, SourceLocation IdLoc,
|
||||
ImplicitTypenameContext IsImplicitTypename = ImplicitTypenameContext::No);
|
||||
|
||||
/// Called when the parser has parsed a C++ typename
|
||||
/// specifier that ends in a template-id, e.g.,
|
||||
|
|
|
@ -2124,7 +2124,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
|
|||
return Actions.ConvertDeclToDeclGroup(TheDecl);
|
||||
}
|
||||
|
||||
if (isDeclarationSpecifier()) {
|
||||
if (isDeclarationSpecifier(ImplicitTypenameContext::No)) {
|
||||
// If there is an invalid declaration specifier right after the
|
||||
// function prototype, then we must be in a missing semicolon case
|
||||
// where this isn't actually a body. Just fall through into the code
|
||||
|
@ -2247,7 +2247,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
|
|||
// Okay, there was no semicolon and one was expected. If we see a
|
||||
// declaration specifier, just assume it was missing and continue parsing.
|
||||
// Otherwise things are very confused and we skip to recover.
|
||||
if (!isDeclarationSpecifier()) {
|
||||
if (!isDeclarationSpecifier(ImplicitTypenameContext::No)) {
|
||||
SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
|
||||
TryConsumeToken(tok::semi);
|
||||
}
|
||||
|
@ -2566,12 +2566,14 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
|
|||
/// type-qualifier specifier-qualifier-list[opt]
|
||||
/// [GNU] attributes specifier-qualifier-list[opt]
|
||||
///
|
||||
void Parser::ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS,
|
||||
DeclSpecContext DSC) {
|
||||
void Parser::ParseSpecifierQualifierList(
|
||||
DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename,
|
||||
AccessSpecifier AS, DeclSpecContext DSC) {
|
||||
/// specifier-qualifier-list is a subset of declaration-specifiers. Just
|
||||
/// parse declaration-specifiers and complain about extra stuff.
|
||||
/// TODO: diagnose attribute-specifiers and alignment-specifiers.
|
||||
ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC);
|
||||
ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC, nullptr,
|
||||
AllowImplicitTypename);
|
||||
|
||||
// Validate declspec for type-name.
|
||||
unsigned Specs = DS.getParsedSpecifiers();
|
||||
|
@ -2881,24 +2883,50 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
|
|||
/// DeclaratorContext enumerator values.
|
||||
Parser::DeclSpecContext
|
||||
Parser::getDeclSpecContextFromDeclaratorContext(DeclaratorContext Context) {
|
||||
if (Context == DeclaratorContext::Member)
|
||||
switch (Context) {
|
||||
case DeclaratorContext::Member:
|
||||
return DeclSpecContext::DSC_class;
|
||||
if (Context == DeclaratorContext::File)
|
||||
case DeclaratorContext::File:
|
||||
return DeclSpecContext::DSC_top_level;
|
||||
if (Context == DeclaratorContext::TemplateParam)
|
||||
case DeclaratorContext::TemplateParam:
|
||||
return DeclSpecContext::DSC_template_param;
|
||||
if (Context == DeclaratorContext::TemplateArg ||
|
||||
Context == DeclaratorContext::TemplateTypeArg)
|
||||
case DeclaratorContext::TemplateArg:
|
||||
return DeclSpecContext::DSC_template_arg;
|
||||
case DeclaratorContext::TemplateTypeArg:
|
||||
return DeclSpecContext::DSC_template_type_arg;
|
||||
if (Context == DeclaratorContext::TrailingReturn ||
|
||||
Context == DeclaratorContext::TrailingReturnVar)
|
||||
case DeclaratorContext::TrailingReturn:
|
||||
case DeclaratorContext::TrailingReturnVar:
|
||||
return DeclSpecContext::DSC_trailing;
|
||||
if (Context == DeclaratorContext::AliasDecl ||
|
||||
Context == DeclaratorContext::AliasTemplate)
|
||||
case DeclaratorContext::AliasDecl:
|
||||
case DeclaratorContext::AliasTemplate:
|
||||
return DeclSpecContext::DSC_alias_declaration;
|
||||
if (Context == DeclaratorContext::Association)
|
||||
case DeclaratorContext::Association:
|
||||
return DeclSpecContext::DSC_association;
|
||||
return DeclSpecContext::DSC_normal;
|
||||
case DeclaratorContext::TypeName:
|
||||
return DeclSpecContext::DSC_type_specifier;
|
||||
case DeclaratorContext::Condition:
|
||||
return DeclSpecContext::DSC_condition;
|
||||
case DeclaratorContext::ConversionId:
|
||||
return DeclSpecContext::DSC_conv_operator;
|
||||
case DeclaratorContext::Prototype:
|
||||
case DeclaratorContext::ObjCResult:
|
||||
case DeclaratorContext::ObjCParameter:
|
||||
case DeclaratorContext::KNRTypeList:
|
||||
case DeclaratorContext::FunctionalCast:
|
||||
case DeclaratorContext::Block:
|
||||
case DeclaratorContext::ForInit:
|
||||
case DeclaratorContext::SelectionInit:
|
||||
case DeclaratorContext::CXXNew:
|
||||
case DeclaratorContext::CXXCatch:
|
||||
case DeclaratorContext::ObjCCatch:
|
||||
case DeclaratorContext::BlockLiteral:
|
||||
case DeclaratorContext::LambdaExpr:
|
||||
case DeclaratorContext::LambdaExprParameter:
|
||||
case DeclaratorContext::RequiresExpr:
|
||||
return DeclSpecContext::DSC_normal;
|
||||
}
|
||||
|
||||
llvm_unreachable("Missing DeclaratorContext case");
|
||||
}
|
||||
|
||||
/// ParseAlignArgument - Parse the argument to an alignment-specifier.
|
||||
|
@ -3134,11 +3162,10 @@ static void SetupFixedPointError(const LangOptions &LangOpts,
|
|||
/// [OpenCL] '__kernel'
|
||||
/// 'friend': [C++ dcl.friend]
|
||||
/// 'constexpr': [C++0x dcl.constexpr]
|
||||
void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
|
||||
const ParsedTemplateInfo &TemplateInfo,
|
||||
AccessSpecifier AS,
|
||||
DeclSpecContext DSContext,
|
||||
LateParsedAttrList *LateAttrs) {
|
||||
void Parser::ParseDeclarationSpecifiers(
|
||||
DeclSpec &DS, const ParsedTemplateInfo &TemplateInfo, AccessSpecifier AS,
|
||||
DeclSpecContext DSContext, LateParsedAttrList *LateAttrs,
|
||||
ImplicitTypenameContext AllowImplicitTypename) {
|
||||
if (DS.getSourceRange().isInvalid()) {
|
||||
// Start the range at the current token but make the end of the range
|
||||
// invalid. This will make the entire range invalid unless we successfully
|
||||
|
@ -3147,6 +3174,14 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
|
|||
DS.SetRangeEnd(SourceLocation());
|
||||
}
|
||||
|
||||
// If we are in a operator context, convert it back into a type specifier
|
||||
// context for better error handling later on.
|
||||
if (DSContext == DeclSpecContext::DSC_conv_operator) {
|
||||
// No implicit typename here.
|
||||
AllowImplicitTypename = ImplicitTypenameContext::No;
|
||||
DSContext = DeclSpecContext::DSC_type_specifier;
|
||||
}
|
||||
|
||||
bool EnteringContext = (DSContext == DeclSpecContext::DSC_class ||
|
||||
DSContext == DeclSpecContext::DSC_top_level);
|
||||
bool AttrsLastTime = false;
|
||||
|
@ -3335,7 +3370,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
|
|||
DSContext == DeclSpecContext::DSC_class) &&
|
||||
TemplateId->Name &&
|
||||
Actions.isCurrentClassName(*TemplateId->Name, getCurScope(), &SS) &&
|
||||
isConstructorDeclarator(/*Unqualified=*/false)) {
|
||||
isConstructorDeclarator(/*Unqualified=*/false,
|
||||
/*DeductionGuide=*/false,
|
||||
DS.isFriendSpecified())) {
|
||||
// The user meant this to be an out-of-line constructor
|
||||
// definition, but template arguments are not allowed
|
||||
// there. Just allow this as a constructor; we'll
|
||||
|
@ -3347,7 +3384,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
|
|||
ConsumeAnnotationToken(); // The C++ scope.
|
||||
assert(Tok.is(tok::annot_template_id) &&
|
||||
"ParseOptionalCXXScopeSpecifier not working");
|
||||
AnnotateTemplateIdTokenAsType(SS);
|
||||
AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -3374,6 +3411,16 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
|
|||
ConsumeAnnotationToken(); // The typename
|
||||
}
|
||||
|
||||
if (AllowImplicitTypename == ImplicitTypenameContext::Yes &&
|
||||
Next.is(tok::annot_template_id) &&
|
||||
static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue())
|
||||
->Kind == TNK_Dependent_template_name) {
|
||||
DS.getTypeSpecScope() = SS;
|
||||
ConsumeAnnotationToken(); // The C++ scope.
|
||||
AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Next.isNot(tok::identifier))
|
||||
goto DoneWithDeclSpec;
|
||||
|
||||
|
@ -3384,7 +3431,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
|
|||
DSContext == DeclSpecContext::DSC_class) &&
|
||||
Actions.isCurrentClassName(*Next.getIdentifierInfo(), getCurScope(),
|
||||
&SS) &&
|
||||
isConstructorDeclarator(/*Unqualified*/ false))
|
||||
isConstructorDeclarator(/*Unqualified=*/false,
|
||||
/*DeductionGuide=*/false,
|
||||
DS.isFriendSpecified()))
|
||||
goto DoneWithDeclSpec;
|
||||
|
||||
// C++20 [temp.spec] 13.9/6.
|
||||
|
@ -3393,12 +3442,12 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
|
|||
// - `return type`.
|
||||
SuppressAccessChecks SAC(*this, IsTemplateSpecOrInst);
|
||||
|
||||
ParsedType TypeRep =
|
||||
Actions.getTypeName(*Next.getIdentifierInfo(), Next.getLocation(),
|
||||
getCurScope(), &SS, false, false, nullptr,
|
||||
/*IsCtorOrDtorName=*/false,
|
||||
/*WantNontrivialTypeSourceInfo=*/true,
|
||||
isClassTemplateDeductionContext(DSContext));
|
||||
ParsedType TypeRep = Actions.getTypeName(
|
||||
*Next.getIdentifierInfo(), Next.getLocation(), getCurScope(), &SS,
|
||||
false, false, nullptr,
|
||||
/*IsCtorOrDtorName=*/false,
|
||||
/*WantNontrivialTypeSourceInfo=*/true,
|
||||
isClassTemplateDeductionContext(DSContext), AllowImplicitTypename);
|
||||
|
||||
if (IsTemplateSpecOrInst)
|
||||
SAC.done();
|
||||
|
@ -3562,7 +3611,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
|
|||
// check whether this is a constructor declaration.
|
||||
if (getLangOpts().CPlusPlus && DSContext == DeclSpecContext::DSC_class &&
|
||||
Actions.isCurrentClassName(*Tok.getIdentifierInfo(), getCurScope()) &&
|
||||
isConstructorDeclarator(/*Unqualified*/true))
|
||||
isConstructorDeclarator(/*Unqualified=*/true,
|
||||
/*DeductionGuide=*/false,
|
||||
DS.isFriendSpecified()))
|
||||
goto DoneWithDeclSpec;
|
||||
|
||||
ParsedType TypeRep = Actions.getTypeName(
|
||||
|
@ -3695,13 +3746,15 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
|
|||
// constructor declaration.
|
||||
if (getLangOpts().CPlusPlus && DSContext == DeclSpecContext::DSC_class &&
|
||||
Actions.isCurrentClassName(*TemplateId->Name, getCurScope()) &&
|
||||
isConstructorDeclarator(/*Unqualified=*/true))
|
||||
isConstructorDeclarator(/*Unqualified=*/true,
|
||||
/*DeductionGuide=*/false,
|
||||
DS.isFriendSpecified()))
|
||||
goto DoneWithDeclSpec;
|
||||
|
||||
// Turn the template-id annotation token into a type annotation
|
||||
// token, then try again to parse it as a type-specifier.
|
||||
CXXScopeSpec SS;
|
||||
AnnotateTemplateIdTokenAsType(SS);
|
||||
AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -4742,7 +4795,10 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
|
|||
// enum E : int *p;
|
||||
// declares 'enum E : int; E *p;' not 'enum E : int*; E p;'.
|
||||
DeclSpec DS(AttrFactory);
|
||||
ParseSpecifierQualifierList(DS, AS, DeclSpecContext::DSC_type_specifier);
|
||||
// enum-base is not assumed to be a type and therefore requires the
|
||||
// typename keyword [p0634r3].
|
||||
ParseSpecifierQualifierList(DS, ImplicitTypenameContext::No, AS,
|
||||
DeclSpecContext::DSC_type_specifier);
|
||||
Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
|
||||
DeclaratorContext::TypeName);
|
||||
BaseType = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
|
||||
|
@ -5301,9 +5357,13 @@ bool Parser::isTypeSpecifierQualifier() {
|
|||
/// isDeclarationSpecifier() - Return true if the current token is part of a
|
||||
/// declaration specifier.
|
||||
///
|
||||
/// \param AllowImplicitTypename whether this is a context where T::type [T
|
||||
/// dependent] can appear.
|
||||
/// \param DisambiguatingWithExpression True to indicate that the purpose of
|
||||
/// this check is to disambiguate between an expression and a declaration.
|
||||
bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
|
||||
bool Parser::isDeclarationSpecifier(
|
||||
ImplicitTypenameContext AllowImplicitTypename,
|
||||
bool DisambiguatingWithExpression) {
|
||||
switch (Tok.getKind()) {
|
||||
default: return false;
|
||||
|
||||
|
@ -5323,7 +5383,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
|
|||
case tok::kw_typename: // typename T::type
|
||||
// Annotate typenames and C++ scope specifiers. If we get one, just
|
||||
// recurse to handle whatever we get.
|
||||
if (TryAnnotateTypeOrScopeToken())
|
||||
if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename))
|
||||
return true;
|
||||
if (TryAnnotateTypeConstraint())
|
||||
return true;
|
||||
|
@ -5339,7 +5399,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
|
|||
isStartOfObjCClassMessageMissingOpenBracket())
|
||||
return false;
|
||||
|
||||
return isDeclarationSpecifier();
|
||||
return isDeclarationSpecifier(AllowImplicitTypename);
|
||||
|
||||
case tok::coloncolon: // ::foo::bar
|
||||
if (NextToken().is(tok::kw_new) || // ::new
|
||||
|
@ -5350,7 +5410,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
|
|||
// recurse to handle whatever we get.
|
||||
if (TryAnnotateTypeOrScopeToken())
|
||||
return true;
|
||||
return isDeclarationSpecifier();
|
||||
return isDeclarationSpecifier(ImplicitTypenameContext::No);
|
||||
|
||||
// storage-class-specifier
|
||||
case tok::kw_typedef:
|
||||
|
@ -5529,7 +5589,8 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide) {
|
||||
bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
|
||||
DeclSpec::FriendSpecified IsFriend) {
|
||||
TentativeParsingAction TPA(*this);
|
||||
|
||||
// Parse the C++ scope specifier.
|
||||
|
@ -5593,8 +5654,11 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide) {
|
|||
// Check whether the next token(s) are part of a declaration
|
||||
// specifier, in which case we have the start of a parameter and,
|
||||
// therefore, we know that this is a constructor.
|
||||
// Due to an ambiguity with implicit typename, the above is not enough.
|
||||
// Additionally, check to see if we are a friend.
|
||||
bool IsConstructor = false;
|
||||
if (isDeclarationSpecifier())
|
||||
if (isDeclarationSpecifier(IsFriend ? ImplicitTypenameContext::No
|
||||
: ImplicitTypenameContext::Yes))
|
||||
IsConstructor = true;
|
||||
else if (Tok.is(tok::identifier) ||
|
||||
(Tok.is(tok::annot_cxxscope) && NextToken().is(tok::identifier))) {
|
||||
|
@ -6409,10 +6473,27 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
|
|||
// is not, the declarator has been fully parsed.
|
||||
bool IsAmbiguous = false;
|
||||
if (getLangOpts().CPlusPlus && D.mayBeFollowedByCXXDirectInit()) {
|
||||
// C++2a [temp.res]p5
|
||||
// A qualified-id is assumed to name a type if
|
||||
// - [...]
|
||||
// - it is a decl-specifier of the decl-specifier-seq of a
|
||||
// - [...]
|
||||
// - parameter-declaration in a member-declaration [...]
|
||||
// - parameter-declaration in a declarator of a function or function
|
||||
// template declaration whose declarator-id is qualified [...]
|
||||
auto AllowImplicitTypename = ImplicitTypenameContext::No;
|
||||
if (D.getCXXScopeSpec().isSet())
|
||||
AllowImplicitTypename =
|
||||
(ImplicitTypenameContext)Actions.isDeclaratorFunctionLike(D);
|
||||
else if (D.getContext() == DeclaratorContext::Member) {
|
||||
AllowImplicitTypename = ImplicitTypenameContext::Yes;
|
||||
}
|
||||
|
||||
// The name of the declarator, if any, is tentatively declared within
|
||||
// a possible direct initializer.
|
||||
TentativelyDeclaredIdentifiers.push_back(D.getIdentifier());
|
||||
bool IsFunctionDecl = isCXXFunctionDeclarator(&IsAmbiguous);
|
||||
bool IsFunctionDecl =
|
||||
isCXXFunctionDeclarator(&IsAmbiguous, AllowImplicitTypename);
|
||||
TentativelyDeclaredIdentifiers.pop_back();
|
||||
if (!IsFunctionDecl)
|
||||
break;
|
||||
|
@ -6571,11 +6652,12 @@ void Parser::ParseParenDeclarator(Declarator &D) {
|
|||
// If this can't be an abstract-declarator, this *must* be a grouping
|
||||
// paren, because we haven't seen the identifier yet.
|
||||
isGrouping = true;
|
||||
} else if (Tok.is(tok::r_paren) || // 'int()' is a function.
|
||||
} else if (Tok.is(tok::r_paren) || // 'int()' is a function.
|
||||
(getLangOpts().CPlusPlus && Tok.is(tok::ellipsis) &&
|
||||
NextToken().is(tok::r_paren)) || // C++ int(...)
|
||||
isDeclarationSpecifier() || // 'int(int)' is a function.
|
||||
isCXX11AttributeSpecifier()) { // 'int([[]]int)' is a function.
|
||||
isDeclarationSpecifier(
|
||||
ImplicitTypenameContext::No) || // 'int(int)' is a function.
|
||||
isCXX11AttributeSpecifier()) { // 'int([[]]int)' is a function.
|
||||
// This handles C99 6.7.5.3p11: in "typedef int X; void foo(X)", X is
|
||||
// considered to be a type, not a K&R identifier-list.
|
||||
isGrouping = false;
|
||||
|
@ -6743,8 +6825,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
|
|||
ProhibitAttributes(FnAttrs);
|
||||
} else {
|
||||
if (Tok.isNot(tok::r_paren))
|
||||
ParseParameterDeclarationClause(D.getContext(), FirstArgAttrs, ParamInfo,
|
||||
EllipsisLoc);
|
||||
ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, EllipsisLoc);
|
||||
else if (RequiresArg)
|
||||
Diag(Tok, diag::err_argument_required_after_attribute);
|
||||
|
||||
|
@ -7003,7 +7084,7 @@ void Parser::ParseFunctionDeclaratorIdentifierList(
|
|||
void Parser::ParseParameterDeclarationClause(
|
||||
DeclaratorContext DeclaratorCtx, ParsedAttributes &FirstArgAttrs,
|
||||
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo,
|
||||
SourceLocation &EllipsisLoc) {
|
||||
SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration) {
|
||||
|
||||
// Avoid exceeding the maximum function scope depth.
|
||||
// See https://bugs.llvm.org/show_bug.cgi?id=19607
|
||||
|
@ -7017,6 +7098,23 @@ void Parser::ParseParameterDeclarationClause(
|
|||
return;
|
||||
}
|
||||
|
||||
// C++2a [temp.res]p5
|
||||
// A qualified-id is assumed to name a type if
|
||||
// - [...]
|
||||
// - it is a decl-specifier of the decl-specifier-seq of a
|
||||
// - [...]
|
||||
// - parameter-declaration in a member-declaration [...]
|
||||
// - parameter-declaration in a declarator of a function or function
|
||||
// template declaration whose declarator-id is qualified [...]
|
||||
// - parameter-declaration in a lambda-declarator [...]
|
||||
auto AllowImplicitTypename = ImplicitTypenameContext::No;
|
||||
if (DeclaratorCtx == DeclaratorContext::Member ||
|
||||
DeclaratorCtx == DeclaratorContext::LambdaExpr ||
|
||||
DeclaratorCtx == DeclaratorContext::RequiresExpr ||
|
||||
IsACXXFunctionDeclaration) {
|
||||
AllowImplicitTypename = ImplicitTypenameContext::Yes;
|
||||
}
|
||||
|
||||
do {
|
||||
// FIXME: Issue a diagnostic if we parsed an attribute-specifier-seq
|
||||
// before deciding this was a parameter-declaration-clause.
|
||||
|
@ -7046,7 +7144,9 @@ void Parser::ParseParameterDeclarationClause(
|
|||
|
||||
SourceLocation DSStart = Tok.getLocation();
|
||||
|
||||
ParseDeclarationSpecifiers(DS);
|
||||
ParseDeclarationSpecifiers(DS, /*TemplateInfo=*/ParsedTemplateInfo(),
|
||||
AS_none, DeclSpecContext::DSC_normal,
|
||||
/*LateAttrs=*/nullptr, AllowImplicitTypename);
|
||||
DS.takeAttributesFrom(ArgDeclSpecAttrs);
|
||||
|
||||
// Parse the declarator. This is "PrototypeContext" or
|
||||
|
|
|
@ -1273,7 +1273,8 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
|
|||
if (Tok.is(tok::annot_template_id)) {
|
||||
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
|
||||
if (TemplateId->mightBeType()) {
|
||||
AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/ true);
|
||||
AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::No,
|
||||
/*IsClassName=*/true);
|
||||
|
||||
assert(Tok.is(tok::annot_typename) && "template-id -> type failed");
|
||||
TypeResult Type = getTypeAnnotation(Tok);
|
||||
|
@ -1315,7 +1316,8 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
|
|||
return true;
|
||||
if (Tok.is(tok::annot_template_id) &&
|
||||
takeTemplateIdAnnotation(Tok)->mightBeType())
|
||||
AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/ true);
|
||||
AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::No,
|
||||
/*IsClassName=*/true);
|
||||
|
||||
// If we didn't end up with a typename token, there's nothing more we
|
||||
// can do.
|
||||
|
@ -1336,7 +1338,8 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
|
|||
*Id, IdLoc, getCurScope(), &SS, /*isClassName=*/true, false, nullptr,
|
||||
/*IsCtorOrDtorName=*/false,
|
||||
/*WantNontrivialTypeSourceInfo=*/true,
|
||||
/*IsClassTemplateDeductionContext*/ false, &CorrectedII);
|
||||
/*IsClassTemplateDeductionContext=*/false, ImplicitTypenameContext::No,
|
||||
&CorrectedII);
|
||||
if (!Type) {
|
||||
Diag(IdLoc, diag::err_expected_class_name);
|
||||
return true;
|
||||
|
@ -3761,7 +3764,8 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) {
|
|||
? takeTemplateIdAnnotation(Tok)
|
||||
: nullptr;
|
||||
if (TemplateId && TemplateId->mightBeType()) {
|
||||
AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/ true);
|
||||
AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::No,
|
||||
/*IsClassName=*/true);
|
||||
assert(Tok.is(tok::annot_typename) && "template-id -> type failed");
|
||||
TemplateTypeTy = getTypeAnnotation(Tok);
|
||||
ConsumeAnnotationToken();
|
||||
|
|
|
@ -1600,7 +1600,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
|
|||
ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
|
||||
/*ObjectHasErrors=*/false,
|
||||
/*EnteringContext=*/false);
|
||||
AnnotateTemplateIdTokenAsType(SS);
|
||||
AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::Yes);
|
||||
return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr,
|
||||
isTypeCast, isVectorLiteral,
|
||||
NotPrimaryExpression);
|
||||
|
@ -1619,7 +1619,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
|
|||
// translate it into a type and continue parsing as a cast
|
||||
// expression.
|
||||
CXXScopeSpec SS;
|
||||
AnnotateTemplateIdTokenAsType(SS);
|
||||
AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::Yes);
|
||||
return ParseCastExpression(ParseKind, isAddressOfOperand,
|
||||
NotCastExpr, isTypeCast, isVectorLiteral,
|
||||
NotPrimaryExpression);
|
||||
|
|
|
@ -1414,8 +1414,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
|
|||
Actions.RecordParsingTemplateParameterDepth(
|
||||
CurTemplateDepthTracker.getOriginalDepth());
|
||||
|
||||
ParseParameterDeclarationClause(D.getContext(), Attr, ParamInfo,
|
||||
EllipsisLoc);
|
||||
ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc);
|
||||
// For a generic lambda, each 'auto' within the parameter declaration
|
||||
// clause creates a template type parameter, so increment the depth.
|
||||
// If we've parsed any explicit template parameters, then the depth will
|
||||
|
@ -1522,7 +1521,8 @@ ExprResult Parser::ParseCXXCasts() {
|
|||
|
||||
// Parse the common declaration-specifiers piece.
|
||||
DeclSpec DS(AttrFactory);
|
||||
ParseSpecifierQualifierList(DS);
|
||||
ParseSpecifierQualifierList(DS, /*AccessSpecifier=*/AS_none,
|
||||
DeclSpecContext::DSC_type_specifier);
|
||||
|
||||
// Parse the abstract-declarator, if present.
|
||||
Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
|
||||
|
@ -2320,8 +2320,9 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) {
|
|||
/// type-specifier-seq: [C++ 8.1]
|
||||
/// type-specifier type-specifier-seq[opt]
|
||||
///
|
||||
bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) {
|
||||
ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_type_specifier);
|
||||
bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS, DeclaratorContext Context) {
|
||||
ParseSpecifierQualifierList(DS, AS_none,
|
||||
getDeclSpecContextFromDeclaratorContext(Context));
|
||||
DS.Finish(Actions, Actions.getASTContext().getPrintingPolicy());
|
||||
return false;
|
||||
}
|
||||
|
@ -2737,7 +2738,8 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
|
|||
|
||||
// Parse the type-specifier-seq.
|
||||
DeclSpec DS(AttrFactory);
|
||||
if (ParseCXXTypeSpecifierSeq(DS)) // FIXME: ObjectType?
|
||||
if (ParseCXXTypeSpecifierSeq(
|
||||
DS, DeclaratorContext::ConversionId)) // FIXME: ObjectType?
|
||||
return true;
|
||||
|
||||
// Parse the conversion-declarator, which is merely a sequence of
|
||||
|
|
|
@ -1420,12 +1420,15 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
|
|||
///
|
||||
/// \param SS The scope specifier appearing before the template-id, if any.
|
||||
///
|
||||
/// \param AllowImplicitTypename whether this is a context where T::type
|
||||
/// denotes a dependent type.
|
||||
/// \param IsClassName Is this template-id appearing in a context where we
|
||||
/// know it names a class, such as in an elaborated-type-specifier or
|
||||
/// base-specifier? ('typename' and 'template' are unneeded and disallowed
|
||||
/// in those contexts.)
|
||||
void Parser::AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS,
|
||||
bool IsClassName) {
|
||||
void Parser::AnnotateTemplateIdTokenAsType(
|
||||
CXXScopeSpec &SS, ImplicitTypenameContext AllowImplicitTypename,
|
||||
bool IsClassName) {
|
||||
assert(Tok.is(tok::annot_template_id) && "Requires template-id tokens");
|
||||
|
||||
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
|
||||
|
@ -1443,7 +1446,7 @@ void Parser::AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS,
|
|||
TemplateId->Template, TemplateId->Name,
|
||||
TemplateId->TemplateNameLoc, TemplateId->LAngleLoc,
|
||||
TemplateArgsPtr, TemplateId->RAngleLoc,
|
||||
/*IsCtorOrDtorName*/ false, IsClassName);
|
||||
/*IsCtorOrDtorName=*/false, IsClassName, AllowImplicitTypename);
|
||||
// Create the new "type" annotation token.
|
||||
Tok.setKind(tok::annot_typename);
|
||||
setTypeAnnotation(Tok, Type);
|
||||
|
|
|
@ -111,8 +111,8 @@ bool Parser::isCXXSimpleDeclaration(bool AllowForRangeDecl) {
|
|||
// a case.
|
||||
|
||||
bool InvalidAsDeclaration = false;
|
||||
TPResult TPR = isCXXDeclarationSpecifier(TPResult::False,
|
||||
&InvalidAsDeclaration);
|
||||
TPResult TPR = isCXXDeclarationSpecifier(
|
||||
ImplicitTypenameContext::No, TPResult::False, &InvalidAsDeclaration);
|
||||
if (TPR != TPResult::Ambiguous)
|
||||
return TPR != TPResult::False; // Returns true for TPResult::True or
|
||||
// TPResult::Error.
|
||||
|
@ -233,7 +233,7 @@ Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) {
|
|||
// simple-declaration. Don't bother calling isCXXDeclarationSpecifier in the
|
||||
// overwhelmingly common case that the next token is a '('.
|
||||
if (Tok.isNot(tok::l_paren)) {
|
||||
TPResult TPR = isCXXDeclarationSpecifier();
|
||||
TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No);
|
||||
if (TPR == TPResult::Ambiguous)
|
||||
return TPResult::True;
|
||||
if (TPR == TPResult::True || TPR == TPResult::Error)
|
||||
|
@ -442,7 +442,8 @@ bool Parser::isEnumBase(bool AllowSemi) {
|
|||
// FIXME: We could disallow non-type decl-specifiers here, but it makes no
|
||||
// difference: those specifiers are ill-formed regardless of the
|
||||
// interpretation.
|
||||
TPResult R = isCXXDeclarationSpecifier(/*BracedCastResult*/ TPResult::True,
|
||||
TPResult R = isCXXDeclarationSpecifier(ImplicitTypenameContext::No,
|
||||
/*BracedCastResult=*/TPResult::True,
|
||||
&InvalidAsDeclSpec);
|
||||
if (R == TPResult::Ambiguous) {
|
||||
// We either have a decl-specifier followed by '(' or an undeclared
|
||||
|
@ -456,7 +457,8 @@ bool Parser::isEnumBase(bool AllowSemi) {
|
|||
return true;
|
||||
|
||||
// A second decl-specifier unambiguously indicatges an enum-base.
|
||||
R = isCXXDeclarationSpecifier(TPResult::True, &InvalidAsDeclSpec);
|
||||
R = isCXXDeclarationSpecifier(ImplicitTypenameContext::No, TPResult::True,
|
||||
&InvalidAsDeclSpec);
|
||||
}
|
||||
|
||||
return R != TPResult::False;
|
||||
|
@ -487,7 +489,7 @@ Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement,
|
|||
|
||||
if (CanBeInitStatement && Tok.is(tok::kw_using))
|
||||
return ConditionOrInitStatement::InitStmtDecl;
|
||||
if (State.update(isCXXDeclarationSpecifier()))
|
||||
if (State.update(isCXXDeclarationSpecifier(ImplicitTypenameContext::No)))
|
||||
return State.result();
|
||||
|
||||
// It might be a declaration; we need tentative parsing.
|
||||
|
@ -573,7 +575,7 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) {
|
|||
// type. The resolution is that any construct that could possibly be a type-id
|
||||
// in its syntactic context shall be considered a type-id.
|
||||
|
||||
TPResult TPR = isCXXDeclarationSpecifier();
|
||||
TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No);
|
||||
if (TPR != TPResult::Ambiguous)
|
||||
return TPR != TPResult::False; // Returns true for TPResult::True or
|
||||
// TPResult::Error.
|
||||
|
@ -934,7 +936,7 @@ Parser::TPResult Parser::TryParseOperatorId() {
|
|||
// Maybe this is a conversion-function-id.
|
||||
bool AnyDeclSpecifiers = false;
|
||||
while (true) {
|
||||
TPResult TPR = isCXXDeclarationSpecifier();
|
||||
TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No);
|
||||
if (TPR == TPResult::Error)
|
||||
return TPR;
|
||||
if (TPR == TPResult::False) {
|
||||
|
@ -1039,10 +1041,11 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
|
|||
} else if (Tok.is(tok::l_paren)) {
|
||||
ConsumeParen();
|
||||
if (mayBeAbstract &&
|
||||
(Tok.is(tok::r_paren) || // 'int()' is a function.
|
||||
// 'int(...)' is a function.
|
||||
(Tok.is(tok::r_paren) || // 'int()' is a function.
|
||||
// 'int(...)' is a function.
|
||||
(Tok.is(tok::ellipsis) && NextToken().is(tok::r_paren)) ||
|
||||
isDeclarationSpecifier())) { // 'int(int)' is a function.
|
||||
isDeclarationSpecifier(
|
||||
ImplicitTypenameContext::No))) { // 'int(int)' is a function.
|
||||
// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
|
||||
// exception-specification[opt]
|
||||
TPResult TPR = TryParseFunctionDeclarator();
|
||||
|
@ -1247,7 +1250,8 @@ public:
|
|||
/// [GNU] restrict
|
||||
///
|
||||
Parser::TPResult
|
||||
Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
||||
Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
|
||||
Parser::TPResult BracedCastResult,
|
||||
bool *InvalidAsDeclSpec) {
|
||||
auto IsPlaceholderSpecifier = [&] (TemplateIdAnnotation *TemplateId,
|
||||
int Lookahead) {
|
||||
|
@ -1290,7 +1294,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
// template template argument, we'll undo this when checking the
|
||||
// validity of the argument.
|
||||
if (getLangOpts().CPlusPlus17) {
|
||||
if (TryAnnotateTypeOrScopeToken())
|
||||
if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename))
|
||||
return TPResult::Error;
|
||||
if (Tok.isNot(tok::identifier))
|
||||
break;
|
||||
|
@ -1311,7 +1315,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
// a missing 'typename' keyword. Don't use TryAnnotateName in this case,
|
||||
// since it will annotate as a primary expression, and we want to use the
|
||||
// "missing 'typename'" logic.
|
||||
if (TryAnnotateTypeOrScopeToken())
|
||||
if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename))
|
||||
return TPResult::Error;
|
||||
// If annotation failed, assume it's a non-type.
|
||||
// FIXME: If this happens due to an undeclared identifier, treat it as
|
||||
|
@ -1321,15 +1325,17 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
}
|
||||
|
||||
// We annotated this token as something. Recurse to handle whatever we got.
|
||||
return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
|
||||
return isCXXDeclarationSpecifier(AllowImplicitTypename, BracedCastResult,
|
||||
InvalidAsDeclSpec);
|
||||
}
|
||||
|
||||
case tok::kw_typename: // typename T::type
|
||||
// Annotate typenames and C++ scope specifiers. If we get one, just
|
||||
// recurse to handle whatever we get.
|
||||
if (TryAnnotateTypeOrScopeToken())
|
||||
if (TryAnnotateTypeOrScopeToken(ImplicitTypenameContext::Yes))
|
||||
return TPResult::Error;
|
||||
return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
|
||||
return isCXXDeclarationSpecifier(ImplicitTypenameContext::Yes,
|
||||
BracedCastResult, InvalidAsDeclSpec);
|
||||
|
||||
case tok::coloncolon: { // ::foo::bar
|
||||
const Token &Next = NextToken();
|
||||
|
@ -1342,9 +1348,10 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
case tok::kw_decltype:
|
||||
// Annotate typenames and C++ scope specifiers. If we get one, just
|
||||
// recurse to handle whatever we get.
|
||||
if (TryAnnotateTypeOrScopeToken())
|
||||
if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename))
|
||||
return TPResult::Error;
|
||||
return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
|
||||
return isCXXDeclarationSpecifier(AllowImplicitTypename, BracedCastResult,
|
||||
InvalidAsDeclSpec);
|
||||
|
||||
// decl-specifier:
|
||||
// storage-class-specifier
|
||||
|
@ -1474,14 +1481,14 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
if (TemplateId->Kind != TNK_Type_template)
|
||||
return TPResult::False;
|
||||
CXXScopeSpec SS;
|
||||
AnnotateTemplateIdTokenAsType(SS);
|
||||
AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename);
|
||||
assert(Tok.is(tok::annot_typename));
|
||||
goto case_typename;
|
||||
}
|
||||
|
||||
case tok::annot_cxxscope: // foo::bar or ::foo::bar, but already parsed
|
||||
// We've already annotated a scope; try to annotate a type.
|
||||
if (TryAnnotateTypeOrScopeToken())
|
||||
if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename))
|
||||
return TPResult::Error;
|
||||
if (!Tok.is(tok::annot_typename)) {
|
||||
if (Tok.is(tok::annot_cxxscope) &&
|
||||
|
@ -1512,8 +1519,8 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
bool isIdentifier = Tok.is(tok::identifier);
|
||||
TPResult TPR = TPResult::False;
|
||||
if (!isIdentifier)
|
||||
TPR = isCXXDeclarationSpecifier(BracedCastResult,
|
||||
InvalidAsDeclSpec);
|
||||
TPR = isCXXDeclarationSpecifier(
|
||||
AllowImplicitTypename, BracedCastResult, InvalidAsDeclSpec);
|
||||
|
||||
if (isIdentifier ||
|
||||
TPR == TPResult::True || TPR == TPResult::Error)
|
||||
|
@ -1539,7 +1546,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
} else {
|
||||
// Try to resolve the name. If it doesn't exist, assume it was
|
||||
// intended to name a type and keep disambiguating.
|
||||
switch (TryAnnotateName()) {
|
||||
switch (TryAnnotateName(/*CCC=*/nullptr, AllowImplicitTypename)) {
|
||||
case ANK_Error:
|
||||
return TPResult::Error;
|
||||
case ANK_TentativeDecl:
|
||||
|
@ -1569,7 +1576,8 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
|||
// Annotated it, check again.
|
||||
assert(Tok.isNot(tok::annot_cxxscope) ||
|
||||
NextToken().isNot(tok::identifier));
|
||||
return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
|
||||
return isCXXDeclarationSpecifier(AllowImplicitTypename,
|
||||
BracedCastResult, InvalidAsDeclSpec);
|
||||
}
|
||||
}
|
||||
return TPResult::False;
|
||||
|
@ -1828,7 +1836,8 @@ Parser::TPResult Parser::TryParseProtocolQualifiers() {
|
|||
/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
|
||||
/// exception-specification[opt]
|
||||
///
|
||||
bool Parser::isCXXFunctionDeclarator(bool *IsAmbiguous) {
|
||||
bool Parser::isCXXFunctionDeclarator(
|
||||
bool *IsAmbiguous, ImplicitTypenameContext AllowImplicitTypename) {
|
||||
|
||||
// C++ 8.2p1:
|
||||
// The ambiguity arising from the similarity between a function-style cast and
|
||||
|
@ -1843,7 +1852,9 @@ bool Parser::isCXXFunctionDeclarator(bool *IsAmbiguous) {
|
|||
|
||||
ConsumeParen();
|
||||
bool InvalidAsDeclaration = false;
|
||||
TPResult TPR = TryParseParameterDeclarationClause(&InvalidAsDeclaration);
|
||||
TPResult TPR = TryParseParameterDeclarationClause(
|
||||
&InvalidAsDeclaration, /*VersusTemplateArgument=*/false,
|
||||
AllowImplicitTypename);
|
||||
if (TPR == TPResult::Ambiguous) {
|
||||
if (Tok.isNot(tok::r_paren))
|
||||
TPR = TPResult::False;
|
||||
|
@ -1887,9 +1898,9 @@ bool Parser::isCXXFunctionDeclarator(bool *IsAmbiguous) {
|
|||
/// attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt]
|
||||
/// attributes[opt] '=' assignment-expression
|
||||
///
|
||||
Parser::TPResult
|
||||
Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
|
||||
bool VersusTemplateArgument) {
|
||||
Parser::TPResult Parser::TryParseParameterDeclarationClause(
|
||||
bool *InvalidAsDeclaration, bool VersusTemplateArgument,
|
||||
ImplicitTypenameContext AllowImplicitTypename) {
|
||||
|
||||
if (Tok.is(tok::r_paren))
|
||||
return TPResult::Ambiguous;
|
||||
|
@ -1922,8 +1933,8 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
|
|||
// decl-specifier-seq
|
||||
// A parameter-declaration's initializer must be preceded by an '=', so
|
||||
// decl-specifier-seq '{' is not a parameter in C++11.
|
||||
TPResult TPR = isCXXDeclarationSpecifier(TPResult::False,
|
||||
InvalidAsDeclaration);
|
||||
TPResult TPR = isCXXDeclarationSpecifier(
|
||||
AllowImplicitTypename, TPResult::False, InvalidAsDeclaration);
|
||||
// A declaration-specifier (not followed by '(' or '{') means this can't be
|
||||
// an expression, but it could still be a template argument.
|
||||
if (TPR != TPResult::Ambiguous &&
|
||||
|
@ -1940,7 +1951,7 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
|
|||
if (SeenType && Tok.is(tok::identifier))
|
||||
return TPResult::True;
|
||||
|
||||
TPR = isCXXDeclarationSpecifier(TPResult::False,
|
||||
TPR = isCXXDeclarationSpecifier(AllowImplicitTypename, TPResult::False,
|
||||
InvalidAsDeclaration);
|
||||
if (TPR == TPResult::Error)
|
||||
return TPR;
|
||||
|
@ -2102,9 +2113,9 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) {
|
|||
// but one good distinguishing factor is that a "decl-specifier" not
|
||||
// followed by '(' or '{' can't appear in an expression.
|
||||
bool InvalidAsTemplateArgumentList = false;
|
||||
if (isCXXDeclarationSpecifier(TPResult::False,
|
||||
&InvalidAsTemplateArgumentList) ==
|
||||
TPResult::True)
|
||||
if (isCXXDeclarationSpecifier(ImplicitTypenameContext::No, TPResult::False,
|
||||
&InvalidAsTemplateArgumentList) ==
|
||||
TPResult::True)
|
||||
return TPResult::True;
|
||||
if (InvalidAsTemplateArgumentList)
|
||||
return TPResult::False;
|
||||
|
|
|
@ -1063,7 +1063,7 @@ bool Parser::isStartOfFunctionDefinition(const ParsingDeclarator &Declarator) {
|
|||
// Handle K&R C argument lists: int X(f) int f; {}
|
||||
if (!getLangOpts().CPlusPlus &&
|
||||
Declarator.getFunctionTypeInfo().isKNRPrototype())
|
||||
return isDeclarationSpecifier();
|
||||
return isDeclarationSpecifier(ImplicitTypenameContext::No);
|
||||
|
||||
if (getLangOpts().CPlusPlus && Tok.is(tok::equal)) {
|
||||
const Token &KW = NextToken();
|
||||
|
@ -1472,7 +1472,7 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) {
|
|||
Scope::FunctionDeclarationScope | Scope::DeclScope);
|
||||
|
||||
// Read all the argument declarations.
|
||||
while (isDeclarationSpecifier()) {
|
||||
while (isDeclarationSpecifier(ImplicitTypenameContext::No)) {
|
||||
SourceLocation DSStart = Tok.getLocation();
|
||||
|
||||
// Parse the common declaration-specifiers piece.
|
||||
|
@ -1685,8 +1685,12 @@ void Parser::AnnotateScopeToken(CXXScopeSpec &SS, bool IsNewAnnotation) {
|
|||
///
|
||||
/// \param CCC Indicates how to perform typo-correction for this name. If NULL,
|
||||
/// no typo correction will be performed.
|
||||
/// \param AllowImplicitTypename Whether we are in a context where a dependent
|
||||
/// nested-name-specifier without typename is treated as a type (e.g.
|
||||
/// T::type).
|
||||
Parser::AnnotatedNameKind
|
||||
Parser::TryAnnotateName(CorrectionCandidateCallback *CCC) {
|
||||
Parser::TryAnnotateName(CorrectionCandidateCallback *CCC,
|
||||
ImplicitTypenameContext AllowImplicitTypename) {
|
||||
assert(Tok.is(tok::identifier) || Tok.is(tok::annot_cxxscope));
|
||||
|
||||
const bool EnteringContext = false;
|
||||
|
@ -1700,7 +1704,8 @@ Parser::TryAnnotateName(CorrectionCandidateCallback *CCC) {
|
|||
return ANK_Error;
|
||||
|
||||
if (Tok.isNot(tok::identifier) || SS.isInvalid()) {
|
||||
if (TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation))
|
||||
if (TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation,
|
||||
AllowImplicitTypename))
|
||||
return ANK_Error;
|
||||
return ANK_Unresolved;
|
||||
}
|
||||
|
@ -1710,10 +1715,11 @@ Parser::TryAnnotateName(CorrectionCandidateCallback *CCC) {
|
|||
|
||||
// FIXME: Move the tentative declaration logic into ClassifyName so we can
|
||||
// typo-correct to tentatively-declared identifiers.
|
||||
if (isTentativelyDeclared(Name)) {
|
||||
if (isTentativelyDeclared(Name) && SS.isEmpty()) {
|
||||
// Identifier has been tentatively declared, and thus cannot be resolved as
|
||||
// an expression. Fall back to annotating it as a type.
|
||||
if (TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation))
|
||||
if (TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation,
|
||||
AllowImplicitTypename))
|
||||
return ANK_Error;
|
||||
return Tok.is(tok::annot_typename) ? ANK_Success : ANK_TentativeDecl;
|
||||
}
|
||||
|
@ -1909,7 +1915,8 @@ bool Parser::TryKeywordIdentFallback(bool DisableKeyword) {
|
|||
///
|
||||
/// Note that this routine emits an error if you call it with ::new or ::delete
|
||||
/// as the current tokens, so only call it in contexts where these are invalid.
|
||||
bool Parser::TryAnnotateTypeOrScopeToken() {
|
||||
bool Parser::TryAnnotateTypeOrScopeToken(
|
||||
ImplicitTypenameContext AllowImplicitTypename) {
|
||||
assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) ||
|
||||
Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope) ||
|
||||
Tok.is(tok::kw_decltype) || Tok.is(tok::annot_template_id) ||
|
||||
|
@ -1926,7 +1933,7 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
|
|||
if (getLangOpts().MSVCCompat && NextToken().is(tok::kw_typedef)) {
|
||||
Token TypedefToken;
|
||||
PP.Lex(TypedefToken);
|
||||
bool Result = TryAnnotateTypeOrScopeToken();
|
||||
bool Result = TryAnnotateTypeOrScopeToken(AllowImplicitTypename);
|
||||
PP.EnterToken(Tok, /*IsReinject=*/true);
|
||||
Tok = TypedefToken;
|
||||
if (!Result)
|
||||
|
@ -1952,7 +1959,8 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
|
|||
Tok.is(tok::annot_decltype)) {
|
||||
// Attempt to recover by skipping the invalid 'typename'
|
||||
if (Tok.is(tok::annot_decltype) ||
|
||||
(!TryAnnotateTypeOrScopeToken() && Tok.isAnnotation())) {
|
||||
(!TryAnnotateTypeOrScopeToken(AllowImplicitTypename) &&
|
||||
Tok.isAnnotation())) {
|
||||
unsigned DiagID = diag::err_expected_qualified_after_typename;
|
||||
// MS compatibility: MSVC permits using known types with typename.
|
||||
// e.g. "typedef typename T* pointer_type"
|
||||
|
@ -2018,22 +2026,24 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
|
|||
/*EnteringContext*/ false))
|
||||
return true;
|
||||
|
||||
return TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation);
|
||||
return TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation,
|
||||
AllowImplicitTypename);
|
||||
}
|
||||
|
||||
/// Try to annotate a type or scope token, having already parsed an
|
||||
/// optional scope specifier. \p IsNewScope should be \c true unless the scope
|
||||
/// specifier was extracted from an existing tok::annot_cxxscope annotation.
|
||||
bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS,
|
||||
bool IsNewScope) {
|
||||
bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(
|
||||
CXXScopeSpec &SS, bool IsNewScope,
|
||||
ImplicitTypenameContext AllowImplicitTypename) {
|
||||
if (Tok.is(tok::identifier)) {
|
||||
// Determine whether the identifier is a type name.
|
||||
if (ParsedType Ty = Actions.getTypeName(
|
||||
*Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope(), &SS,
|
||||
false, NextToken().is(tok::period), nullptr,
|
||||
/*IsCtorOrDtorName=*/false,
|
||||
/*NonTrivialTypeSourceInfo*/true,
|
||||
/*IsClassTemplateDeductionContext*/true)) {
|
||||
/*NonTrivialTypeSourceInfo=*/true,
|
||||
/*IsClassTemplateDeductionContext=*/true, AllowImplicitTypename)) {
|
||||
SourceLocation BeginLoc = Tok.getLocation();
|
||||
if (SS.isNotEmpty()) // it was a C++ qualified type name.
|
||||
BeginLoc = SS.getBeginLoc();
|
||||
|
@ -2119,7 +2129,7 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS,
|
|||
// template-id annotation in a context where we weren't allowed
|
||||
// to produce a type annotation token. Update the template-id
|
||||
// annotation token to a type annotation token now.
|
||||
AnnotateTemplateIdTokenAsType(SS);
|
||||
AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "clang/Sema/TemplateInstCallback.h"
|
||||
#include "clang/Sema/TypoCorrection.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/Support/TimeProfiler.h"
|
||||
|
||||
|
@ -2679,3 +2680,26 @@ Sema::FPFeaturesStateRAII::~FPFeaturesStateRAII() {
|
|||
S.FpPragmaStack.CurrentValue = OldOverrides;
|
||||
S.PP.setCurrentFPEvalMethod(OldFPPragmaLocation, OldEvalMethod);
|
||||
}
|
||||
|
||||
bool Sema::isDeclaratorFunctionLike(Declarator &D) {
|
||||
assert(D.getCXXScopeSpec().isSet() &&
|
||||
"can only be called for qualified names");
|
||||
|
||||
auto LR = LookupResult(*this, D.getIdentifier(), D.getBeginLoc(),
|
||||
LookupOrdinaryName, forRedeclarationInCurContext());
|
||||
DeclContext *DC = computeDeclContext(D.getCXXScopeSpec(),
|
||||
!D.getDeclSpec().isFriendSpecified());
|
||||
if (!DC)
|
||||
return false;
|
||||
|
||||
LookupQualifiedName(LR, DC);
|
||||
bool Result = std::all_of(LR.begin(), LR.end(), [](Decl *Dcl) {
|
||||
if (NamedDecl *ND = dyn_cast<NamedDecl>(Dcl)) {
|
||||
ND = ND->getUnderlyingDecl();
|
||||
return isa<FunctionDecl>(ND) || isa<FunctionTemplateDecl>(ND) ||
|
||||
isa<UsingDecl>(ND);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return Result;
|
||||
}
|
||||
|
|
|
@ -324,12 +324,12 @@ static ParsedType buildNamedType(Sema &S, const CXXScopeSpec *SS, QualType T,
|
|||
/// opaque pointer (actually a QualType) corresponding to that
|
||||
/// type. Otherwise, returns NULL.
|
||||
ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
|
||||
Scope *S, CXXScopeSpec *SS,
|
||||
bool isClassName, bool HasTrailingDot,
|
||||
ParsedType ObjectTypePtr,
|
||||
Scope *S, CXXScopeSpec *SS, bool isClassName,
|
||||
bool HasTrailingDot, ParsedType ObjectTypePtr,
|
||||
bool IsCtorOrDtorName,
|
||||
bool WantNontrivialTypeSourceInfo,
|
||||
bool IsClassTemplateDeductionContext,
|
||||
ImplicitTypenameContext AllowImplicitTypename,
|
||||
IdentifierInfo **CorrectedII) {
|
||||
// FIXME: Consider allowing this outside C++1z mode as an extension.
|
||||
bool AllowDeducedTemplate = IsClassTemplateDeductionContext &&
|
||||
|
@ -356,17 +356,33 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
|
|||
//
|
||||
// We therefore do not perform any name lookup if the result would
|
||||
// refer to a member of an unknown specialization.
|
||||
if (!isClassName && !IsCtorOrDtorName)
|
||||
// In C++2a, in several contexts a 'typename' is not required. Also
|
||||
// allow this as an extension.
|
||||
if (AllowImplicitTypename == ImplicitTypenameContext::No &&
|
||||
!isClassName && !IsCtorOrDtorName)
|
||||
return nullptr;
|
||||
bool IsImplicitTypename = !isClassName && !IsCtorOrDtorName;
|
||||
if (IsImplicitTypename) {
|
||||
SourceLocation QualifiedLoc = SS->getRange().getBegin();
|
||||
if (getLangOpts().CPlusPlus20)
|
||||
Diag(QualifiedLoc, diag::warn_cxx17_compat_implicit_typename);
|
||||
else
|
||||
Diag(QualifiedLoc, diag::ext_implicit_typename)
|
||||
<< SS->getScopeRep() << II.getName()
|
||||
<< FixItHint::CreateInsertion(QualifiedLoc, "typename ");
|
||||
}
|
||||
|
||||
// We know from the grammar that this name refers to a type,
|
||||
// so build a dependent node to describe the type.
|
||||
if (WantNontrivialTypeSourceInfo)
|
||||
return ActOnTypenameType(S, SourceLocation(), *SS, II, NameLoc).get();
|
||||
return ActOnTypenameType(S, SourceLocation(), *SS, II, NameLoc,
|
||||
(ImplicitTypenameContext)IsImplicitTypename)
|
||||
.get();
|
||||
|
||||
NestedNameSpecifierLoc QualifierLoc = SS->getWithLocInContext(Context);
|
||||
QualType T = CheckTypenameType(ETK_None, SourceLocation(), QualifierLoc,
|
||||
II, NameLoc);
|
||||
QualType T =
|
||||
CheckTypenameType(IsImplicitTypename ? ETK_Typename : ETK_None,
|
||||
SourceLocation(), QualifierLoc, II, NameLoc);
|
||||
return ParsedType::make(T);
|
||||
}
|
||||
|
||||
|
|
|
@ -4059,7 +4059,8 @@ TypeResult Sema::ActOnTemplateIdType(
|
|||
TemplateTy TemplateD, IdentifierInfo *TemplateII,
|
||||
SourceLocation TemplateIILoc, SourceLocation LAngleLoc,
|
||||
ASTTemplateArgsPtr TemplateArgsIn, SourceLocation RAngleLoc,
|
||||
bool IsCtorOrDtorName, bool IsClassName) {
|
||||
bool IsCtorOrDtorName, bool IsClassName,
|
||||
ImplicitTypenameContext AllowImplicitTypename) {
|
||||
if (SS.isInvalid())
|
||||
return true;
|
||||
|
||||
|
@ -4073,9 +4074,18 @@ TypeResult Sema::ActOnTemplateIdType(
|
|||
// qualified-id denotes a type, forming an
|
||||
// elaborated-type-specifier (7.1.5.3).
|
||||
if (!LookupCtx && isDependentScopeSpecifier(SS)) {
|
||||
Diag(SS.getBeginLoc(), diag::err_typename_missing_template)
|
||||
<< SS.getScopeRep() << TemplateII->getName();
|
||||
// Recover as if 'typename' were specified.
|
||||
// C++2a relaxes some of those restrictions in [temp.res]p5.
|
||||
if (AllowImplicitTypename == ImplicitTypenameContext::Yes) {
|
||||
if (getLangOpts().CPlusPlus20)
|
||||
Diag(SS.getBeginLoc(), diag::warn_cxx17_compat_implicit_typename);
|
||||
else
|
||||
Diag(SS.getBeginLoc(), diag::ext_implicit_typename)
|
||||
<< SS.getScopeRep() << TemplateII->getName()
|
||||
<< FixItHint::CreateInsertion(SS.getBeginLoc(), "typename ");
|
||||
} else
|
||||
Diag(SS.getBeginLoc(), diag::err_typename_missing_template)
|
||||
<< SS.getScopeRep() << TemplateII->getName();
|
||||
|
||||
// FIXME: This is not quite correct recovery as we don't transform SS
|
||||
// into the corresponding dependent form (and we don't diagnose missing
|
||||
// 'template' keywords within SS as a result).
|
||||
|
@ -10601,10 +10611,11 @@ Sema::ActOnDependentTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
|
|||
return CreateParsedType(Result, TLB.getTypeSourceInfo(Context, Result));
|
||||
}
|
||||
|
||||
TypeResult
|
||||
Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc,
|
||||
const CXXScopeSpec &SS, const IdentifierInfo &II,
|
||||
SourceLocation IdLoc) {
|
||||
TypeResult Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc,
|
||||
const CXXScopeSpec &SS,
|
||||
const IdentifierInfo &II,
|
||||
SourceLocation IdLoc,
|
||||
ImplicitTypenameContext IsImplicitTypename) {
|
||||
if (SS.isInvalid())
|
||||
return true;
|
||||
|
||||
|
@ -10617,9 +10628,13 @@ Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc,
|
|||
|
||||
NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
|
||||
TypeSourceInfo *TSI = nullptr;
|
||||
QualType T = CheckTypenameType(TypenameLoc.isValid()? ETK_Typename : ETK_None,
|
||||
TypenameLoc, QualifierLoc, II, IdLoc, &TSI,
|
||||
/*DeducedTSTContext=*/true);
|
||||
QualType T =
|
||||
CheckTypenameType((TypenameLoc.isValid() ||
|
||||
IsImplicitTypename == ImplicitTypenameContext::Yes)
|
||||
? ETK_Typename
|
||||
: ETK_None,
|
||||
TypenameLoc, QualifierLoc, II, IdLoc, &TSI,
|
||||
/*DeducedTSTContext=*/true);
|
||||
if (T.isNull())
|
||||
return true;
|
||||
return CreateParsedType(T, TSI);
|
||||
|
|
|
@ -31,8 +31,8 @@ X0::X0 X0::f1() { return X0(); } // expected-error{{qualified reference to 'X0'
|
|||
|
||||
struct X0::X0 X0::f2() { return X0(); }
|
||||
|
||||
template<typename T> X1<T>::X1<T> X1<T>::f2() { } // expected-error{{missing 'typename'}}
|
||||
template<typename T> X1<T>::X1<T> (X1<T>::f2)(int) { } // expected-error{{missing 'typename'}}
|
||||
template<typename T> X1<T>::X1<T> X1<T>::f2() { } // expected-warning{{missing 'typename'}}
|
||||
template<typename T> X1<T>::X1<T> (X1<T>::f2)(int) { } // expected-warning{{missing 'typename'}}
|
||||
template<typename T> struct X1<T>::X1<T> (X1<T>::f2)(float) { }
|
||||
template<typename T> struct X1<T>::X1 (X1<T>::f2)(double) { }
|
||||
template<typename T> typename X1<T>::template X1<T> X1<T>::f2(short) { } // expected-warning {{qualified reference to 'X1' is a constructor name rather than a template name in this context}}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
|
||||
namespace dr100 { // dr100: yes
|
||||
template<const char (*)[4]> struct A {}; // expected-note 0-1{{declared here}}
|
||||
|
@ -73,7 +74,10 @@ namespace dr107 { // dr107: yes
|
|||
namespace dr108 { // dr108: yes
|
||||
template<typename T> struct A {
|
||||
struct B { typedef int X; };
|
||||
B::X x; // expected-error {{missing 'typename'}}
|
||||
B::X x;
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-error@-2 {{implicit 'typename' is a C++20 extension}}
|
||||
#endif
|
||||
struct C : B { X x; }; // expected-error {{unknown type name}}
|
||||
};
|
||||
template<> struct A<int>::B { int X; };
|
||||
|
|
|
@ -243,13 +243,16 @@ namespace dr222 { // dr222: dup 637
|
|||
|
||||
// dr223: na
|
||||
|
||||
namespace dr224 { // dr224: no
|
||||
namespace dr224 { // dr224: yes
|
||||
namespace example1 {
|
||||
template <class T> class A {
|
||||
typedef int type;
|
||||
A::type a;
|
||||
A<T>::type b;
|
||||
A<T*>::type c; // expected-error {{missing 'typename'}}
|
||||
A<T*>::type c;
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-error@-2 {{implicit 'typename' is a C++20 extension}}
|
||||
#endif
|
||||
::dr224::example1::A<T>::type d;
|
||||
|
||||
class B {
|
||||
|
@ -257,12 +260,18 @@ namespace dr224 { // dr224: no
|
|||
|
||||
A::type a;
|
||||
A<T>::type b;
|
||||
A<T*>::type c; // expected-error {{missing 'typename'}}
|
||||
A<T*>::type c;
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-error@-2 {{implicit 'typename' is a C++20 extension}}
|
||||
#endif
|
||||
::dr224::example1::A<T>::type d;
|
||||
|
||||
B::type e;
|
||||
A<T>::B::type f;
|
||||
A<T*>::B::type g; // expected-error {{missing 'typename'}}
|
||||
A<T*>::B::type g;
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-error@-2 {{implicit 'typename' is a C++20 extension}}
|
||||
#endif
|
||||
typename A<T*>::B::type h;
|
||||
};
|
||||
};
|
||||
|
@ -270,21 +279,32 @@ namespace dr224 { // dr224: no
|
|||
template <class T> class A<T*> {
|
||||
typedef int type;
|
||||
A<T*>::type a;
|
||||
A<T>::type b; // expected-error {{missing 'typename'}}
|
||||
A<T>::type b;
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-error@-2 {{implicit 'typename' is a C++20 extension}}
|
||||
#endif
|
||||
};
|
||||
|
||||
template <class T1, class T2, int I> struct B {
|
||||
typedef int type;
|
||||
B<T1, T2, I>::type b1;
|
||||
B<T2, T1, I>::type b2; // expected-error {{missing 'typename'}}
|
||||
B<T2, T1, I>::type b2;
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-error@-2 {{implicit 'typename' is a C++20 extension}}
|
||||
#endif
|
||||
|
||||
typedef T1 my_T1;
|
||||
static const int my_I = I;
|
||||
static const int my_I2 = I+0;
|
||||
static const int my_I3 = my_I;
|
||||
B<my_T1, T2, my_I>::type b3; // FIXME: expected-error {{missing 'typename'}}
|
||||
B<my_T1, T2, my_I2>::type b4; // expected-error {{missing 'typename'}}
|
||||
B<my_T1, T2, my_I3>::type b5; // FIXME: expected-error {{missing 'typename'}}
|
||||
B<my_T1, T2, my_I>::type b3;
|
||||
B<my_T1, T2, my_I2>::type b4;
|
||||
B<my_T1, T2, my_I3>::type b5;
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-error@-4 {{implicit 'typename' is a C++20 extension}}
|
||||
// expected-error@-4 {{implicit 'typename' is a C++20 extension}}
|
||||
// expected-error@-4 {{implicit 'typename' is a C++20 extension}}
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -295,7 +315,10 @@ namespace dr224 { // dr224: no
|
|||
X<i, int>::type w;
|
||||
X<A::i, char>::type x;
|
||||
X<A<T>::i, double>::type y;
|
||||
X<A<T*>::i, long>::type z; // expected-error {{missing 'typename'}}
|
||||
X<A<T*>::i, long>::type z;
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-error@-2 {{implicit 'typename' is a C++20 extension}}
|
||||
#endif
|
||||
int f();
|
||||
};
|
||||
template <class T> int A<T>::f() {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %clang_cc1 -std=c++20 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
|
||||
// FIXME: __SIZE_TYPE__ expands to 'long long' on some targets.
|
||||
__extension__ typedef __SIZE_TYPE__ size_t;
|
||||
|
@ -176,7 +177,10 @@ namespace dr409 { // dr409: yes
|
|||
B b1;
|
||||
A::B b2;
|
||||
A<T>::B b3;
|
||||
A<T*>::B b4; // expected-error {{missing 'typename'}}
|
||||
A<T*>::B b4;
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-error@-2 {{implicit 'typename' is a C++20 extension}}
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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++20 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
|
||||
// FIXME: This is included to avoid a diagnostic with no source location
|
||||
// pointing at the implicit operator new. We can't match such a diagnostic
|
||||
|
@ -228,8 +229,12 @@ namespace dr526 { // dr526: yes
|
|||
template<int N> struct X {
|
||||
typedef int type;
|
||||
X<N>::type v1;
|
||||
X<(N)>::type v2; // expected-error {{missing 'typename'}}
|
||||
X<+N>::type v3; // expected-error {{missing 'typename'}}
|
||||
X<(N)>::type v2;
|
||||
X<+N>::type v3;
|
||||
#if __cplusplus <= 201703L
|
||||
// expected-error@-3 {{implicit 'typename' is a C++20 extension}}
|
||||
// expected-error@-3 {{implicit 'typename' is a C++20 extension}}
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -345,12 +350,15 @@ namespace dr531 { // dr531: partial
|
|||
template<> template<typename U> template<typename V> void A<int>::B<U>::h() {}
|
||||
template<typename U> template<typename V> void A<int>::B<U>::i() {} // expected-error {{should be empty}}
|
||||
|
||||
#if __cplusplus <= 201703L
|
||||
// FIXME: All of those declarations shouldn't crash in C++20 mode.
|
||||
template<> template<> void A<int>::B<int>::f() {}
|
||||
template<> template<> template<typename V> void A<int>::B<int>::h() {}
|
||||
template<> template<> template<> void A<int>::B<int>::h<int>() {}
|
||||
|
||||
template<> void A<int>::B<char>::f() {} // expected-error {{requires 'template<>'}}
|
||||
template<> template<typename V> void A<int>::B<char>::h() {} // expected-error {{should be empty}}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -478,8 +486,15 @@ namespace dr541 { // dr541: yes
|
|||
|
||||
namespace dr542 { // dr542: yes
|
||||
#if __cplusplus >= 201103L
|
||||
// In C++20 A and B are no longer aggregates and thus the constructor is
|
||||
// called, which fails.
|
||||
struct A { A() = delete; int n; };
|
||||
A a[32] = {}; // ok, constructor not called
|
||||
#if __cplusplus > 201703L
|
||||
// expected-error@-2 {{call to deleted constructor}}
|
||||
// expected-note@-3 {{in implicit initialization}}
|
||||
// expected-note@-5 {{marked deleted here}}
|
||||
#endif
|
||||
|
||||
struct B {
|
||||
int n;
|
||||
|
@ -487,6 +502,10 @@ namespace dr542 { // dr542: yes
|
|||
B() = default;
|
||||
};
|
||||
B b[32] = {}; // ok, constructor not called
|
||||
#if __cplusplus > 201703L
|
||||
// expected-error@-2 {{calling a private constructor}}
|
||||
// expected-note@-5 {{declared private here}}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -45,3 +45,9 @@ namespace std_example {
|
|||
};
|
||||
static_assert(C2<int>); // expected-note{{because 'int' does not satisfy 'C2'}} expected-error{{static assertion failed}}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
concept K = requires (T::Type X) {
|
||||
X.next();
|
||||
};
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@ struct X {
|
|||
};
|
||||
};
|
||||
|
||||
template<typename T> A // expected-error {{missing 'typename' prior to dependent type template name 'A<T>::B'}}
|
||||
template<typename T> A // expected-warning {{missing 'typename'}}
|
||||
<T>::B<T> f1();
|
||||
template<typename T> A<T>::C<T> f2(); // expected-error {{missing 'typename' prior to dependent type template name 'A<T>::C'}}
|
||||
template<typename T> A<T>::C<T> f2(); // expected-warning {{missing 'typename'}}
|
||||
|
||||
// FIXME: Should these cases really be valid? There doesn't appear to be a rule prohibiting them...
|
||||
template<typename T> A<T>::C<X>::X(T) {}
|
||||
|
@ -30,9 +30,6 @@ template<typename T> int A<T>::template C<int>::*f5() {} // expected-error {{has
|
|||
template<typename T> template<typename U> struct A<T>::B {
|
||||
friend A<T>::C<T> f6(); // ok, same as 'friend T f6();'
|
||||
|
||||
// FIXME: Error recovery here is awful; we decide that the template-id names
|
||||
// a type, and then complain about the rest of the tokens, and then complain
|
||||
// that we didn't get a function declaration.
|
||||
friend A<U>::C<T> f7(); // expected-error {{use 'template' keyword to treat 'C' as a dependent template name}} expected-error 3{{}}
|
||||
friend A<U>::template C<T> f8(); // expected-error 4{{}}
|
||||
friend A<U>::C<T> f7(); // expected-error {{use 'template' keyword to treat 'C' as a dependent template name}} expected-warning {{missing 'typename'}}
|
||||
friend A<U>::template C<T> f8(); // expected-warning {{missing 'typename'}}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
// RUN: %clang_cc1 -std=c++20 -pedantic -verify %s
|
||||
|
||||
struct X {
|
||||
using type = int;
|
||||
static constexpr int value = 1;
|
||||
class tclass {};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void f() {
|
||||
// it is a qualified name in a type-id-only context (see below), or
|
||||
// [its smallest enclosing [/new/defining/]-type-id is]:
|
||||
// - a new-type-id
|
||||
auto *Ptr = new T::type();
|
||||
// - a defining-type-id
|
||||
class T::tclass Empty1;
|
||||
T::tclass Empty2; // expected-error{{missing 'typename'}}
|
||||
// - a trailing-return-type
|
||||
auto f()->T::type;
|
||||
// - default argument of a type-parameter of a template [see below]
|
||||
|
||||
// - type-id of a
|
||||
// static_cast,
|
||||
auto StaticCast = static_cast<T::type>(1.2);
|
||||
// const_cast,
|
||||
const auto *ConstCast = const_cast<const T::type *>(Ptr);
|
||||
// reinterpret_cast,
|
||||
int ReinterpretCast = reinterpret_cast<T::type>(4);
|
||||
// dynamic_cast
|
||||
struct B {
|
||||
virtual ~B() = default;
|
||||
};
|
||||
struct D : T::tclass {};
|
||||
auto *Base = dynamic_cast<T::tclass *>(new B);
|
||||
|
||||
T::type Invalid; // expected-error{{missing 'typename'}}
|
||||
}
|
||||
|
||||
template void f<X>();
|
||||
|
||||
// As default argument.
|
||||
template <typename T, typename = T::type>
|
||||
struct DefaultArg {};
|
||||
|
||||
template struct DefaultArg<X>;
|
||||
|
||||
// it is a decl-specifier of the decl-specifier-seq of a
|
||||
// - simple-declaration or a function-definition in namespace scope
|
||||
template <typename T>
|
||||
T::type VarTemp = 1;
|
||||
|
||||
template int VarTemp<X>;
|
||||
|
||||
template <typename T>
|
||||
T::type FuncDef() { return 1; }
|
||||
|
||||
template int FuncDef<X>();
|
||||
|
||||
template <typename T>
|
||||
T::type funcDecl();
|
||||
|
||||
template <typename T>
|
||||
void FuncParam(T::type); // ok, but variable template
|
||||
// expected-error@-1{{variable has incomplete type 'void'}}
|
||||
|
||||
template <typename T>
|
||||
void FuncParam2(const T::type, int); // expected-error{{missing 'typename'}}
|
||||
|
||||
template <typename T>
|
||||
struct MemberDecl {
|
||||
// member-declaration,
|
||||
T::type Member;
|
||||
|
||||
// parameter-declaration in a member-declaration, unless that
|
||||
// parameter-declaration appears in a default argument
|
||||
void NoDefault(T::type);
|
||||
void Default(int A = T::value);
|
||||
};
|
||||
|
||||
template struct MemberDecl<X>;
|
||||
|
||||
// parameter-declaration in a declarator of a function or function template
|
||||
// declaration where the declarator-id is qualified, unless that
|
||||
// parameter-declaration appears in a default argument,
|
||||
struct QualifiedFunc {
|
||||
template <typename T>
|
||||
void foo(typename T::type);
|
||||
template <typename T>
|
||||
void bar(T::type);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void QualifiedFunc::foo(T::type) {}
|
||||
template <typename T>
|
||||
void QualifiedFunc::bar(typename T::type) {}
|
||||
|
||||
template <typename T>
|
||||
void g() {
|
||||
// parameter-declaration in a lambda-declarator, unless that
|
||||
// parameter-declaration appears in a default argument, or
|
||||
auto Lambda1 = [](T::type) {};
|
||||
auto Lambda2 = [](int A = T::value) {};
|
||||
}
|
||||
|
||||
template void g<X>();
|
||||
|
||||
// parameter-declaration of a (non-type) template-parameter.
|
||||
template <typename T, T::type>
|
||||
void NonTypeArg() {}
|
||||
|
||||
template void NonTypeArg<X, 0>();
|
||||
|
||||
template <typename T>
|
||||
void f(T::type) {} // expected-error {{missing 'typename'}}
|
||||
|
||||
namespace N {
|
||||
template <typename T>
|
||||
int f(typename T::type);
|
||||
template <typename T>
|
||||
extern int Var;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int N::f(T::type); // ok, function
|
||||
template <typename T>
|
||||
int N::Var(T::value); // ok, variable
|
||||
|
||||
int h() {
|
||||
return N::f<X>(10) + N::Var<X>;
|
||||
}
|
||||
|
||||
namespace NN {
|
||||
inline namespace A { template <typename T> int f(typename T::type); } // expected-note{{previous definition is here}}
|
||||
inline namespace B { template <typename T> int f(T::type); }
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int NN::f(T::type); // expected-error{{redefinition of 'f' as different kind of symbol}}
|
||||
|
||||
template <auto V>
|
||||
struct videntity {
|
||||
static constexpr auto value = V;
|
||||
};
|
||||
|
||||
template <typename T,
|
||||
bool = T::value,
|
||||
bool = bool(T::value),
|
||||
bool = videntity<bool(T::value)>::value>
|
||||
void f(int = T::value) {}
|
||||
|
||||
template <typename> int test() = delete;
|
||||
template <auto> int test();
|
||||
|
||||
template <typename T>
|
||||
int Test = test<int(T::value)>();
|
||||
template int Test<X>;
|
||||
|
||||
template<typename T> struct A {
|
||||
enum E : T::type {}; // expected-error{{missing 'typename'}}
|
||||
operator T::type() {} // expected-error{{missing 'typename'}}
|
||||
void f() { this->operator T::type(); } // expected-error{{missing 'typename'}}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct C {
|
||||
C(T::type); // implicit typename context
|
||||
friend C (T::fn)(); // not implicit typename context, declarator-id of friend declaration
|
||||
C(T::type::*x)[3]; // not implicit typename context, pointer-to-member type
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
C<T>::C(T::type) {}
|
|
@ -17,7 +17,7 @@ namespace Example1 {
|
|||
|
||||
template<class T> struct A<A<A<T>>> {
|
||||
struct C {};
|
||||
B<B<T>>::C bc; // expected-error {{missing 'typename'}}
|
||||
B<B<T>>::C bc; // expected-warning {{implicit 'typename' is a C++20 extension}}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -211,7 +211,7 @@ public:
|
|||
template<class T> struct Mystery;
|
||||
template<class T> typedef Mystery<T>::type getMysteriousThing() { // \
|
||||
expected-error {{function definition declared 'typedef'}} \
|
||||
expected-error {{missing 'typename' prior to dependent}}
|
||||
expected-warning {{implicit 'typename' is a C++20 extension}}
|
||||
return Mystery<T>::get();
|
||||
}
|
||||
|
||||
|
|
|
@ -103,5 +103,9 @@ class G {
|
|||
void l(int x = C<int, C<int, int>::C1>().f()) {}
|
||||
|
||||
// This isn't, but it shouldn't crash. The diagnostics don't matter much.
|
||||
void m(int x = C<int, union int>().f()) {} // expected-error {{declaration of anonymous union must be a definition}} expected-error {{expected a type}} expected-error {{expected '>'}} expected-note {{to match}}
|
||||
void m(int x = C<int, union int>().f()) {}
|
||||
// expected-error@-1 {{declaration of anonymous union must be a definition}}
|
||||
// expected-error@-2 {{type name requires a specifier or qualifier}}
|
||||
// expected-error@-3 {{expected '>'}}
|
||||
// expected-note@-4 {{to match this '<'}}
|
||||
};
|
||||
|
|
|
@ -211,14 +211,14 @@ public:
|
|||
typedef B<U> Base2;
|
||||
typedef A<U> Base3;
|
||||
|
||||
A<T>::TYPE a1; // expected-warning {{missing 'typename' prior to dependent type name}}
|
||||
Base1::TYPE a2; // expected-warning {{missing 'typename' prior to dependent type name}}
|
||||
A<T>::TYPE a1; // expected-warning {{implicit 'typename' is a C++20 extension}}
|
||||
Base1::TYPE a2; // expected-warning {{implicit 'typename' is a C++20 extension}}
|
||||
|
||||
B<U>::TYPE a3; // expected-warning {{missing 'typename' prior to dependent type name}}
|
||||
Base2::TYPE a4; // expected-warning {{missing 'typename' prior to dependent type name}}
|
||||
B<U>::TYPE a3; // expected-warning {{implicit 'typename' is a C++20 extension}}
|
||||
Base2::TYPE a4; // expected-warning {{implicit 'typename' is a C++20 extension}}
|
||||
|
||||
A<U>::TYPE a5; // expected-error {{missing 'typename' prior to dependent type name}}
|
||||
Base3::TYPE a6; // expected-error {{missing 'typename' prior to dependent type name}}
|
||||
A<U>::TYPE a5; // expected-warning {{implicit 'typename' is a C++20 extension}}
|
||||
Base3::TYPE a6; // expected-warning {{implicit 'typename' is a C++20 extension}}
|
||||
};
|
||||
|
||||
class D {
|
||||
|
@ -227,9 +227,9 @@ public:
|
|||
};
|
||||
|
||||
template <class T>
|
||||
void function_missing_typename(const T::Type param)// expected-warning {{missing 'typename' prior to dependent type name}}
|
||||
void function_missing_typename(const T::Type param)// expected-warning {{missing 'typename'}}
|
||||
{
|
||||
const T::Type var = 2; // expected-warning {{missing 'typename' prior to dependent type name}}
|
||||
const T::Type var = 2; // expected-warning {{missing 'typename'}}
|
||||
}
|
||||
|
||||
template void function_missing_typename<D>(const D::Type param);
|
||||
|
|
|
@ -594,7 +594,7 @@ typedef char __unaligned *aligned_type; // expected-error {{expected ';' after t
|
|||
|
||||
namespace PR32750 {
|
||||
template<typename T> struct A {};
|
||||
template<typename T> struct B : A<A<T>> { A<T>::C::D d; }; // expected-error {{missing 'typename' prior to dependent type name 'A<T>::C::D'}}
|
||||
template<typename T> struct B : A<A<T>> { A<T>::C::D d; }; // expected-warning {{implicit 'typename' is a C++20 extension}}
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
|
@ -108,8 +108,8 @@ struct DerivedFromDependentBase : BaseTemplate<T> {
|
|||
typename __super::XXX a;
|
||||
typedef typename __super::XXX b;
|
||||
|
||||
__super::XXX c; // expected-error {{missing 'typename'}}
|
||||
typedef __super::XXX d; // expected-error {{missing 'typename'}}
|
||||
__super::XXX c; // expected-warning {{implicit 'typename' is a C++20 extension}}
|
||||
typedef __super::XXX d; // expected-warning {{implicit 'typename' is a C++20 extension}}
|
||||
|
||||
void foo() {
|
||||
typename __super::XXX e;
|
||||
|
@ -127,8 +127,8 @@ struct DerivedFromTemplateParameter : T {
|
|||
typename __super::XXX a;
|
||||
typedef typename __super::XXX b;
|
||||
|
||||
__super::XXX c; // expected-error {{missing 'typename'}}
|
||||
typedef __super::XXX d; // expected-error {{missing 'typename'}}
|
||||
__super::XXX c; // expected-warning {{implicit 'typename' is a C++20 extension}}
|
||||
typedef __super::XXX d; // expected-warning {{implicit 'typename' is a C++20 extension}}
|
||||
|
||||
void foo() {
|
||||
typename __super::XXX e;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-linux -fsyntax-only -frounding-math -verify %s
|
||||
|
||||
template <class b> b::a() {} // expected-error {{nested name specifier}}
|
||||
template <class b> b::a() {}
|
||||
// expected-warning@-1 {{implicit 'typename' is a C++20 extension}}
|
||||
// expected-error@-2 {{expected unqualified-id}}
|
||||
|
|
|
@ -753,7 +753,7 @@ namespace PR46487 {
|
|||
// because it doesn't make the expression valid.
|
||||
// expected-error@+2 {{did you mean 'g_var_bool'}}
|
||||
// expected-error@+1 {{assigning to 'bool' from incompatible type 'void'}}
|
||||
enum : decltype((g_var_long = throw))::a {
|
||||
enum : typename decltype((g_var_long = throw))::a {
|
||||
b = g_volatile_uchar // expected-error {{did you mean 'g_volatile_char'}}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -36,15 +36,15 @@ struct A {
|
|||
|
||||
static int n;
|
||||
static type m;
|
||||
static int h(T::type, int); // expected-error{{missing 'typename'}}
|
||||
static int h(T::type x, char); // expected-error{{missing 'typename'}}
|
||||
static int h(T::type, int); // expected-warning{{implicit 'typename' is a C++20 extension}}
|
||||
static int h(T::type x, char); // expected-warning{{implicit 'typename' is a C++20 extension}}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
A<T>::type g(T t) { return t; } // expected-error{{missing 'typename'}}
|
||||
A<T>::type g(T t) { return t; } // expected-warning{{implicit 'typename' is a C++20 extension}}
|
||||
|
||||
template<typename T>
|
||||
A<T>::type A<T>::f() { return type(); } // expected-error{{missing 'typename'}}
|
||||
A<T>::type A<T>::f() { return type(); } // expected-warning{{implicit 'typename' is a C++20 extension}}
|
||||
|
||||
template<typename T>
|
||||
void f(T::type) { } // expected-error{{missing 'typename'}}
|
||||
|
@ -84,11 +84,11 @@ int *test(UnknownType *fool) { return 0; } // expected-error{{unknown type name
|
|||
|
||||
template<typename T> int A<T>::n(T::value); // ok
|
||||
template<typename T>
|
||||
A<T>::type // expected-error{{missing 'typename'}}
|
||||
A<T>::type // expected-warning {{implicit 'typename' is a C++20 extension}}
|
||||
A<T>::m(T::value, 0); // ok
|
||||
|
||||
template<typename T> int A<T>::h(T::type, int) {} // expected-error{{missing 'typename'}}
|
||||
template<typename T> int A<T>::h(T::type x, char) {} // expected-error{{missing 'typename'}}
|
||||
template<typename T> int A<T>::h(T::type, int) {} // expected-warning{{implicit 'typename' is a C++20 extension}}
|
||||
template<typename T> int A<T>::h(T::type x, char) {} // expected-warning{{implicit 'typename' is a C++20 extension}}
|
||||
|
||||
template<typename T> int h(T::type, int); // expected-error{{missing 'typename'}}
|
||||
template<typename T> int h(T::type x, char); // expected-error{{missing 'typename'}}
|
||||
|
@ -116,4 +116,5 @@ template<typename T> int i(T::type, int());
|
|||
// FIXME: We know which type specifier should have been specified here. Provide
|
||||
// a fix-it to add 'typename A<T>::type'
|
||||
template<typename T>
|
||||
A<T>::g() { } // expected-error{{a type specifier is required}}
|
||||
A<T>::g() { } // expected-error{{expected unqualified-id}}
|
||||
// expected-warning@-1{{implicit 'typename' is a C++20 extension}}
|
||||
|
|
|
@ -193,11 +193,10 @@ namespace PR16904 {
|
|||
struct base {
|
||||
template <typename> struct derived;
|
||||
};
|
||||
// FIXME: The diagnostics here are terrible.
|
||||
template <typename T, typename U, typename V>
|
||||
using derived = base<T, U>::template derived<V>; // expected-error {{expected a type}} expected-error {{expected ';'}}
|
||||
using derived = base<T, U>::template derived<V>; // expected-warning {{missing 'typename'}}
|
||||
template <typename T, typename U, typename V>
|
||||
using derived2 = ::PR16904::base<T, U>::template derived<V>; // expected-error {{expected a type}} expected-error {{expected ';'}}
|
||||
using derived2 = ::PR16904::base<T, U>::template derived<V>; // expected-warning {{missing 'typename'}}
|
||||
}
|
||||
|
||||
namespace PR14858 {
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace PR12884_original {
|
|||
typedef int arg;
|
||||
};
|
||||
struct C {
|
||||
typedef B::X<typename B::arg> x; // expected-error {{missing 'typename'}}
|
||||
typedef B::X<typename B::arg> x; // precxx17-warning{{missing 'typename' prior to dependent type name B::X; implicit 'typename' is a C++20 extension}}
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -959,11 +959,7 @@ code. This issue is expected to be rectified soon.
|
|||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://wg21.link/p2092r0">P2092R0</a></td>
|
||||
<td rowspan="1" class="partial" align="center">
|
||||
<details><summary>Partial</summary>
|
||||
<tt>typename</tt> not yet optional (depends on P0634R3).
|
||||
</details>
|
||||
</td>
|
||||
<td rowspan="1" class="unreleased" align="center">Clang 16</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://wg21.link/p2113r0">P2113R0</a></td>
|
||||
|
@ -1052,7 +1048,7 @@ code. This issue is expected to be rectified soon.
|
|||
<tr>
|
||||
<td><tt>typename</tt> optional in more contexts</td>
|
||||
<td><a href="https://wg21.link/p0634r3">P0634R3</a></td>
|
||||
<td class="none" align="center">No</td>
|
||||
<td class="unreleased" align="center">Clang 16</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pack expansion in lambda <i>init-capture</i></td>
|
||||
|
|
Loading…
Reference in New Issue