[clang] 4848f3b - [C++2a] P0634r3: Down with typename!
Alan Zhao via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 28 09:50:27 PDT 2022
Author: Nicolas Lesser
Date: 2022-09-28T09:50:19-07:00
New Revision: 4848f3bf2ff5ec57a8e2b8d3676c947dcf0fd735
URL: https://github.com/llvm/llvm-project/commit/4848f3bf2ff5ec57a8e2b8d3676c947dcf0fd735
DIFF: https://github.com/llvm/llvm-project/commit/4848f3bf2ff5ec57a8e2b8d3676c947dcf0fd735.diff
LOG: [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
Added:
clang/test/CXX/temp/temp.res/p4.cpp
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Parse/Parser.h
clang/include/clang/Sema/DeclSpec.h
clang/include/clang/Sema/Sema.h
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseDeclCXX.cpp
clang/lib/Parse/ParseExpr.cpp
clang/lib/Parse/ParseExprCXX.cpp
clang/lib/Parse/ParseTemplate.cpp
clang/lib/Parse/ParseTentative.cpp
clang/lib/Parse/Parser.cpp
clang/lib/Sema/Sema.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp
clang/test/CXX/drs/dr1xx.cpp
clang/test/CXX/drs/dr2xx.cpp
clang/test/CXX/drs/dr4xx.cpp
clang/test/CXX/drs/dr5xx.cpp
clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
clang/test/CXX/temp/temp.res/p3.cpp
clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp
clang/test/FixIt/fixit.cpp
clang/test/Parser/cxx-member-initializers.cpp
clang/test/SemaCXX/MicrosoftCompatibility.cpp
clang/test/SemaCXX/MicrosoftExtensions.cpp
clang/test/SemaCXX/MicrosoftSuper.cpp
clang/test/SemaCXX/rounding-math-crash.cpp
clang/test/SemaCXX/typo-correction.cpp
clang/test/SemaCXX/unknown-type-name.cpp
clang/test/SemaTemplate/alias-templates.cpp
clang/test/SemaTemplate/typename-specifier-3.cpp
clang/www/cxx_status.html
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ee31da39d1978..b82aec630287b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -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
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 2b544928ab763..1484622d8f8ae 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -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 "
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index e6144ae01c779..cb396b8b54122 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -857,9 +857,12 @@ class Parser : public CodeCompletionHandler {
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 @@ class Parser : public CodeCompletionHandler {
/// 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 @@ class Parser : public CodeCompletionHandler {
/// 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 @@ class Parser : public CodeCompletionHandler {
/// 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 @@ class Parser : public CodeCompletionHandler {
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 @@ class Parser : public CodeCompletionHandler {
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 @@ class Parser : public CodeCompletionHandler {
: 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 @@ class Parser : public CodeCompletionHandler {
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 @@ class Parser : public CodeCompletionHandler {
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 @@ class Parser : public CodeCompletionHandler {
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 @@ class Parser : public CodeCompletionHandler {
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 @@ class Parser : public CodeCompletionHandler {
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 @@ class Parser : public CodeCompletionHandler {
/// 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 @@ class Parser : public CodeCompletionHandler {
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 @@ class Parser : public CodeCompletionHandler {
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 @@ class Parser : public CodeCompletionHandler {
/// 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 @@ class Parser : public CodeCompletionHandler {
/// 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 @@ class Parser : public CodeCompletionHandler {
/// 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 @@ class Parser : public CodeCompletionHandler {
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 @@ class Parser : public CodeCompletionHandler {
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 @@ class Parser : public CodeCompletionHandler {
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();
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 1aa335b8b9b7a..0d56a7c94ab32 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -326,6 +326,11 @@ class DeclSpec {
// FIXME: Attributes should be included here.
};
+ enum FriendSpecified : bool {
+ No,
+ Yes,
+ };
+
private:
// storage-class-specifier
/*SCS*/unsigned StorageClassSpec : 3;
@@ -757,7 +762,10 @@ class DeclSpec {
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.
///
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 72995befeadf2..d89422f93efe1 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2573,6 +2573,8 @@ class Sema final {
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 @@ class Sema final {
/// \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 @@ class Sema final {
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 @@ class Sema final {
/// \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.,
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 4d25b22c9cd33..9363827f03db9 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -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
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 52e627a375442..639320c6bb474 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -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();
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 9b3c39e7b43fb..8209aff4bdc04 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -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);
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 207977f7176d9..7fa75d40af331 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -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
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 55eb3cb6399b5..138117e1a31ab 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -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);
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 9cd405a40efa1..90b281862d091 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -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
//
diff erence: 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 @@ class TentativeParseCCC final : public CorrectionCandidateCallback {
/// [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;
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 73e5e1b5f15a0..79528d25be21f 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -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;
}
}
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 9fdd5098e62d3..c229a77ff0b8d 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -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;
+}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 7aadd61e56982..77d9516b2f791 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -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);
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index cf501adbe1174..e2040f8d90281 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -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);
diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp
index 73e0369e608e1..be07ab0a48b33 100644
--- a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp
+++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp
@@ -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}}
diff --git a/clang/test/CXX/drs/dr1xx.cpp b/clang/test/CXX/drs/dr1xx.cpp
index 0a44b87f80a71..02ad3109bfcb7 100644
--- a/clang/test/CXX/drs/dr1xx.cpp
+++ b/clang/test/CXX/drs/dr1xx.cpp
@@ -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 at -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; };
diff --git a/clang/test/CXX/drs/dr2xx.cpp b/clang/test/CXX/drs/dr2xx.cpp
index eebb80a15af82..580e496857bd7 100644
--- a/clang/test/CXX/drs/dr2xx.cpp
+++ b/clang/test/CXX/drs/dr2xx.cpp
@@ -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 at -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 at -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 at -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 at -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 at -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 at -4 {{implicit 'typename' is a C++20 extension}}
+ // expected-error at -4 {{implicit 'typename' is a C++20 extension}}
+ // expected-error at -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 at -2 {{implicit 'typename' is a C++20 extension}}
+#endif
int f();
};
template <class T> int A<T>::f() {
diff --git a/clang/test/CXX/drs/dr4xx.cpp b/clang/test/CXX/drs/dr4xx.cpp
index 2bf6133f8ad22..4e437b272087b 100644
--- a/clang/test/CXX/drs/dr4xx.cpp
+++ b/clang/test/CXX/drs/dr4xx.cpp
@@ -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 at -2 {{implicit 'typename' is a C++20 extension}}
+#endif
};
}
diff --git a/clang/test/CXX/drs/dr5xx.cpp b/clang/test/CXX/drs/dr5xx.cpp
index 1eb3058f07359..c489878531159 100644
--- a/clang/test/CXX/drs/dr5xx.cpp
+++ b/clang/test/CXX/drs/dr5xx.cpp
@@ -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 at -3 {{implicit 'typename' is a C++20 extension}}
+ // expected-error at -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 at -2 {{call to deleted constructor}}
+ // expected-note at -3 {{in implicit initialization}}
+ // expected-note at -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 at -2 {{calling a private constructor}}
+ // expected-note at -5 {{declared private here}}
+#endif
#endif
}
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
index 02c1a9dcaf3b3..da4c16992d19e 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
@@ -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();
+};
+
diff --git a/clang/test/CXX/temp/temp.res/p3.cpp b/clang/test/CXX/temp/temp.res/p3.cpp
index 8984ce3494969..37ab93559e369 100644
--- a/clang/test/CXX/temp/temp.res/p3.cpp
+++ b/clang/test/CXX/temp/temp.res/p3.cpp
@@ -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'}}
};
diff --git a/clang/test/CXX/temp/temp.res/p4.cpp b/clang/test/CXX/temp/temp.res/p4.cpp
new file mode 100644
index 0000000000000..12ab355a26989
--- /dev/null
+++ b/clang/test/CXX/temp/temp.res/p4.cpp
@@ -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 at -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
diff erent 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) {}
diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp
index 81b070f040ee3..82983f05fe878 100644
--- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp
+++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp
@@ -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}}
};
}
diff --git a/clang/test/FixIt/fixit.cpp b/clang/test/FixIt/fixit.cpp
index 3c8797ae68532..3dbc6cf8e085d 100644
--- a/clang/test/FixIt/fixit.cpp
+++ b/clang/test/FixIt/fixit.cpp
@@ -211,7 +211,7 @@ struct MoreAccidentalCommas {
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();
}
diff --git a/clang/test/Parser/cxx-member-initializers.cpp b/clang/test/Parser/cxx-member-initializers.cpp
index f5b09752d72b1..c29260b731223 100644
--- a/clang/test/Parser/cxx-member-initializers.cpp
+++ b/clang/test/Parser/cxx-member-initializers.cpp
@@ -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 at -1 {{declaration of anonymous union must be a definition}}
+ // expected-error at -2 {{type name requires a specifier or qualifier}}
+ // expected-error at -3 {{expected '>'}}
+ // expected-note at -4 {{to match this '<'}}
};
diff --git a/clang/test/SemaCXX/MicrosoftCompatibility.cpp b/clang/test/SemaCXX/MicrosoftCompatibility.cpp
index 8513177b4fc25..a830883280173 100644
--- a/clang/test/SemaCXX/MicrosoftCompatibility.cpp
+++ b/clang/test/SemaCXX/MicrosoftCompatibility.cpp
@@ -211,14 +211,14 @@ class C : private A<T>, public B<U> {
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 @@ class D {
};
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);
diff --git a/clang/test/SemaCXX/MicrosoftExtensions.cpp b/clang/test/SemaCXX/MicrosoftExtensions.cpp
index 7f9681dcda445..effe2e6e6f086 100644
--- a/clang/test/SemaCXX/MicrosoftExtensions.cpp
+++ b/clang/test/SemaCXX/MicrosoftExtensions.cpp
@@ -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
diff --git a/clang/test/SemaCXX/MicrosoftSuper.cpp b/clang/test/SemaCXX/MicrosoftSuper.cpp
index 1d1460f74ffa6..94e29b23ef11c 100644
--- a/clang/test/SemaCXX/MicrosoftSuper.cpp
+++ b/clang/test/SemaCXX/MicrosoftSuper.cpp
@@ -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;
diff --git a/clang/test/SemaCXX/rounding-math-crash.cpp b/clang/test/SemaCXX/rounding-math-crash.cpp
index fb45d236b41bd..2a09b02fe9cef 100644
--- a/clang/test/SemaCXX/rounding-math-crash.cpp
+++ b/clang/test/SemaCXX/rounding-math-crash.cpp
@@ -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 at -1 {{implicit 'typename' is a C++20 extension}}
+// expected-error at -2 {{expected unqualified-id}}
diff --git a/clang/test/SemaCXX/typo-correction.cpp b/clang/test/SemaCXX/typo-correction.cpp
index 60141ee13d597..d466d9c49c3a3 100644
--- a/clang/test/SemaCXX/typo-correction.cpp
+++ b/clang/test/SemaCXX/typo-correction.cpp
@@ -753,7 +753,7 @@ namespace PR46487 {
// because it doesn't make the expression valid.
// expected-error at +2 {{did you mean 'g_var_bool'}}
// expected-error at +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'}}
};
}
diff --git a/clang/test/SemaCXX/unknown-type-name.cpp b/clang/test/SemaCXX/unknown-type-name.cpp
index 6fc3137088287..602f8f9ec7d29 100644
--- a/clang/test/SemaCXX/unknown-type-name.cpp
+++ b/clang/test/SemaCXX/unknown-type-name.cpp
@@ -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 at -1{{implicit 'typename' is a C++20 extension}}
diff --git a/clang/test/SemaTemplate/alias-templates.cpp b/clang/test/SemaTemplate/alias-templates.cpp
index 6dffd94892943..8d7cc6118610a 100644
--- a/clang/test/SemaTemplate/alias-templates.cpp
+++ b/clang/test/SemaTemplate/alias-templates.cpp
@@ -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 {
diff --git a/clang/test/SemaTemplate/typename-specifier-3.cpp b/clang/test/SemaTemplate/typename-specifier-3.cpp
index de2b9b61ebbab..714830f0032d2 100644
--- a/clang/test/SemaTemplate/typename-specifier-3.cpp
+++ b/clang/test/SemaTemplate/typename-specifier-3.cpp
@@ -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}}
};
};
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 06ad3868dd9d7..d895cb75ec7ac 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -959,11 +959,7 @@ <h2 id="cxx20">C++20 implementation status</h2>
</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 @@ <h2 id="cxx20">C++20 implementation status</h2>
<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>
More information about the cfe-commits
mailing list