[clang] [Clang] Fix name lookup of conversion operators (PR #142945)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 5 09:43:40 PDT 2025
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/142945
>From cdd6868879abf4b6c991c7f2b3e9cf9673b0570a Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 5 Jun 2025 18:52:01 +0800
Subject: [PATCH 1/2] [Clang] Fix name lookup of conversion operators
(TODO: Add explanation)
---
clang/include/clang/Parse/Parser.h | 57 ++++++++++++-------
clang/include/clang/Sema/DeclSpec.h | 23 +++++---
clang/include/clang/Sema/Sema.h | 1 +
clang/lib/Parse/ParseDecl.cpp | 32 +++++++----
clang/lib/Parse/ParseDeclCXX.cpp | 10 +++-
clang/lib/Parse/ParseExpr.cpp | 3 +-
clang/lib/Parse/ParseExprCXX.cpp | 42 +++++++++-----
clang/lib/Parse/ParseOpenMP.cpp | 16 +++---
clang/lib/Parse/ParseStmtAsm.cpp | 3 +-
clang/lib/Parse/ParseTemplate.cpp | 1 +
clang/lib/Parse/Parser.cpp | 9 ++-
clang/lib/Sema/SemaDecl.cpp | 20 ++++---
clang/lib/Sema/SemaType.cpp | 4 ++
.../basic.lookup/basic.lookup.unqual/p5.cpp | 52 +++++++++++++++++
clang/test/CXX/drs/cwg11xx.cpp | 4 +-
clang/test/CXX/temp/temp.res/p4.cpp | 2 +-
clang/unittests/Tooling/TestVisitor.h | 8 +--
17 files changed, 204 insertions(+), 83 deletions(-)
create mode 100644 clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p5.cpp
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 98db8201390be..16764b5ba1ad6 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -359,7 +359,8 @@ class Parser : public CodeCompletionHandler {
/// 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 TryAnnotateCXXScopeToken(bool EnteringContext = false);
+ bool TryAnnotateCXXScopeToken(bool EnteringContext = false,
+ ParsedType ObjectType = nullptr);
bool MightBeCXXScopeToken() {
return getLangOpts().CPlusPlus &&
@@ -1525,8 +1526,10 @@ class Parser : public CodeCompletionHandler {
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_conv_operator, // C++ type-specifier-seq in an conversion operator
+ DSC_alias_declaration, // C++11 type-specifier-seq in an alias-declaration
+ DSC_conv_operator, // C++ type-specifier-seq in an conversion operator
+ DSC_conv_operator_in_postfix_expr, // C++ type-specifier-seq which is
+ // referenced in a postfix expression
DSC_top_level, // top-level/namespace declaration context
DSC_template_param, // template parameter context
DSC_template_arg, // template argument context
@@ -1554,6 +1557,7 @@ class Parser : public CodeCompletionHandler {
case DeclSpecContext::DSC_template_type_arg:
case DeclSpecContext::DSC_type_specifier:
case DeclSpecContext::DSC_conv_operator:
+ case DeclSpecContext::DSC_conv_operator_in_postfix_expr:
case DeclSpecContext::DSC_trailing:
case DeclSpecContext::DSC_alias_declaration:
case DeclSpecContext::DSC_association:
@@ -1605,6 +1609,7 @@ class Parser : public CodeCompletionHandler {
case DeclSpecContext::DSC_trailing:
case DeclSpecContext::DSC_conv_operator:
+ case DeclSpecContext::DSC_conv_operator_in_postfix_expr:
case DeclSpecContext::DSC_template_arg:
case DeclSpecContext::DSC_new:
return AllowDefiningTypeSpec::No;
@@ -1629,6 +1634,7 @@ class Parser : public CodeCompletionHandler {
case DeclSpecContext::DSC_trailing:
case DeclSpecContext::DSC_association:
case DeclSpecContext::DSC_conv_operator:
+ case DeclSpecContext::DSC_conv_operator_in_postfix_expr:
case DeclSpecContext::DSC_template_arg:
case DeclSpecContext::DSC_new:
@@ -1650,6 +1656,7 @@ class Parser : public CodeCompletionHandler {
case DeclSpecContext::DSC_type_specifier:
case DeclSpecContext::DSC_association:
case DeclSpecContext::DSC_conv_operator:
+ case DeclSpecContext::DSC_conv_operator_in_postfix_expr:
case DeclSpecContext::DSC_new:
return true;
@@ -1673,6 +1680,7 @@ class Parser : public CodeCompletionHandler {
case DeclSpecContext::DSC_trailing:
case DeclSpecContext::DSC_alias_declaration:
case DeclSpecContext::DSC_template_param:
+ case DeclSpecContext::DSC_conv_operator_in_postfix_expr:
case DeclSpecContext::DSC_new:
return ImplicitTypenameContext::Yes;
@@ -1826,9 +1834,11 @@ class Parser : public CodeCompletionHandler {
ParseDeclarationSpecifiers(DeclSpec &DS, ParsedTemplateInfo &TemplateInfo,
AccessSpecifier AS = AS_none,
DeclSpecContext DSC = DeclSpecContext::DSC_normal,
- LateParsedAttrList *LateAttrs = nullptr) {
+ LateParsedAttrList *LateAttrs = nullptr,
+ ParsedType ObjectType = nullptr) {
return ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC, LateAttrs,
- getImplicitTypenameContext(DSC));
+ getImplicitTypenameContext(DSC),
+ ObjectType);
}
/// ParseDeclarationSpecifiers
@@ -1860,11 +1870,12 @@ class Parser : public CodeCompletionHandler {
/// 'friend': [C++ dcl.friend]
/// 'constexpr': [C++0x dcl.constexpr]
/// \endverbatim
- void
- ParseDeclarationSpecifiers(DeclSpec &DS, ParsedTemplateInfo &TemplateInfo,
- AccessSpecifier AS, DeclSpecContext DSC,
- LateParsedAttrList *LateAttrs,
- ImplicitTypenameContext AllowImplicitTypename);
+ void ParseDeclarationSpecifiers(DeclSpec &DS,
+ ParsedTemplateInfo &TemplateInfo,
+ AccessSpecifier AS, DeclSpecContext DSC,
+ LateParsedAttrList *LateAttrs,
+ ImplicitTypenameContext AllowImplicitTypename,
+ ParsedType ObjectType);
/// Determine whether we're looking at something that might be a declarator
/// in a simple-declaration. If it can't possibly be a declarator, maybe
@@ -1877,10 +1888,12 @@ class Parser : public CodeCompletionHandler {
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, AccessSpecifier AS = AS_none,
+ DeclSpecContext DSC = DeclSpecContext::DSC_normal,
+ ParsedType ObjectType = nullptr) {
+ ParseSpecifierQualifierList(DS, getImplicitTypenameContext(DSC), AS, DSC,
+ ObjectType);
}
/// ParseSpecifierQualifierList
@@ -1891,10 +1904,12 @@ class Parser : public CodeCompletionHandler {
/// [GNU] attributes specifier-qualifier-list[opt]
/// \endverbatim
///
- void ParseSpecifierQualifierList(
- DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename,
- AccessSpecifier AS = AS_none,
- DeclSpecContext DSC = DeclSpecContext::DSC_normal);
+ void
+ ParseSpecifierQualifierList(DeclSpec &DS,
+ ImplicitTypenameContext AllowImplicitTypename,
+ AccessSpecifier AS = AS_none,
+ DeclSpecContext DSC = DeclSpecContext::DSC_normal,
+ ParsedType ObjectType = nullptr);
/// ParseEnumSpecifier
/// \verbatim
@@ -4413,7 +4428,7 @@ class Parser : public CodeCompletionHandler {
bool ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
bool ObjectHadErrors, bool EnteringContext,
bool AllowDestructorName, bool AllowConstructorName,
- bool AllowDeductionGuide,
+ bool AllowDeductionGuide, bool ForPostfixExpression,
SourceLocation *TemplateKWLoc, UnqualifiedId &Result);
private:
@@ -4826,7 +4841,8 @@ class Parser : public CodeCompletionHandler {
/// \endverbatim
///
bool ParseCXXTypeSpecifierSeq(
- DeclSpec &DS, DeclaratorContext Context = DeclaratorContext::TypeName);
+ DeclSpec &DS, DeclaratorContext Context = DeclaratorContext::TypeName,
+ ParsedType ObjectType = nullptr);
//===--------------------------------------------------------------------===//
// C++ 5.3.4 and 5.3.5: C++ new and delete
@@ -5091,6 +5107,7 @@ class Parser : public CodeCompletionHandler {
///
/// \returns true if parsing fails, false otherwise.
bool ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
+ bool ForPostfixExpression,
ParsedType ObjectType, UnqualifiedId &Result);
//===--------------------------------------------------------------------===//
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 6c4a32c4ac2f0..e33411a1bd7dd 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -1871,14 +1871,15 @@ enum class DeclaratorContext {
LambdaExpr, // Lambda-expression declarator.
LambdaExprParameter, // Lambda-expression parameter declarator.
ConversionId, // C++ conversion-type-id.
- TrailingReturn, // C++11 trailing-type-specifier.
- TrailingReturnVar, // C++11 trailing-type-specifier for variable.
- TemplateArg, // Any template argument (in template argument list).
- TemplateTypeArg, // Template type argument (in default argument).
- AliasDecl, // C++11 alias-declaration.
- AliasTemplate, // C++11 alias-declaration template.
- RequiresExpr, // C++2a requires-expression.
- Association // C11 _Generic selection expression association.
+ ConversionIdInPostfixExpr, // C++ conversion-type-id.
+ TrailingReturn, // C++11 trailing-type-specifier.
+ TrailingReturnVar, // C++11 trailing-type-specifier for variable.
+ TemplateArg, // Any template argument (in template argument list).
+ TemplateTypeArg, // Template type argument (in default argument).
+ AliasDecl, // C++11 alias-declaration.
+ AliasTemplate, // C++11 alias-declaration template.
+ RequiresExpr, // C++2a requires-expression.
+ Association, // C11 _Generic selection expression association.
};
// Describes whether the current context is a context where an implicit
@@ -2159,6 +2160,7 @@ class Declarator {
case DeclaratorContext::BlockLiteral:
case DeclaratorContext::LambdaExpr:
case DeclaratorContext::ConversionId:
+ case DeclaratorContext::ConversionIdInPostfixExpr:
case DeclaratorContext::TemplateArg:
case DeclaratorContext::TemplateTypeArg:
case DeclaratorContext::TrailingReturn:
@@ -2200,6 +2202,7 @@ class Declarator {
case DeclaratorContext::BlockLiteral:
case DeclaratorContext::LambdaExpr:
case DeclaratorContext::ConversionId:
+ case DeclaratorContext::ConversionIdInPostfixExpr:
case DeclaratorContext::TemplateArg:
case DeclaratorContext::TemplateTypeArg:
case DeclaratorContext::TrailingReturn:
@@ -2244,6 +2247,7 @@ class Declarator {
case DeclaratorContext::BlockLiteral:
case DeclaratorContext::LambdaExpr:
case DeclaratorContext::ConversionId:
+ case DeclaratorContext::ConversionIdInPostfixExpr:
case DeclaratorContext::TemplateArg:
case DeclaratorContext::TemplateTypeArg:
case DeclaratorContext::TrailingReturn:
@@ -2301,6 +2305,7 @@ class Declarator {
case DeclaratorContext::BlockLiteral:
case DeclaratorContext::LambdaExpr:
case DeclaratorContext::ConversionId:
+ case DeclaratorContext::ConversionIdInPostfixExpr:
case DeclaratorContext::TemplateArg:
case DeclaratorContext::TemplateTypeArg:
case DeclaratorContext::TrailingReturn:
@@ -2539,6 +2544,7 @@ class Declarator {
case DeclaratorContext::BlockLiteral:
case DeclaratorContext::LambdaExpr:
case DeclaratorContext::ConversionId:
+ case DeclaratorContext::ConversionIdInPostfixExpr:
case DeclaratorContext::TemplateArg:
case DeclaratorContext::TemplateTypeArg:
case DeclaratorContext::TrailingReturn:
@@ -2587,6 +2593,7 @@ class Declarator {
case DeclaratorContext::SelectionInit:
case DeclaratorContext::Condition:
case DeclaratorContext::TemplateArg:
+ case DeclaratorContext::ConversionIdInPostfixExpr:
return true;
}
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 84d30561fecde..265d35bff16c3 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3568,6 +3568,7 @@ class Sema final : public SemaBase {
bool isClassName = false, bool HasTrailingDot = false,
ParsedType ObjectType = nullptr,
bool IsCtorOrDtorName = false,
+ bool IsOperatorName = false,
bool WantNontrivialTypeSourceInfo = false,
bool IsClassTemplateDeductionContext = true,
ImplicitTypenameContext AllowImplicitTypename =
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index d6c36616bab47..11477da097f43 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2728,13 +2728,13 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
void Parser::ParseSpecifierQualifierList(
DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename,
- AccessSpecifier AS, DeclSpecContext DSC) {
+ AccessSpecifier AS, DeclSpecContext DSC, ParsedType ObjectType) {
ParsedTemplateInfo TemplateInfo;
/// 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, TemplateInfo, AS, DSC, nullptr,
- AllowImplicitTypename);
+ AllowImplicitTypename, ObjectType);
// Validate declspec for type-name.
unsigned Specs = DS.getParsedSpecifiers();
@@ -3055,6 +3055,8 @@ Parser::getDeclSpecContextFromDeclaratorContext(DeclaratorContext Context) {
return DeclSpecContext::DSC_condition;
case DeclaratorContext::ConversionId:
return DeclSpecContext::DSC_conv_operator;
+ case DeclaratorContext::ConversionIdInPostfixExpr:
+ return DeclSpecContext::DSC_conv_operator_in_postfix_expr;
case DeclaratorContext::CXXNew:
return DeclSpecContext::DSC_new;
case DeclaratorContext::Prototype:
@@ -3360,7 +3362,7 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS,
void Parser::ParseDeclarationSpecifiers(
DeclSpec &DS, ParsedTemplateInfo &TemplateInfo, AccessSpecifier AS,
DeclSpecContext DSContext, LateParsedAttrList *LateAttrs,
- ImplicitTypenameContext AllowImplicitTypename) {
+ ImplicitTypenameContext AllowImplicitTypename, ParsedType ObjectType) {
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
@@ -3369,7 +3371,7 @@ void Parser::ParseDeclarationSpecifiers(
DS.SetRangeEnd(SourceLocation());
}
- // If we are in a operator context, convert it back into a type specifier
+ // If we are in an 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.
@@ -3610,7 +3612,10 @@ void Parser::ParseDeclarationSpecifiers(
ConsumeAnnotationToken(); // The C++ scope.
assert(Tok.is(tok::annot_template_id) &&
"ParseOptionalCXXScopeSpecifier not working");
- AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename);
+ AnnotateTemplateIdTokenAsType(
+ SS, AllowImplicitTypename,
+ /*IsClassName=*/DSContext ==
+ DeclSpecContext::DSC_conv_operator_in_postfix_expr);
continue;
}
@@ -3673,6 +3678,8 @@ void Parser::ParseDeclarationSpecifiers(
*Next.getIdentifierInfo(), Next.getLocation(), getCurScope(), &SS,
false, false, nullptr,
/*IsCtorOrDtorName=*/false,
+ /*IsOperatorName=*/DSContext ==
+ DeclSpecContext::DSC_conv_operator_in_postfix_expr,
/*WantNontrivialTypeSourceInfo=*/true,
isClassTemplateDeductionContext(DSContext), AllowImplicitTypename);
@@ -3796,12 +3803,13 @@ void Parser::ParseDeclarationSpecifiers(
// - `return type`.
SuppressAccessChecks SAC(*this, IsTemplateSpecOrInst);
- const bool Success = TryAnnotateCXXScopeToken(EnteringContext);
+ const bool Failed =
+ TryAnnotateCXXScopeToken(EnteringContext, ObjectType);
if (IsTemplateSpecOrInst)
SAC.done();
- if (Success) {
+ if (Failed) {
if (IsTemplateSpecOrInst)
SAC.redelay();
DS.SetTypeSpecError();
@@ -3846,7 +3854,9 @@ void Parser::ParseDeclarationSpecifiers(
ParsedType TypeRep = Actions.getTypeName(
*Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope(), nullptr,
- false, false, nullptr, false, false,
+ false, false, ObjectType, false,
+ /*IsOperatorName=*/DSContext ==
+ DeclSpecContext::DSC_conv_operator_in_postfix_expr,
isClassTemplateDeductionContext(DSContext));
// If this is not a typedef name, don't parse it as part of the declspec,
@@ -6648,7 +6658,8 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
/*ObjectHadErrors=*/false,
/*EnteringContext=*/true,
/*AllowDestructorName=*/true, AllowConstructorName,
- AllowDeductionGuide, &TemplateKWLoc,
+ AllowDeductionGuide,
+ /*ForPostfixExpression=*/false, &TemplateKWLoc,
D.getName()) ||
// Once we're past the identifier, if the scope was bad, mark the
// whole declarator bad.
@@ -7509,7 +7520,8 @@ void Parser::ParseParameterDeclarationClause(
ParsedTemplateInfo TemplateInfo;
ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none,
DeclSpecContext::DSC_normal,
- /*LateAttrs=*/nullptr, AllowImplicitTypename);
+ /*LateAttrs=*/nullptr, AllowImplicitTypename,
+ /*ObjectType=*/nullptr);
DS.takeAttributesFrom(ArgDeclSpecAttrs);
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 2cf33a856c4f4..21686bb5a97dd 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -604,7 +604,8 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
/*AllowDestructorName=*/true,
/*AllowConstructorName=*/
!(Tok.is(tok::identifier) && NextToken().is(tok::equal)),
- /*AllowDeductionGuide=*/false, nullptr, D.Name))
+ /*AllowDeductionGuide=*/false, /*ForPostfixExpression=*/false,
+ nullptr, D.Name))
return true;
}
@@ -641,7 +642,7 @@ Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
}
CXXScopeSpec SS;
if (ParseOptionalCXXScopeSpecifier(SS, /*ParsedType=*/nullptr,
- /*ObectHasErrors=*/false,
+ /*ObjectHasErrors=*/false,
/*EnteringConttext=*/false,
/*MayBePseudoDestructor=*/nullptr,
/*IsTypename=*/true,
@@ -670,6 +671,7 @@ Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
*IdentInfo, IdentLoc, getCurScope(), &SS, /*isClassName=*/true,
/*HasTrailingDot=*/false,
/*ObjectType=*/nullptr, /*IsCtorOrDtorName=*/false,
+ /*IsOperatorName=*/false,
/*WantNontrivialTypeSourceInfo=*/true);
UED = Actions.ActOnUsingEnumDeclaration(
@@ -1399,6 +1401,7 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
ParsedType Type = Actions.getTypeName(
*Id, IdLoc, getCurScope(), &SS, /*isClassName=*/true, false, nullptr,
/*IsCtorOrDtorName=*/false,
+ /*IsOperatorName=*/false,
/*WantNontrivialTypeSourceInfo=*/true,
/*IsClassTemplateDeductionContext=*/false, ImplicitTypenameContext::No,
&CorrectedII);
@@ -2750,7 +2753,8 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
UnqualifiedId Name;
if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
/*ObjectHadErrors=*/false, false, true, true,
- false, &TemplateKWLoc, Name)) {
+ false, /*ForPostfixExpression=*/false,
+ &TemplateKWLoc, Name)) {
SkipUntil(tok::semi);
return nullptr;
}
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 951a157305ddc..a18d0ac262de3 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -2049,7 +2049,8 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
/*AllowDestructorName=*/true,
/*AllowConstructorName=*/
getLangOpts().MicrosoftExt && SS.isNotEmpty(),
- /*AllowDeductionGuide=*/false, &TemplateKWLoc, Name)) {
+ /*AllowDeductionGuide=*/false,
+ /*ForPostfixExpression=*/true, &TemplateKWLoc, Name)) {
(void)Actions.CorrectDelayedTyposInExpr(LHS);
LHS = ExprError();
}
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index d95260829e4a0..d74f51b2fb7dc 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -283,8 +283,9 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
// because a simple-template-id cannot start with 'operator', but
// go ahead and parse it anyway for consistency with the case where
// we already annotated the template-id.
- if (ParseUnqualifiedIdOperator(SS, EnteringContext, ObjectType,
- TemplateName)) {
+ if (ParseUnqualifiedIdOperator(SS, EnteringContext,
+ /*ForPostfixExpression=*/false,
+ ObjectType, TemplateName)) {
TPA.Commit();
break;
}
@@ -608,7 +609,8 @@ ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS,
/*EnteringContext=*/false,
/*AllowDestructorName=*/false,
/*AllowConstructorName=*/false,
- /*AllowDeductionGuide=*/false, &TemplateKWLoc, Name))
+ /*AllowDeductionGuide=*/false,
+ /*ForPostfixExpression=*/true, &TemplateKWLoc, Name))
return ExprError();
// This is only the direct operand of an & operator if it is not
@@ -2212,9 +2214,11 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) {
DS.Finish(Actions, Policy);
}
-bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS, DeclaratorContext Context) {
+bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS, DeclaratorContext Context,
+ ParsedType ObjectType) {
ParseSpecifierQualifierList(DS, AS_none,
- getDeclSpecContextFromDeclaratorContext(Context));
+ getDeclSpecContextFromDeclaratorContext(Context),
+ ObjectType);
DS.Finish(Actions, Actions.getASTContext().getPrintingPolicy());
return false;
}
@@ -2375,6 +2379,7 @@ bool Parser::ParseUnqualifiedIdTemplateId(
}
bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
+ bool ForPostfixExpression,
ParsedType ObjectType,
UnqualifiedId &Result) {
assert(Tok.is(tok::kw_operator) && "Expected 'operator' keyword");
@@ -2555,8 +2560,18 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
// Parse the type-specifier-seq.
DeclSpec DS(AttrFactory);
+ if (ForPostfixExpression && !SS.isEmpty() && Tok.is(tok::identifier)) {
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ EnteringContext))
+ return true;
+ AnnotateScopeToken(SS, /*IsNewAnnotation=*/true);
+ }
if (ParseCXXTypeSpecifierSeq(
- DS, DeclaratorContext::ConversionId)) // FIXME: ObjectType?
+ DS,
+ ForPostfixExpression ? DeclaratorContext::ConversionIdInPostfixExpr
+ : DeclaratorContext::ConversionId,
+ ObjectType))
return true;
// Parse the conversion-declarator, which is merely a sequence of
@@ -2576,13 +2591,11 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
return false;
}
-bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
- bool ObjectHadErrors, bool EnteringContext,
- bool AllowDestructorName,
- bool AllowConstructorName,
- bool AllowDeductionGuide,
- SourceLocation *TemplateKWLoc,
- UnqualifiedId &Result) {
+bool Parser::ParseUnqualifiedId(
+ CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
+ bool EnteringContext, bool AllowDestructorName, bool AllowConstructorName,
+ bool AllowDeductionGuide, bool ForPostfixExpression,
+ SourceLocation *TemplateKWLoc, UnqualifiedId &Result) {
if (TemplateKWLoc)
*TemplateKWLoc = SourceLocation();
@@ -2723,7 +2736,8 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
// operator-function-id
// conversion-function-id
if (Tok.is(tok::kw_operator)) {
- if (ParseUnqualifiedIdOperator(SS, EnteringContext, ObjectType, Result))
+ if (ParseUnqualifiedIdOperator(SS, EnteringContext, ForPostfixExpression,
+ ObjectType, Result))
return true;
// If we have an operator-function-id or a literal-operator-id and the next
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index e41e5ba8596b9..6a149131b046e 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -2997,7 +2997,7 @@ bool Parser::ParseOpenMPSimpleVarList(
StopBeforeMatch);
} else if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
/*ObjectHadErrors=*/false, false, false,
- false, false, nullptr, Name)) {
+ false, false, false, nullptr, Name)) {
IsCorrect = false;
SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
StopBeforeMatch);
@@ -4052,12 +4052,14 @@ static bool ParseReductionId(Parser &P, CXXScopeSpec &ReductionIdScopeSpec,
return false;
}
}
- return P.ParseUnqualifiedId(
- ReductionIdScopeSpec, /*ObjectType=*/nullptr,
- /*ObjectHadErrors=*/false, /*EnteringContext*/ false,
- /*AllowDestructorName*/ false,
- /*AllowConstructorName*/ false,
- /*AllowDeductionGuide*/ false, nullptr, ReductionId);
+ return P.ParseUnqualifiedId(ReductionIdScopeSpec, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext*/ false,
+ /*AllowDestructorName*/ false,
+ /*AllowConstructorName*/ false,
+ /*AllowDeductionGuide*/ false,
+ /*ForPostfixExpression=*/false, nullptr,
+ ReductionId);
}
/// Checks if the token is a valid map-type-modifier.
diff --git a/clang/lib/Parse/ParseStmtAsm.cpp b/clang/lib/Parse/ParseStmtAsm.cpp
index f2417479a0e78..ac240138bc492 100644
--- a/clang/lib/Parse/ParseStmtAsm.cpp
+++ b/clang/lib/Parse/ParseStmtAsm.cpp
@@ -237,7 +237,8 @@ ExprResult Parser::ParseMSAsmIdentifier(llvm::SmallVectorImpl<Token> &LineToks,
/*EnteringContext=*/false,
/*AllowDestructorName=*/false,
/*AllowConstructorName=*/false,
- /*AllowDeductionGuide=*/false, &TemplateKWLoc, Id);
+ /*AllowDeductionGuide=*/false,
+ /*ForPostfixExpression=*/false, &TemplateKWLoc, Id);
// Perform the lookup.
Result = Actions.LookupInlineAsmIdentifier(SS, TemplateKWLoc, Id,
IsUnevaluatedContext);
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index d3c9ca029c9aa..dfd8e5d835de1 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -266,6 +266,7 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo,
/*AllowDestructorName=*/false,
/*AllowConstructorName=*/false,
/*AllowDeductionGuide=*/false,
+ /*ForPostfixExpression=*/false,
/*TemplateKWLoc=*/nullptr, Result)) {
SkipUntil(tok::semi);
return nullptr;
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index db65c05cc114a..39bd83eccd02d 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -2019,6 +2019,7 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(
*Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope(), &SS,
false, NextToken().is(tok::period), nullptr,
/*IsCtorOrDtorName=*/false,
+ /*IsOperatorName=*/false,
/*NonTrivialTypeSourceInfo=*/true,
/*IsClassTemplateDeductionContext=*/true, AllowImplicitTypename)) {
SourceLocation BeginLoc = Tok.getLocation();
@@ -2126,13 +2127,14 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(
return false;
}
-bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext) {
+bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext,
+ ParsedType ObjectType) {
assert(getLangOpts().CPlusPlus &&
"Call sites of this function should be guarded by checking for C++");
assert(MightBeCXXScopeToken() && "Cannot be a type or scope token!");
CXXScopeSpec SS;
- if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/ObjectType,
/*ObjectHasErrors=*/false,
EnteringContext))
return true;
@@ -2262,7 +2264,8 @@ bool Parser::ParseMicrosoftIfExistsCondition(IfExistsCondition& Result) {
/*ObjectHadErrors=*/false, /*EnteringContext*/ false,
/*AllowDestructorName*/ true,
/*AllowConstructorName*/ true,
- /*AllowDeductionGuide*/ false, &TemplateKWLoc,
+ /*AllowDeductionGuide*/ false,
+ /*ForPostfixExpression=*/false, &TemplateKWLoc,
Result.Name)) {
T.skipToEnd();
return true;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 86b871396ec90..089a660c89179 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -311,12 +311,13 @@ static ParsedType buildNamedType(Sema &S, const CXXScopeSpec *SS, QualType T,
ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
Scope *S, CXXScopeSpec *SS, bool isClassName,
bool HasTrailingDot, ParsedType ObjectTypePtr,
- bool IsCtorOrDtorName,
+ bool IsCtorOrDtorName, bool IsOperatorName,
bool WantNontrivialTypeSourceInfo,
bool IsClassTemplateDeductionContext,
ImplicitTypenameContext AllowImplicitTypename,
IdentifierInfo **CorrectedII) {
- bool IsImplicitTypename = !isClassName && !IsCtorOrDtorName;
+ bool IsImplicitTypename =
+ !isClassName && !IsCtorOrDtorName && !IsOperatorName;
// FIXME: Consider allowing this outside C++1z mode as an extension.
bool AllowDeducedTemplate = IsClassTemplateDeductionContext &&
getLangOpts().CPlusPlus17 && IsImplicitTypename &&
@@ -368,6 +369,9 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
: ElaboratedTypeKeyword::None,
SourceLocation(), QualifierLoc, II, NameLoc);
return ParsedType::make(T);
+ // return CreateParsedType(
+ // T, Context.getTrivialTypeSourceInfo(T,
+ // QualifierLoc.getBeginLoc()));
}
return nullptr;
@@ -393,7 +397,7 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
// nested-name-specifier.
LookupQualifiedName(Result, LookupCtx);
- if (ObjectTypePtr && Result.empty()) {
+ if ((ObjectTypePtr || IsOperatorName) && Result.empty()) {
// C++ [basic.lookup.classref]p3:
// If the unqualified-id is ~type-name, the type-name is looked up
// in the context of the entire postfix-expression. If the type T of
@@ -443,11 +447,10 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
!(getLangOpts().CPlusPlus && NewSSPtr &&
isTemplateName(S, *NewSSPtr, false, TemplateName, nullptr, false,
Template, MemberOfUnknownSpecialization))) {
- ParsedType Ty = getTypeName(*NewII, NameLoc, S, NewSSPtr,
- isClassName, HasTrailingDot, ObjectTypePtr,
- IsCtorOrDtorName,
- WantNontrivialTypeSourceInfo,
- IsClassTemplateDeductionContext);
+ ParsedType Ty = getTypeName(
+ *NewII, NameLoc, S, NewSSPtr, isClassName, HasTrailingDot,
+ ObjectTypePtr, IsCtorOrDtorName, IsOperatorName,
+ WantNontrivialTypeSourceInfo, IsClassTemplateDeductionContext);
if (Ty) {
diagnoseTypo(Correction,
PDiag(diag::err_unknown_type_or_class_name_suggest)
@@ -755,6 +758,7 @@ void Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II,
getTypeName(*Corrected.getCorrectionAsIdentifierInfo(), IILoc, S,
tmpSS.isSet() ? &tmpSS : SS, false, false, nullptr,
/*IsCtorOrDtorName=*/false,
+ /*IsOperatorName=*/false,
/*WantNontrivialTypeSourceInfo=*/true);
}
return;
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 338b81fe89748..06807a7a1f6e5 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -3310,6 +3310,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
IsDeducedReturnType = true;
break;
case DeclaratorContext::ConversionId:
+ case DeclaratorContext::ConversionIdInPostfixExpr:
if (!SemaRef.getLangOpts().CPlusPlus14 || !IsCXXAutoType)
Error = 14; // conversion-type-id
IsDeducedReturnType = true;
@@ -3432,6 +3433,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
case DeclaratorContext::TypeName:
case DeclaratorContext::FunctionalCast:
case DeclaratorContext::ConversionId:
+ case DeclaratorContext::ConversionIdInPostfixExpr:
case DeclaratorContext::TemplateParam:
case DeclaratorContext::CXXNew:
case DeclaratorContext::CXXCatch:
@@ -4488,6 +4490,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
}
case DeclaratorContext::ConversionId:
+ case DeclaratorContext::ConversionIdInPostfixExpr:
complainAboutMissingNullability = CAMN_Yes;
break;
@@ -5663,6 +5666,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
case DeclaratorContext::BlockLiteral:
case DeclaratorContext::LambdaExpr:
case DeclaratorContext::ConversionId:
+ case DeclaratorContext::ConversionIdInPostfixExpr:
case DeclaratorContext::TrailingReturn:
case DeclaratorContext::TrailingReturnVar:
case DeclaratorContext::TemplateArg:
diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p5.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p5.cpp
new file mode 100644
index 0000000000000..683d2091a3bfd
--- /dev/null
+++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p5.cpp
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -fsyntax-only -Wno-unused-result -verify=expected,pre-cxx20 %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -Wno-unused-result -verify=expected,since-cxx20 %s
+
+// expected-no-diagnostics
+
+namespace GH28181 {
+
+struct S {
+ using T = int;
+ operator T() { return 42; }
+};
+
+void foo() {
+ S{}.operator T();
+}
+
+}
+
+namespace GH94052 {
+
+namespace a {
+template <typename> class b {
+ public:
+ typedef int string_type;
+ operator string_type();
+};
+} // namespace a
+template <class> void c() {
+ (void)&a::b<char>::operator string_type;
+}
+
+}
+
+namespace CXXScopeSpec {
+
+struct S {
+ template <class U>
+ struct T {
+
+ };
+
+ template <class U>
+ operator T<U>() { return 42; }
+};
+
+template <class U>
+void foo() {
+ &S::operator T<U>();
+ S().operator T<U>();
+}
+
+}
diff --git a/clang/test/CXX/drs/cwg11xx.cpp b/clang/test/CXX/drs/cwg11xx.cpp
index 03612b6d87645..0867a26e544b5 100644
--- a/clang/test/CXX/drs/cwg11xx.cpp
+++ b/clang/test/CXX/drs/cwg11xx.cpp
@@ -80,9 +80,7 @@ struct B : A {
operator T();
} b;
void foo() {
- b.A::operator T(); // FIXME: qualified lookup should find T in A.
- // expected-error at -1 {{unknown type name 'T'}}
- // expected-note@#cwg1111-A-T {{'A::T' declared here}}
+ b.A::operator T();
}
} // namespace example4
diff --git a/clang/test/CXX/temp/temp.res/p4.cpp b/clang/test/CXX/temp/temp.res/p4.cpp
index 9dbdd235e925d..5cf2aef91c33d 100644
--- a/clang/test/CXX/temp/temp.res/p4.cpp
+++ b/clang/test/CXX/temp/temp.res/p4.cpp
@@ -158,7 +158,7 @@ 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'}}
+ void f() { this->operator T::type(); }
};
template<typename T>
diff --git a/clang/unittests/Tooling/TestVisitor.h b/clang/unittests/Tooling/TestVisitor.h
index fdf57a946a6e2..228214d33e834 100644
--- a/clang/unittests/Tooling/TestVisitor.h
+++ b/clang/unittests/Tooling/TestVisitor.h
@@ -253,8 +253,8 @@ class ExpectedLocationVisitorHelper {
///
/// Visits template instantiations and implicit code by default.
///
-/// For post-order traversal etc. use CTRPTestVisitor from
-/// CTRPTestVisitor.h instead.
+/// For post-order traversal etc. use CRTPTestVisitor from
+/// CRTPTestVisitor.h instead.
class TestVisitor : public DynamicRecursiveASTVisitor,
public detail::TestVisitorHelper {
public:
@@ -273,8 +273,8 @@ class TestVisitor : public DynamicRecursiveASTVisitor,
/// and allows simple creation of test visitors running matches on only a small
/// subset of the Visit* methods.
///
-/// For post-order traversal etc. use CTRPExpectedLocationVisitor from
-/// CTRPTestVisitor.h instead.
+/// For post-order traversal etc. use CRTPExpectedLocationVisitor from
+/// CRTPTestVisitor.h instead.
class ExpectedLocationVisitor : public TestVisitor,
public detail::ExpectedLocationVisitorHelper {
ASTContext *getASTContext() override { return Context; }
>From ae8be50f312bfef100d74ca572a01ea20fc82eb0 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 6 Jun 2025 00:43:08 +0800
Subject: [PATCH 2/2] Add the libcxx regression
---
.../basic.lookup/basic.lookup.unqual/p5.cpp | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p5.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p5.cpp
index 683d2091a3bfd..0277e6a8001a9 100644
--- a/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p5.cpp
+++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p5.cpp
@@ -50,3 +50,22 @@ void foo() {
}
}
+
+namespace Regression {
+
+namespace ns {
+template <class T>
+struct Bar {};
+}
+
+template <class T>
+struct S {
+ operator T();
+};
+
+template <class T>
+void foo(T t) {
+ t.operator ns::Bar<T>();
+}
+
+}
More information about the cfe-commits
mailing list