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

via cfe-commits cfe-commits at lists.llvm.org
Mon May 20 08:44:09 PDT 2024


================
@@ -5695,19 +5694,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:

Here we add `SFINAETrap Trap(*this)`. It was used to resolve the issue which looks like https://github.com/llvm/llvm-project/blob/main/clang/test/CXX/dcl.decl/dcl.init/p14-0x.cpp .
A shorter reproducer:
```
class Private {
  Private(int); // expected-note {{here}}
public:
  Private();
};

class S {
  Private p = 42; // expected-error {{private constructor}}

  S() {} // expected-error {{call to deleted constructor of 'NoDefault'}} \
            expected-error {{must explicitly initialize the member 'e1' which does not have a default constructor}}
  S(int) {}
};

// FIXME: test the other forms which use copy-initialization

```

`Field->getInClassInitializer()` contains an `bad access error`, then we try to rebuild this initializer, the diagnostic will emit 3 times. (The result of `Field->getInClassInitializer()->containsErrors()` 3 times are `false`).
1st time: parser member initializer.
2nd time: After rebuild, Line 5726 `      Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc);`
3rd time: After rebuild, Line 5726 `      Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc);`

```
./error.cpp:8:15: error: field of type 'Private' has private constructor
    8 |   Private p = 42; // expected-error {{private constructor}}
      |               ^
./error.cpp:2:3: note: implicitly declared private here
    2 |   Private(int); // expected-note {{here}}
      |   ^
./error.cpp:8:15: error: field of type 'Private' has private constructor
    8 |   Private p = 42; // expected-error {{private constructor}}
      |               ^
./error.cpp:2:3: note: implicitly declared private here
    2 |   Private(int); // expected-note {{here}}
      |   ^
./error.cpp:8:15: error: field of type 'Private' has private constructor
    8 |   Private p = 42; // expected-error {{private constructor}}
      |               ^
./error.cpp:2:3: note: implicitly declared private here
    2 |   Private(int); // expected-note {{here}}
      |   ^
3 errors generated.
```

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


More information about the cfe-commits mailing list