[clang] Reapply "[Clang][CWG1815] Support lifetime extension of temporary created by aggregate initialization using a default member initializer" (PR #97308)

via cfe-commits cfe-commits at lists.llvm.org
Mon Aug 26 05:03:24 PDT 2024


================
@@ -5567,19 +5568,35 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
   ImmediateCallVisitor V(getASTContext());
   if (!NestedDefaultChecking)
     V.TraverseDecl(Field);
-  if (V.HasImmediateCalls) {
+
+  // CWG1815
+  // Support lifetime extension of temporary created by aggregate
+  // initialization using a default member initializer. We should always rebuild
+  // the initializer if it contains any temporaries (if the initializer
+  // expression is an ExprWithCleanups). Then make sure the normal lifetime
+  // extension code recurses into the default initializer and does lifetime
+  // extension when warranted.
+  bool ContainsAnyTemporaries =
+      isa_and_present<ExprWithCleanups>(Field->getInClassInitializer());
+  if (V.HasImmediateCalls || InLifetimeExtendingContext ||
+      ContainsAnyTemporaries) {
     ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
                                                                    CurContext};
     ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
         NestedDefaultChecking;
-
+    // Pass down lifetime extending flag, and collect temporaries in
+    // CreateMaterializeTemporaryExpr when we rewrite the call argument.
+    keepInLifetimeExtendingContext();
     EnsureImmediateInvocationInDefaultArgs Immediate(*this);
     ExprResult Res;
+
+    // Rebuild CXXDefaultInitExpr might cause diagnostics.
+    SFINAETrap Trap(*this);
----------------
yronglin wrote:

This was used to silence the duplocated access diagnostics:
```C++
class Private {
  Private(int);
public:
  Private();
};

class S {
  Private p = 42;
  S() {}
  S(int) {}
};

S s{};
```
The latest clang trunk will emit 2 erros https://godbolt.org/z/dWcqY97GK, and without this, this PR will emit duplicated errors:
```
./access.cpp:8:15: error: field of type 'Private' has private constructor
    8 |   Private p = 42;
      |               ^
./access.cpp:2:3: note: implicitly declared private here
    2 |   Private(int);
      |   ^
Contains Error: 0
./access.cpp:8:15: error: field of type 'Private' has private constructor
    8 |   Private p = 42;
      |               ^
./access.cpp:2:3: note: implicitly declared private here
    2 |   Private(int);
      |   ^
Contains Error: 0
./access.cpp:8:15: error: field of type 'Private' has private constructor
    8 |   Private p = 42;
      |               ^
./access.cpp:2:3: note: implicitly declared private here
    2 |   Private(int);
      |   ^
./access.cpp:13:3: error: calling a private constructor of class 'S'
   13 | S s{};
      |   ^
./access.cpp:9:3: note: implicitly declared private here
    9 |   S() {}
      |   ^
4 errors generated.
```
This doesn't seem like a good approach. If the in-class-initializer contains error(or warnings?), should we skip rebuild the init expr? And seems Expr::containsErrors doesn't works for accessibility issues. 

https://github.com/llvm/llvm-project/pull/97308


More information about the cfe-commits mailing list