[clang] 9e1f1cf - [Clang][Sema] Handle class member access expressions with valid nested-name-specifiers that become invalid after lookup (#98167)

via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 9 13:40:56 PDT 2024


Author: Krystian Stasiowski
Date: 2024-07-09T16:40:53-04:00
New Revision: 9e1f1cfa59c4513798de5c326d77e1e5912b1634

URL: https://github.com/llvm/llvm-project/commit/9e1f1cfa59c4513798de5c326d77e1e5912b1634
DIFF: https://github.com/llvm/llvm-project/commit/9e1f1cfa59c4513798de5c326d77e1e5912b1634.diff

LOG: [Clang][Sema] Handle class member access expressions with valid nested-name-specifiers that become invalid after lookup (#98167)

The following code causes an assert in `SemaExprMember.cpp` on line 981
to fail:
```
struct A { };
struct B;

void f(A *x) {
  x->B::y; // crash here
}
```
This happens because we only return early from
`BuildMemberReferenceExpr` when the `CXXScopeSpecifier` is invalid
_before_ the lookup is performed. Since the lookup may invalidate the
`CXXScopeSpecifier` (e.g. if the _nested-name-specifier_ is incomplete),
this results in the second `BuildMemberReferenceExpr` overload being
called with an invalid `CXXScopeSpecifier`, which causes the assert to
fail. This patch moves the early return for invalid `CXXScopeSpecifiers`
to occur _after_ lookup is performed. This fixes #92972.

I also removed the `if (SS.isSet() && SS.isInvalid())` check in
`ActOnMemberAccessExpr` because the condition can never be true (`isSet`
returns `getScopeRep() != nullptr` and `isInvalid` returns
`Range.isValid() && getScopeRep() == nullptr`).

Added: 
    clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p2.cpp

Modified: 
    clang/lib/Sema/SemaExprMember.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index b7ea24790d361..2070f3b7bb3a2 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -789,9 +789,6 @@ ExprResult Sema::BuildMemberReferenceExpr(
     ActOnMemberAccessExtraArgs *ExtraArgs) {
   LookupResult R(*this, NameInfo, LookupMemberName);
 
-  if (SS.isInvalid())
-    return ExprError();
-
   // Implicit member accesses.
   if (!Base) {
     TypoExpr *TE = nullptr;
@@ -826,6 +823,11 @@ ExprResult Sema::BuildMemberReferenceExpr(
     BaseType = Base->getType();
   }
 
+  // BuildMemberReferenceExpr expects the nested-name-specifier, if any, to be
+  // valid.
+  if (SS.isInvalid())
+    return ExprError();
+
   return BuildMemberReferenceExpr(Base, BaseType,
                                   OpLoc, IsArrow, SS, TemplateKWLoc,
                                   FirstQualifierInScope, R, TemplateArgs, S,
@@ -1745,14 +1747,9 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
 
 ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base,
                                        SourceLocation OpLoc,
-                                       tok::TokenKind OpKind,
-                                       CXXScopeSpec &SS,
+                                       tok::TokenKind OpKind, CXXScopeSpec &SS,
                                        SourceLocation TemplateKWLoc,
-                                       UnqualifiedId &Id,
-                                       Decl *ObjCImpDecl) {
-  if (SS.isSet() && SS.isInvalid())
-    return ExprError();
-
+                                       UnqualifiedId &Id, Decl *ObjCImpDecl) {
   // Warn about the explicit constructor calls Microsoft extension.
   if (getLangOpts().MicrosoftExt &&
       Id.getKind() == UnqualifiedIdKind::IK_ConstructorName)

diff  --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p2.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p2.cpp
new file mode 100644
index 0000000000000..ebdae971a929e
--- /dev/null
+++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p2.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -verify -Wno-unused %s
+
+struct A {
+  int y;
+};
+
+struct B; // expected-note 4{{forward declaration of 'B'}}
+
+void f(A *a, B *b) {
+  a->B::x; // expected-error {{incomplete type 'B' named in nested name specifier}}
+  a->A::x; // expected-error {{no member named 'x' in 'A'}}
+  a->A::y;
+  b->B::x; // expected-error {{member access into incomplete type 'B'}}
+  b->A::x; // expected-error {{member access into incomplete type 'B'}}
+  b->A::y; // expected-error {{member access into incomplete type 'B'}}
+}


        


More information about the cfe-commits mailing list