https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/109422
>From 6edb1455c03c8d6453ff813dd1c797dbaefc6398 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Thu, 15 Aug 2024 11:27:34 -0400 Subject: [PATCH 1/8] Reapply "[Clang][Sema] Use the correct lookup context when building overloaded 'operator->' in the current instantiation (#104458)" --- clang/include/clang/Sema/Sema.h | 3 +-- clang/lib/Sema/SemaExprCXX.cpp | 25 ++++++++----------- clang/lib/Sema/SemaOverload.cpp | 14 +++++++---- clang/lib/Sema/TreeTransform.h | 4 +-- .../temp.res/temp.dep/temp.dep.type/p4.cpp | 24 ++++++++++-------- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index a41f16f6dc8c9b..a29b8cf0765a01 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10608,8 +10608,7 @@ class Sema final : public SemaBase { /// BuildOverloadedArrowExpr - Build a call to an overloaded @c operator-> /// (if one exists), where @c Base is an expression of class type and /// @c Member is the name of the member we're trying to find. - ExprResult BuildOverloadedArrowExpr(Scope *S, Expr *Base, - SourceLocation OpLoc, + ExprResult BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc, bool *NoArrowOperatorFound = nullptr); ExprResult BuildCXXMemberCallExpr(Expr *Exp, NamedDecl *FoundDecl, diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 1e39d69e8b230f..c0aad3aa9a813b 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -7999,18 +7999,6 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, QualType BaseType = Base->getType(); MayBePseudoDestructor = false; - if (BaseType->isDependentType()) { - // If we have a pointer to a dependent type and are using the -> operator, - // the object type is the type that the pointer points to. We might still - // have enough information about that type to do something useful. - if (OpKind == tok::arrow) - if (const PointerType *Ptr = BaseType->getAs<PointerType>()) - BaseType = Ptr->getPointeeType(); - - ObjectType = ParsedType::make(BaseType); - MayBePseudoDestructor = true; - return Base; - } // C++ [over.match.oper]p8: // [...] When operator->returns, the operator-> is applied to the value @@ -8025,7 +8013,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, SmallVector<FunctionDecl*, 8> OperatorArrows; CTypes.insert(Context.getCanonicalType(BaseType)); - while (BaseType->isRecordType()) { + while (BaseType->getAsRecordDecl()) { if (OperatorArrows.size() >= getLangOpts().ArrowDepth) { Diag(OpLoc, diag::err_operator_arrow_depth_exceeded) << StartingType << getLangOpts().ArrowDepth << Base->getSourceRange(); @@ -8036,7 +8024,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, } Result = BuildOverloadedArrowExpr( - S, Base, OpLoc, + Base, OpLoc, // When in a template specialization and on the first loop iteration, // potentially give the default diagnostic (with the fixit in a // separate note) instead of having the error reported back to here @@ -8062,7 +8050,14 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, } } return ExprError(); + } else if (Result.isUnset()) { + // BuildOverloadedArrowExpr returns an empty expression to indicate + // that we need to build a dependent overloaded arrow expression. + assert(BaseType->isDependentType()); + BaseType = Context.DependentTy; + break; } + Base = Result.get(); if (CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(Base)) OperatorArrows.push_back(OpCall->getDirectCallee()); @@ -8100,7 +8095,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, // it's legal for the type to be incomplete if this is a pseudo-destructor // call. We'll do more incomplete-type checks later in the lookup process, // so just skip this check for ObjC types. - if (!BaseType->isRecordType()) { + if (!BaseType->getAsRecordDecl()) { ObjectType = ParsedType::make(BaseType); MayBePseudoDestructor = true; return Base; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 7e8811b5274efb..46dc823ed92262 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -15962,10 +15962,9 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj, return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), Method); } -ExprResult -Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc, - bool *NoArrowOperatorFound) { - assert(Base->getType()->isRecordType() && +ExprResult Sema::BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc, + bool *NoArrowOperatorFound) { + assert(Base->getType()->getAsRecordDecl() && "left-hand side must have class type"); if (checkPlaceholderForOverload(*this, Base)) @@ -15988,7 +15987,12 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc, return ExprError(); LookupResult R(*this, OpName, OpLoc, LookupOrdinaryName); - LookupQualifiedName(R, Base->getType()->castAs<RecordType>()->getDecl()); + LookupParsedName(R, /*S=*/nullptr, /*SS=*/nullptr, Base->getType()); + + if (R.wasNotFoundInCurrentInstantiation() || + (Base->isTypeDependent() && !R.empty())) + return ExprEmpty(); + R.suppressAccessDiagnostics(); for (LookupResult::iterator Oper = R.begin(), OperEnd = R.end(); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 7dc88a1ae23b98..d529a40eedceb6 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -17282,10 +17282,10 @@ ExprResult TreeTransform<Derived>::RebuildCXXOperatorCallExpr( } else if (Op == OO_Arrow) { // It is possible that the type refers to a RecoveryExpr created earlier // in the tree transformation. - if (First->getType()->isDependentType()) + if (First->containsErrors()) return ExprError(); // -> is never a builtin operation. - return SemaRef.BuildOverloadedArrowExpr(nullptr, First, OpLoc); + return SemaRef.BuildOverloadedArrowExpr(First, OpLoc); } else if (Second == nullptr || isPostIncDec) { if (!First->getType()->isOverloadableType() || (Op == OO_Amp && getSema().isQualifiedMemberAccess(First))) { diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp index f32f49ef4539a5..89c22a0bc137d9 100644 --- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp +++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp @@ -484,16 +484,19 @@ namespace N4 { template<typename T> struct A { void not_instantiated(A a, A<T> b, T c) { - a->x; - b->x; + a->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}} + b->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}} c->x; } void instantiated(A a, A<T> b, T c) { - a->x; // expected-error {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}} - // expected-error@-1 {{no member named 'x' in 'N4::A<int>'}} - b->x; // expected-error {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}} - // expected-error@-1 {{no member named 'x' in 'N4::A<int>'}} + // FIXME: We should only emit a single diagnostic suggesting to use '.'! + a->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}} + // expected-error@-1 {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}} + // expected-error@-2 {{no member named 'x' in 'N4::A<int>'}} + b->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}} + // expected-error@-1 {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}} + // expected-error@-2 {{no member named 'x' in 'N4::A<int>'}} c->x; // expected-error {{member reference type 'int' is not a pointer}} } }; @@ -540,11 +543,10 @@ namespace N4 { a->T::f(); a->T::g(); - // FIXME: 'U' should be a dependent name, and its lookup context should be 'a.operator->()'! - a->U::x; // expected-error {{use of undeclared identifier 'U'}} - a->U::y; // expected-error {{use of undeclared identifier 'U'}} - a->U::f(); // expected-error {{use of undeclared identifier 'U'}} - a->U::g(); // expected-error {{use of undeclared identifier 'U'}} + a->U::x; + a->U::y; + a->U::f(); + a->U::g(); } void instantiated(D a) { >From a4ad2b900f3669db43fc509111cf279f9c5971b8 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Thu, 15 Aug 2024 13:24:27 -0400 Subject: [PATCH 2/8] [FOLD] fix destructor lookup when the object expression is dependent --- clang/lib/Sema/SemaExprCXX.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index c0aad3aa9a813b..605d98c419f988 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8113,6 +8113,10 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, return CreateRecoveryExpr(Base->getBeginLoc(), Base->getEndLoc(), {Base}); } + // We can't implicitly declare the destructor for a templated class. + if (BaseType->isDependentType()) + MayBePseudoDestructor = true; + // C++ [basic.lookup.classref]p2: // If the id-expression in a class member access (5.2.5) is an // unqualified-id, and the type of the object expression is of a class >From 0380314895076a8faae98518e92fd2152ddf294e Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Tue, 3 Sep 2024 12:17:39 -0400 Subject: [PATCH 3/8] [FOLD] use IsDependent output parameter --- clang/include/clang/Sema/Sema.h | 3 ++- clang/lib/Sema/SemaExprCXX.cpp | 19 ++++++++++++------- clang/lib/Sema/SemaOverload.cpp | 16 +++++++++++++--- clang/lib/Sema/TreeTransform.h | 6 +++++- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index a29b8cf0765a01..1601acd1b06a13 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10609,7 +10609,8 @@ class Sema final : public SemaBase { /// (if one exists), where @c Base is an expression of class type and /// @c Member is the name of the member we're trying to find. ExprResult BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc, - bool *NoArrowOperatorFound = nullptr); + bool *NoArrowOperatorFound, + bool &IsDependent); ExprResult BuildCXXMemberCallExpr(Expr *Exp, NamedDecl *FoundDecl, CXXConversionDecl *Method, diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 605d98c419f988..b8e66aba51f39d 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8023,6 +8023,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, return ExprError(); } + bool IsDependent; Result = BuildOverloadedArrowExpr( Base, OpLoc, // When in a template specialization and on the first loop iteration, @@ -8031,7 +8032,17 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, // and giving a diagnostic with a fixit attached to the error itself. (FirstIteration && CurFD && CurFD->isFunctionTemplateSpecialization()) ? nullptr - : &NoArrowOperatorFound); + : &NoArrowOperatorFound, + IsDependent); + + if (IsDependent) { + // BuildOverloadedArrowExpr sets IsDependent to indicate that we need + // to build a dependent overloaded arrow expression. + assert(BaseType->isDependentType()); + BaseType = Context.DependentTy; + break; + } + if (Result.isInvalid()) { if (NoArrowOperatorFound) { if (FirstIteration) { @@ -8050,12 +8061,6 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, } } return ExprError(); - } else if (Result.isUnset()) { - // BuildOverloadedArrowExpr returns an empty expression to indicate - // that we need to build a dependent overloaded arrow expression. - assert(BaseType->isDependentType()); - BaseType = Context.DependentTy; - break; } Base = Result.get(); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 46dc823ed92262..69f41ec4585743 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -15963,10 +15963,13 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj, } ExprResult Sema::BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc, - bool *NoArrowOperatorFound) { + bool *NoArrowOperatorFound, + bool &IsDependent) { assert(Base->getType()->getAsRecordDecl() && "left-hand side must have class type"); + IsDependent = false; + if (checkPlaceholderForOverload(*this, Base)) return ExprError(); @@ -15989,9 +15992,16 @@ ExprResult Sema::BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc, LookupResult R(*this, OpName, OpLoc, LookupOrdinaryName); LookupParsedName(R, /*S=*/nullptr, /*SS=*/nullptr, Base->getType()); + // If the expression is dependent and we either: + // - Found a member of the current instantiation named 'operator->', or + // - Found nothing, and the lookup context has no dependent base classes + // + // Then we should build a dependent class member access expression. if (R.wasNotFoundInCurrentInstantiation() || - (Base->isTypeDependent() && !R.empty())) - return ExprEmpty(); + (Base->isTypeDependent() && !R.empty())) { + IsDependent = true; + return Base; + } R.suppressAccessDiagnostics(); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index d529a40eedceb6..5d4aece9524e94 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -17284,8 +17284,12 @@ ExprResult TreeTransform<Derived>::RebuildCXXOperatorCallExpr( // in the tree transformation. if (First->containsErrors()) return ExprError(); + bool IsDependent; // -> is never a builtin operation. - return SemaRef.BuildOverloadedArrowExpr(First, OpLoc); + ExprResult Result = SemaRef.BuildOverloadedArrowExpr( + First, OpLoc, /*NoArrowOperatorFound=*/nullptr, IsDependent); + assert(!IsDependent); + return Result; } else if (Second == nullptr || isPostIncDec) { if (!First->getType()->isOverloadableType() || (Op == OO_Amp && getSema().isQualifiedMemberAccess(First))) { >From 772a6c0c77446cb0e2e19e8049df2dafd05be793 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Wed, 4 Sep 2024 09:43:22 -0400 Subject: [PATCH 4/8] [FOLD] don't use getAsRecordDecl --- clang/lib/Sema/SemaExprCXX.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index b8e66aba51f39d..f7721fb0b41f27 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8013,7 +8013,8 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, SmallVector<FunctionDecl*, 8> OperatorArrows; CTypes.insert(Context.getCanonicalType(BaseType)); - while (BaseType->getAsRecordDecl()) { + while ( + isa<InjectedClassNameType, RecordType>(BaseType.getCanonicalType())) { if (OperatorArrows.size() >= getLangOpts().ArrowDepth) { Diag(OpLoc, diag::err_operator_arrow_depth_exceeded) << StartingType << getLangOpts().ArrowDepth << Base->getSourceRange(); @@ -8100,7 +8101,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, // it's legal for the type to be incomplete if this is a pseudo-destructor // call. We'll do more incomplete-type checks later in the lookup process, // so just skip this check for ObjC types. - if (!BaseType->getAsRecordDecl()) { + if (!isa<InjectedClassNameType, RecordType>(BaseType.getCanonicalType())) { ObjectType = ParsedType::make(BaseType); MayBePseudoDestructor = true; return Base; >From 51a3384b5a0e8c152666b8635200b05332202b4d Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Mon, 9 Sep 2024 11:35:18 -0400 Subject: [PATCH 5/8] [FOLD] fix capitalization --- clang/lib/Sema/SemaOverload.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 69f41ec4585743..6f43e747c41c8d 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -15993,10 +15993,10 @@ ExprResult Sema::BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc, LookupParsedName(R, /*S=*/nullptr, /*SS=*/nullptr, Base->getType()); // If the expression is dependent and we either: - // - Found a member of the current instantiation named 'operator->', or - // - Found nothing, and the lookup context has no dependent base classes + // - found a member of the current instantiation named 'operator->', or + // - found nothing, and the lookup context has no dependent base classes // - // Then we should build a dependent class member access expression. + // then we should build a dependent class member access expression. if (R.wasNotFoundInCurrentInstantiation() || (Base->isTypeDependent() && !R.empty())) { IsDependent = true; >From e3326ab703ad159a4a69f453891206d716de540e Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Thu, 12 Sep 2024 09:42:05 -0400 Subject: [PATCH 6/8] [FOLD] fix dependent overloaded arrow --- clang/lib/Sema/SemaExprCXX.cpp | 2 +- clang/lib/Sema/SemaExprMember.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index f7721fb0b41f27..994202958816c4 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8039,7 +8039,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, if (IsDependent) { // BuildOverloadedArrowExpr sets IsDependent to indicate that we need // to build a dependent overloaded arrow expression. - assert(BaseType->isDependentType()); + assert(Base->isTypeDependent()); BaseType = Context.DependentTy; break; } diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index d130e8b86bc56d..17bd30e4d4d5f7 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -581,7 +581,9 @@ Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType, } } - assert(BaseType->isDependentType() || NameInfo.getName().isDependentName() || + assert(BaseType->isDependentType() || + (BaseExpr && BaseExpr->isTypeDependent()) || + NameInfo.getName().isDependentName() || isDependentScopeSpecifier(SS) || (TemplateArgs && llvm::any_of(TemplateArgs->arguments(), [](const TemplateArgumentLoc &Arg) { @@ -1357,7 +1359,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, BaseType = Ptr->getPointeeType(); else if (BaseType->isFunctionType()) goto fail; - else if (BaseType->isDependentType()) + else if (BaseExpr.get()->isTypeDependent()) BaseType = S.Context.DependentTy; else if (BaseType->isRecordType()) { // Recover from arrow accesses to records, e.g.: >From 764a9a873a8076fc1329c06b290a37eef52f9eab Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Fri, 20 Sep 2024 09:25:23 -0400 Subject: [PATCH 7/8] [FOLD] cleanup --- clang/lib/Sema/SemaExprCXX.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 994202958816c4..ecbd3e410bc8f6 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8101,7 +8101,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, // it's legal for the type to be incomplete if this is a pseudo-destructor // call. We'll do more incomplete-type checks later in the lookup process, // so just skip this check for ObjC types. - if (!isa<InjectedClassNameType, RecordType>(BaseType.getCanonicalType())) { + if (BaseType->isDependentType() || !BaseType->isRecordType()) { ObjectType = ParsedType::make(BaseType); MayBePseudoDestructor = true; return Base; @@ -8112,17 +8112,12 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, // Unlike the object expression in other contexts, *this is not required to // be of complete type for purposes of class member access (5.2.5) outside // the member function body. - if (!BaseType->isDependentType() && - !isThisOutsideMemberFunctionBody(BaseType) && + if (!isThisOutsideMemberFunctionBody(BaseType) && RequireCompleteType(OpLoc, BaseType, diag::err_incomplete_member_access)) { return CreateRecoveryExpr(Base->getBeginLoc(), Base->getEndLoc(), {Base}); } - // We can't implicitly declare the destructor for a templated class. - if (BaseType->isDependentType()) - MayBePseudoDestructor = true; - // C++ [basic.lookup.classref]p2: // If the id-expression in a class member access (5.2.5) is an // unqualified-id, and the type of the object expression is of a class >From 30297f7aefe0dba6a7b8718d9819ca07f5121190 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Fri, 20 Sep 2024 10:03:19 -0400 Subject: [PATCH 8/8] [FOLD] add test --- .../temp.res/temp.dep/temp.dep.type/p4.cpp | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp index 89c22a0bc137d9..03eda1f13feed7 100644 --- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp +++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp @@ -607,3 +607,24 @@ namespace N5 { template void g(int); // expected-note {{in instantiation of}} } // namespace N5 + +namespace N6 { + struct A { + int x; + }; + + struct B { + A* operator->(); + }; + + struct C { + B y; + }; + + template<typename T> + struct D : C { + void f() { + y->x; + } + }; +} // namespace N6 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits