-
Notifications
You must be signed in to change notification settings - Fork 12.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reapply "[Clang][Sema] Use the correct lookup context when building overloaded 'operator->' in the current instantiation (#104458)" #109422
base: main
Are you sure you want to change the base?
Changes from all commits
d1b0cad
325bb02
11a63ff
9e106c2
7e57af6
8b80cff
7e06a1b
0d4beec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7966,18 +7966,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 | ||
|
|
@@ -7992,7 +7980,8 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, | |
| SmallVector<FunctionDecl*, 8> OperatorArrows; | ||
| CTypes.insert(Context.getCanonicalType(BaseType)); | ||
|
|
||
| while (BaseType->isRecordType()) { | ||
| while ( | ||
| isa<InjectedClassNameType, RecordType>(BaseType.getCanonicalType())) { | ||
| if (OperatorArrows.size() >= getLangOpts().ArrowDepth) { | ||
| Diag(OpLoc, diag::err_operator_arrow_depth_exceeded) | ||
| << StartingType << getLangOpts().ArrowDepth << Base->getSourceRange(); | ||
|
|
@@ -8002,15 +7991,26 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, | |
| return ExprError(); | ||
| } | ||
|
|
||
| bool IsDependent; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please initialize this to false anyway. |
||
| 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 | ||
| // 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(Base->isTypeDependent()); | ||
| BaseType = Context.DependentTy; | ||
| break; | ||
| } | ||
|
|
||
| if (Result.isInvalid()) { | ||
| if (NoArrowOperatorFound) { | ||
| if (FirstIteration) { | ||
|
|
@@ -8030,6 +8030,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, | |
| } | ||
| return ExprError(); | ||
| } | ||
|
|
||
| Base = Result.get(); | ||
| if (CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(Base)) | ||
| OperatorArrows.push_back(OpCall->getDirectCallee()); | ||
|
|
@@ -8067,7 +8068,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->isDependentType() || !BaseType->isRecordType()) { | ||
| ObjectType = ParsedType::make(BaseType); | ||
| MayBePseudoDestructor = true; | ||
| return Base; | ||
|
|
@@ -8078,8 +8079,7 @@ 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}); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 '.'! | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is unfortunate, it looks like we're having this problem diagnosing in both phases here, and can get worse with partial instantiations. Typically we'd want to solve this by replacing the expression with a RecoveryExpr or something to suppress it in the instantiated version, can you look into something like that? |
||
| 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) { | ||
|
|
@@ -605,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 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooof, I'm almost always really against 'out' parameters in C++. I'd hope we could come up with a better way for this.