Skip to content

Commit ce4aada

Browse files
authored
Reapply "[Clang] Implement resolution for CWG1835 (llvm#92957)" (llvm#98547)
Reapplies llvm#92957, fixing an instance where the `template` keyword was missing prior to a dependent name in `llvm/ADT/ArrayRef.h`. An _alias-declaration_ is used to work around a bug affecting GCC releases before 11.1 (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94799) which rejects the use of the `template` keyword prior to the _nested-name-specifier_ in the class member access.
1 parent ac304d5 commit ce4aada

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1118
-813
lines changed

clang/docs/ReleaseNotes.rst

+3
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@ Resolutions to C++ Defect Reports
310310
- Clang now considers ``noexcept(typeid(expr))`` more carefully, instead of always assuming that ``std::bad_typeid`` can be thrown.
311311
(`CWG2191: Incorrect result for noexcept(typeid(v)) <https://cplusplus.github.io/CWG/issues/2191.html>`_).
312312

313+
- Clang now correctly implements lookup for the terminal name of a member-qualified nested-name-specifier.
314+
(`CWG1835: Dependent member lookup before < <https://cplusplus.github.io/CWG/issues/1835.html>`_).
315+
313316
C Language Changes
314317
------------------
315318

clang/include/clang/AST/ExprCXX.h

+49-43
Original file line numberDiff line numberDiff line change
@@ -3676,9 +3676,9 @@ class CXXUnresolvedConstructExpr final
36763676
/// an implicit access if a qualifier is provided.
36773677
class CXXDependentScopeMemberExpr final
36783678
: public Expr,
3679-
private llvm::TrailingObjects<CXXDependentScopeMemberExpr,
3680-
ASTTemplateKWAndArgsInfo,
3681-
TemplateArgumentLoc, NamedDecl *> {
3679+
private llvm::TrailingObjects<
3680+
CXXDependentScopeMemberExpr, NestedNameSpecifierLoc, DeclAccessPair,
3681+
ASTTemplateKWAndArgsInfo, TemplateArgumentLoc> {
36823682
friend class ASTStmtReader;
36833683
friend class ASTStmtWriter;
36843684
friend TrailingObjects;
@@ -3691,17 +3691,15 @@ class CXXDependentScopeMemberExpr final
36913691
/// implicit accesses.
36923692
QualType BaseType;
36933693

3694-
/// The nested-name-specifier that precedes the member name, if any.
3695-
/// FIXME: This could be in principle store as a trailing object.
3696-
/// However the performance impact of doing so should be investigated first.
3697-
NestedNameSpecifierLoc QualifierLoc;
3698-
36993694
/// The member to which this member expression refers, which
37003695
/// can be name, overloaded operator, or destructor.
37013696
///
37023697
/// FIXME: could also be a template-id
37033698
DeclarationNameInfo MemberNameInfo;
37043699

3700+
/// The location of the '->' or '.' operator.
3701+
SourceLocation OperatorLoc;
3702+
37053703
// CXXDependentScopeMemberExpr is followed by several trailing objects,
37063704
// some of which optional. They are in order:
37073705
//
@@ -3721,8 +3719,16 @@ class CXXDependentScopeMemberExpr final
37213719
return CXXDependentScopeMemberExprBits.HasTemplateKWAndArgsInfo;
37223720
}
37233721

3724-
bool hasFirstQualifierFoundInScope() const {
3725-
return CXXDependentScopeMemberExprBits.HasFirstQualifierFoundInScope;
3722+
unsigned getNumUnqualifiedLookups() const {
3723+
return CXXDependentScopeMemberExprBits.NumUnqualifiedLookups;
3724+
}
3725+
3726+
unsigned numTrailingObjects(OverloadToken<NestedNameSpecifierLoc>) const {
3727+
return hasQualifier();
3728+
}
3729+
3730+
unsigned numTrailingObjects(OverloadToken<DeclAccessPair>) const {
3731+
return getNumUnqualifiedLookups();
37263732
}
37273733

37283734
unsigned numTrailingObjects(OverloadToken<ASTTemplateKWAndArgsInfo>) const {
@@ -3733,33 +3739,32 @@ class CXXDependentScopeMemberExpr final
37333739
return getNumTemplateArgs();
37343740
}
37353741

3736-
unsigned numTrailingObjects(OverloadToken<NamedDecl *>) const {
3737-
return hasFirstQualifierFoundInScope();
3738-
}
3739-
37403742
CXXDependentScopeMemberExpr(const ASTContext &Ctx, Expr *Base,
37413743
QualType BaseType, bool IsArrow,
37423744
SourceLocation OperatorLoc,
37433745
NestedNameSpecifierLoc QualifierLoc,
37443746
SourceLocation TemplateKWLoc,
3745-
NamedDecl *FirstQualifierFoundInScope,
3747+
ArrayRef<DeclAccessPair> UnqualifiedLookups,
37463748
DeclarationNameInfo MemberNameInfo,
37473749
const TemplateArgumentListInfo *TemplateArgs);
37483750

3749-
CXXDependentScopeMemberExpr(EmptyShell Empty, bool HasTemplateKWAndArgsInfo,
3750-
bool HasFirstQualifierFoundInScope);
3751+
CXXDependentScopeMemberExpr(EmptyShell Empty, bool HasQualifier,
3752+
unsigned NumUnqualifiedLookups,
3753+
bool HasTemplateKWAndArgsInfo);
37513754

37523755
public:
37533756
static CXXDependentScopeMemberExpr *
37543757
Create(const ASTContext &Ctx, Expr *Base, QualType BaseType, bool IsArrow,
37553758
SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc,
3756-
SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope,
3759+
SourceLocation TemplateKWLoc,
3760+
ArrayRef<DeclAccessPair> UnqualifiedLookups,
37573761
DeclarationNameInfo MemberNameInfo,
37583762
const TemplateArgumentListInfo *TemplateArgs);
37593763

37603764
static CXXDependentScopeMemberExpr *
3761-
CreateEmpty(const ASTContext &Ctx, bool HasTemplateKWAndArgsInfo,
3762-
unsigned NumTemplateArgs, bool HasFirstQualifierFoundInScope);
3765+
CreateEmpty(const ASTContext &Ctx, bool HasQualifier,
3766+
unsigned NumUnqualifiedLookups, bool HasTemplateKWAndArgsInfo,
3767+
unsigned NumTemplateArgs);
37633768

37643769
/// True if this is an implicit access, i.e. one in which the
37653770
/// member being accessed was not written in the source. The source
@@ -3784,34 +3789,35 @@ class CXXDependentScopeMemberExpr final
37843789
bool isArrow() const { return CXXDependentScopeMemberExprBits.IsArrow; }
37853790

37863791
/// Retrieve the location of the '->' or '.' operator.
3787-
SourceLocation getOperatorLoc() const {
3788-
return CXXDependentScopeMemberExprBits.OperatorLoc;
3792+
SourceLocation getOperatorLoc() const { return OperatorLoc; }
3793+
3794+
/// Determines whether this member expression had a nested-name-specifier
3795+
/// prior to the name of the member, e.g., x->Base::foo.
3796+
bool hasQualifier() const {
3797+
return CXXDependentScopeMemberExprBits.HasQualifier;
37893798
}
37903799

3791-
/// Retrieve the nested-name-specifier that qualifies the member name.
3792-
NestedNameSpecifier *getQualifier() const {
3793-
return QualifierLoc.getNestedNameSpecifier();
3800+
/// If the member name was qualified, retrieves the nested-name-specifier
3801+
/// that precedes the member name, with source-location information.
3802+
NestedNameSpecifierLoc getQualifierLoc() const {
3803+
if (!hasQualifier())
3804+
return NestedNameSpecifierLoc();
3805+
return *getTrailingObjects<NestedNameSpecifierLoc>();
37943806
}
37953807

3796-
/// Retrieve the nested-name-specifier that qualifies the member
3797-
/// name, with source location information.
3798-
NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; }
3808+
/// If the member name was qualified, retrieves the
3809+
/// nested-name-specifier that precedes the member name. Otherwise, returns
3810+
/// NULL.
3811+
NestedNameSpecifier *getQualifier() const {
3812+
return getQualifierLoc().getNestedNameSpecifier();
3813+
}
37993814

3800-
/// Retrieve the first part of the nested-name-specifier that was
3801-
/// found in the scope of the member access expression when the member access
3802-
/// was initially parsed.
3803-
///
3804-
/// This function only returns a useful result when member access expression
3805-
/// uses a qualified member name, e.g., "x.Base::f". Here, the declaration
3806-
/// returned by this function describes what was found by unqualified name
3807-
/// lookup for the identifier "Base" within the scope of the member access
3808-
/// expression itself. At template instantiation time, this information is
3809-
/// combined with the results of name lookup into the type of the object
3810-
/// expression itself (the class type of x).
3811-
NamedDecl *getFirstQualifierFoundInScope() const {
3812-
if (!hasFirstQualifierFoundInScope())
3813-
return nullptr;
3814-
return *getTrailingObjects<NamedDecl *>();
3815+
/// Retrieve the declarations found by unqualified lookup for the first
3816+
/// component name of the nested-name-specifier, if any.
3817+
ArrayRef<DeclAccessPair> unqualified_lookups() const {
3818+
if (!getNumUnqualifiedLookups())
3819+
return std::nullopt;
3820+
return {getTrailingObjects<DeclAccessPair>(), getNumUnqualifiedLookups()};
38153821
}
38163822

38173823
/// Retrieve the name of the member that this expression refers to.

clang/include/clang/AST/Stmt.h

+8-7
Original file line numberDiff line numberDiff line change
@@ -1020,18 +1020,19 @@ class alignas(void *) Stmt {
10201020
LLVM_PREFERRED_TYPE(bool)
10211021
unsigned IsArrow : 1;
10221022

1023+
/// True if this member expression used a nested-name-specifier to
1024+
/// refer to the member, e.g., "x->Base::f".
1025+
LLVM_PREFERRED_TYPE(bool)
1026+
unsigned HasQualifier : 1;
1027+
10231028
/// Whether this member expression has info for explicit template
10241029
/// keyword and arguments.
10251030
LLVM_PREFERRED_TYPE(bool)
10261031
unsigned HasTemplateKWAndArgsInfo : 1;
10271032

1028-
/// See getFirstQualifierFoundInScope() and the comment listing
1029-
/// the trailing objects.
1030-
LLVM_PREFERRED_TYPE(bool)
1031-
unsigned HasFirstQualifierFoundInScope : 1;
1032-
1033-
/// The location of the '->' or '.' operator.
1034-
SourceLocation OperatorLoc;
1033+
/// Number of declarations found by unqualified lookup for the
1034+
/// first component name of the nested-name-specifier.
1035+
unsigned NumUnqualifiedLookups;
10351036
};
10361037

10371038
class OverloadExprBitfields {

clang/include/clang/AST/UnresolvedSet.h

+4
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ class UnresolvedSetImpl {
9797
decls().push_back(DeclAccessPair::make(D, AS));
9898
}
9999

100+
void addAllDecls(ArrayRef<DeclAccessPair> Other) {
101+
append(iterator(Other.begin()), iterator(Other.end()));
102+
}
103+
100104
/// Replaces the given declaration with the new one, once.
101105
///
102106
/// \return true if the set changed

clang/include/clang/Basic/DiagnosticParseKinds.td

+1-3
Original file line numberDiff line numberDiff line change
@@ -895,9 +895,7 @@ def missing_template_arg_list_after_template_kw : Extension<
895895
"keyword">, InGroup<DiagGroup<"missing-template-arg-list-after-template-kw">>,
896896
DefaultError;
897897

898-
def err_missing_dependent_template_keyword : Error<
899-
"use 'template' keyword to treat '%0' as a dependent template name">;
900-
def warn_missing_dependent_template_keyword : ExtWarn<
898+
def ext_missing_dependent_template_keyword : ExtWarn<
901899
"use 'template' keyword to treat '%0' as a dependent template name">;
902900

903901
def ext_extern_template : Extension<

clang/include/clang/Parse/Parser.h

+5-9
Original file line numberDiff line numberDiff line change
@@ -3368,15 +3368,11 @@ class Parser : public CodeCompletionHandler {
33683368
BaseResult ParseBaseSpecifier(Decl *ClassDecl);
33693369
AccessSpecifier getAccessSpecifierIfPresent() const;
33703370

3371-
bool ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
3372-
ParsedType ObjectType,
3373-
bool ObjectHadErrors,
3374-
SourceLocation TemplateKWLoc,
3375-
IdentifierInfo *Name,
3376-
SourceLocation NameLoc,
3377-
bool EnteringContext,
3378-
UnqualifiedId &Id,
3379-
bool AssumeTemplateId);
3371+
bool ParseUnqualifiedIdTemplateId(
3372+
CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
3373+
SourceLocation TemplateKWLoc, SourceLocation TildeLoc,
3374+
IdentifierInfo *Name, SourceLocation NameLoc, bool EnteringContext,
3375+
UnqualifiedId &Id, bool AssumeTemplateId);
33803376
bool ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
33813377
ParsedType ObjectType,
33823378
UnqualifiedId &Result);

clang/include/clang/Sema/DeclSpec.h

+8
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class CXXScopeSpec {
7575
SourceRange Range;
7676
NestedNameSpecifierLocBuilder Builder;
7777
ArrayRef<TemplateParameterList *> TemplateParamLists;
78+
ArrayRef<DeclAccessPair> UnqualifiedLookups;
7879

7980
public:
8081
SourceRange getRange() const { return Range; }
@@ -91,6 +92,13 @@ class CXXScopeSpec {
9192
return TemplateParamLists;
9293
}
9394

95+
void setUnqualifiedLookups(ArrayRef<DeclAccessPair> Found) {
96+
UnqualifiedLookups = Found;
97+
}
98+
ArrayRef<DeclAccessPair> getUnqualifiedLookups() const {
99+
return UnqualifiedLookups;
100+
}
101+
94102
/// Retrieve the representation of the nested-name-specifier.
95103
NestedNameSpecifier *getScopeRep() const {
96104
return Builder.getRepresentation();

clang/include/clang/Sema/Lookup.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -483,11 +483,15 @@ class LookupResult {
483483
ResultKind = Found;
484484
}
485485

486+
void addAllDecls(ArrayRef<DeclAccessPair> Other) {
487+
Decls.addAllDecls(Other);
488+
ResultKind = Found;
489+
}
490+
486491
/// Add all the declarations from another set of lookup
487492
/// results.
488493
void addAllDecls(const LookupResult &Other) {
489-
Decls.append(Other.Decls.begin(), Other.Decls.end());
490-
ResultKind = Found;
494+
addAllDecls(Other.Decls.pairs());
491495
}
492496

493497
/// Determine whether no result was found because we could not

clang/include/clang/Sema/Sema.h

+22-32
Original file line numberDiff line numberDiff line change
@@ -2802,7 +2802,8 @@ class Sema final : public SemaBase {
28022802
/// (e.g., Base::), perform name lookup for that identifier as a
28032803
/// nested-name-specifier within the given scope, and return the result of
28042804
/// that name lookup.
2805-
NamedDecl *FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS);
2805+
bool LookupFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS,
2806+
UnresolvedSetImpl &R);
28062807

28072808
/// Keeps information about an identifier in a nested-name-spec.
28082809
///
@@ -2842,9 +2843,6 @@ class Sema final : public SemaBase {
28422843
/// \param EnteringContext If true, enter the context specified by the
28432844
/// nested-name-specifier.
28442845
/// \param SS Optional nested name specifier preceding the identifier.
2845-
/// \param ScopeLookupResult Provides the result of name lookup within the
2846-
/// scope of the nested-name-specifier that was computed at template
2847-
/// definition time.
28482846
/// \param ErrorRecoveryLookup Specifies if the method is called to improve
28492847
/// error recovery and what kind of recovery is performed.
28502848
/// \param IsCorrectedToColon If not null, suggestion of replace '::' -> ':'
@@ -2853,11 +2851,6 @@ class Sema final : public SemaBase {
28532851
/// not '::'.
28542852
/// \param OnlyNamespace If true, only considers namespaces in lookup.
28552853
///
2856-
/// This routine differs only slightly from ActOnCXXNestedNameSpecifier, in
2857-
/// that it contains an extra parameter \p ScopeLookupResult, which provides
2858-
/// the result of name lookup within the scope of the nested-name-specifier
2859-
/// that was computed at template definition time.
2860-
///
28612854
/// If ErrorRecoveryLookup is true, then this call is used to improve error
28622855
/// recovery. This means that it should not emit diagnostics, it should
28632856
/// just return true on failure. It also means it should only return a valid
@@ -2866,7 +2859,6 @@ class Sema final : public SemaBase {
28662859
/// specifier.
28672860
bool BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
28682861
bool EnteringContext, CXXScopeSpec &SS,
2869-
NamedDecl *ScopeLookupResult,
28702862
bool ErrorRecoveryLookup,
28712863
bool *IsCorrectedToColon = nullptr,
28722864
bool OnlyNamespace = false);
@@ -8566,11 +8558,12 @@ class Sema final : public SemaBase {
85668558
const TemplateArgumentListInfo *TemplateArgs,
85678559
bool IsDefiniteInstance, const Scope *S);
85688560

8569-
ExprResult ActOnDependentMemberExpr(
8570-
Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OpLoc,
8571-
const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
8572-
NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo,
8573-
const TemplateArgumentListInfo *TemplateArgs);
8561+
ExprResult
8562+
ActOnDependentMemberExpr(Expr *Base, QualType BaseType, bool IsArrow,
8563+
SourceLocation OpLoc, const CXXScopeSpec &SS,
8564+
SourceLocation TemplateKWLoc,
8565+
const DeclarationNameInfo &NameInfo,
8566+
const TemplateArgumentListInfo *TemplateArgs);
85748567

85758568
/// The main callback when the parser finds something like
85768569
/// expression . [nested-name-specifier] identifier
@@ -8626,15 +8619,14 @@ class Sema final : public SemaBase {
86268619
ExprResult BuildMemberReferenceExpr(
86278620
Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow,
86288621
CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
8629-
NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo,
8622+
const DeclarationNameInfo &NameInfo,
86308623
const TemplateArgumentListInfo *TemplateArgs, const Scope *S,
86318624
ActOnMemberAccessExtraArgs *ExtraArgs = nullptr);
86328625

86338626
ExprResult
86348627
BuildMemberReferenceExpr(Expr *Base, QualType BaseType, SourceLocation OpLoc,
86358628
bool IsArrow, const CXXScopeSpec &SS,
8636-
SourceLocation TemplateKWLoc,
8637-
NamedDecl *FirstQualifierInScope, LookupResult &R,
8629+
SourceLocation TemplateKWLoc, LookupResult &R,
86388630
const TemplateArgumentListInfo *TemplateArgs,
86398631
const Scope *S, bool SuppressQualifierCheck = false,
86408632
ActOnMemberAccessExtraArgs *ExtraArgs = nullptr);
@@ -11122,15 +11114,14 @@ class Sema final : public SemaBase {
1112211114
QualType ObjectType, bool EnteringContext,
1112311115
RequiredTemplateKind RequiredTemplate = SourceLocation(),
1112411116
AssumedTemplateKind *ATK = nullptr,
11125-
bool AllowTypoCorrection = true);
11117+
bool AllowTypoCorrection = true, bool MayBeNNS = false);
1112611118

11127-
TemplateNameKind isTemplateName(Scope *S, CXXScopeSpec &SS,
11128-
bool hasTemplateKeyword,
11129-
const UnqualifiedId &Name,
11130-
ParsedType ObjectType, bool EnteringContext,
11131-
TemplateTy &Template,
11132-
bool &MemberOfUnknownSpecialization,
11133-
bool Disambiguation = false);
11119+
TemplateNameKind
11120+
isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword,
11121+
const UnqualifiedId &Name, ParsedType ObjectType,
11122+
bool EnteringContext, TemplateTy &Template,
11123+
bool &MemberOfUnknownSpecialization,
11124+
bool Disambiguation = false, bool MayBeNNS = false);
1113411125

1113511126
/// Try to resolve an undeclared template name as a type template.
1113611127
///
@@ -11459,12 +11450,11 @@ class Sema final : public SemaBase {
1145911450
/// For example, given "x.MetaFun::template apply", the scope specifier
1146011451
/// \p SS will be "MetaFun::", \p TemplateKWLoc contains the location
1146111452
/// of the "template" keyword, and "apply" is the \p Name.
11462-
TemplateNameKind ActOnTemplateName(Scope *S, CXXScopeSpec &SS,
11463-
SourceLocation TemplateKWLoc,
11464-
const UnqualifiedId &Name,
11465-
ParsedType ObjectType,
11466-
bool EnteringContext, TemplateTy &Template,
11467-
bool AllowInjectedClassName = false);
11453+
TemplateNameKind
11454+
ActOnTemplateName(Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
11455+
const UnqualifiedId &Name, ParsedType ObjectType,
11456+
bool EnteringContext, TemplateTy &Template,
11457+
bool AllowInjectedClassName = false, bool MayBeNNS = false);
1146811458

1146911459
DeclResult ActOnClassTemplateSpecialization(
1147011460
Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,

0 commit comments

Comments
 (0)