(original) (raw)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 5d4d152b2eb54..fe24c08f0c3ed 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -598,6 +598,7 @@ Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ - Clang now properly preserves ``FoundDecls`` within a ``ConceptReference``. (#GH82628) - The presence of the ``typename`` keyword is now stored in ``TemplateTemplateParmDecl``. +- Fixed malformed AST generated for anonymous union access in templates. (#GH90842) Miscellaneous Bug Fixes ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index f47bc219e6fa3..a4074dbd62987 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -2876,10 +2876,21 @@ class TreeTransform { return ExprError(); Base = BaseResult.get(); + // `TranformMaterializeTemporaryExpr()` removes materialized temporaries + // from the AST, so we need to re-insert them if needed (since + // `BuildFieldRefereneExpr()` doesn't do this). + if (!isArrow && Base->isPRValue()) { + BaseResult = getSema().TemporaryMaterializationConversion(Base); + if (BaseResult.isInvalid()) + return ExprError(); + Base = BaseResult.get(); + } + CXXScopeSpec EmptySS; return getSema().BuildFieldReferenceExpr( Base, isArrow, OpLoc, EmptySS, cast(Member), - DeclAccessPair::make(FoundDecl, FoundDecl->getAccess()), MemberNameInfo); + DeclAccessPair::make(FoundDecl, FoundDecl->getAccess()), + MemberNameInfo); } CXXScopeSpec SS; diff --git a/clang/test/AST/ast-dump-anonymous-class.cpp b/clang/test/AST/ast-dump-anonymous-class.cpp new file mode 100644 index 0000000000000..393c084c913d1 --- /dev/null +++ b/clang/test/AST/ast-dump-anonymous-class.cpp @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown -ast-dump %s \ +// RUN: | FileCheck -strict-whitespace %s + +struct S { + struct { + int i; + }; +}; + +int accessInRegularFunction() { + return S().i; + // CHECK: FunctionDecl {{.*}} accessInRegularFunction 'int ()' + // CHECK: | `-ReturnStmt {{.*}} + // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'int' + // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'int' + // CHECK-NEXT: | `-MemberExpr {{.*}} 'int' xvalue .i + // CHECK-NEXT: | `-MemberExpr {{.*}} 'S::(anonymous struct at {{.*}}) + // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'S' xvalue + // CHECK-NEXT: | `-CXXTemporaryObjectExpr {{.*}} 'S' 'void () noexcept' zeroing +} + +// AST should look the same in a function template with an unused template +// parameter. +template +int accessInFunctionTemplate() { + return S().i; + // CHECK: FunctionDecl {{.*}} accessInFunctionTemplate 'int ()' + // CHECK: | `-ReturnStmt {{.*}} + // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'int' + // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'int' + // CHECK-NEXT: | `-MemberExpr {{.*}} 'int' xvalue .i + // CHECK-NEXT: | `-MemberExpr {{.*}} 'S::(anonymous struct at {{.*}}) + // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'S' xvalue + // CHECK-NEXT: | `-CXXTemporaryObjectExpr {{.*}} 'S' 'void () noexcept' zeroing +} + +// AST should look the same in an instantiation of the function template. +// This is a regression test: The AST used to contain the +// `MaterializeTemporaryExpr` in the wrong place, causing a `MemberExpr` to have +// a prvalue base (which is not allowed in C++). +template int accessInFunctionTemplate(); + // CHECK: FunctionDecl {{.*}} accessInFunctionTemplate 'int ()' explicit_instantiation_definition + // CHECK: `-ReturnStmt {{.*}} + // CHECK-NEXT: `-ExprWithCleanups {{.*}} 'int' + // CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int' + // CHECK-NEXT: `-MemberExpr {{.*}} 'int' xvalue .i + // CHECK-NEXT: `-MemberExpr {{.*}} 'S::(anonymous struct at {{.*}}) + // CHECK-NEXT: `-MaterializeTemporaryExpr {{.*}} 'S' xvalue + // CHECK-NEXT: `-CXXTemporaryObjectExpr {{.*}} 'S' 'void () noexcept' zeroing diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp index 301bec32c0cf1..3f68adaf1c62d 100644 --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -27,6 +27,14 @@ #include #include +namespace clang { +namespace dataflow { +namespace { +AST_MATCHER(FunctionDecl, isTemplated) { return Node.isTemplated(); } +} // namespace +} // namespace dataflow +} // namespace clang + namespace { using namespace clang; @@ -7205,4 +7213,31 @@ TEST(TransferTest, ConditionalRelation) { }); } +// This is a crash repro. +// We used to crash while transferring `S().i` because Clang contained a bug +// causing the AST to be malformed. +TEST(TransferTest, AnonymousUnionMemberExprInTemplate) { + using ast_matchers::functionDecl; + using ast_matchers::hasName; + using ast_matchers::unless; + + std::string Code = R"cc( + struct S { + struct { + int i; + }; + }; + + template + void target() { + S().i; + } + + template void target(); + )cc"; + auto Matcher = functionDecl(hasName("target"), unless(isTemplated())); + ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis(Code, Matcher), + llvm::Succeeded()); +} + } // namespace