Commit 311f725d authored by Timm Bäder's avatar Timm Bäder
Browse files

[clang][Interp] Create only globals when initializing a global variable

For this code:

struct O {
  int &&j;
};

O o1(0);

The generated AST for the initializer of o1 is:

VarDecl 0x62100006ab08 <array.cpp:119:3, col:9> col:5 o1 'O':'O' parenlistinit
`-ExprWithCleanups 0x62100006b250 <col:7, col:9> 'O':'O'
  `-CXXParenListInitExpr 0x62100006b210 <col:7, col:9> 'O':'O'
    `-MaterializeTemporaryExpr 0x62100006b1f0 <col:8> 'int' xvalue
      `-IntegerLiteral 0x62100006abd0 <col:8> 'int' 0

Before this patch, we create a local temporary variable for the
MaterializeTemporaryExpr and destroy it again when destroying the
EvalEmitter we create to interpret the initializer. However, since
O::j is a reference, this reference now points to a local variable
that doesn't exist anymore.

Differential Revision: https://reviews.llvm.org/D156453
parent 8505c3b1
Loading
Loading
Loading
Loading
+23 −6
Original line number Diff line number Diff line
@@ -29,14 +29,20 @@ namespace interp {
template <class Emitter> class DeclScope final : public VariableScope<Emitter> {
public:
  DeclScope(ByteCodeExprGen<Emitter> *Ctx, const ValueDecl *VD)
      : VariableScope<Emitter>(Ctx), Scope(Ctx->P, VD) {}
      : VariableScope<Emitter>(Ctx), Scope(Ctx->P, VD),
        OldGlobalDecl(Ctx->GlobalDecl) {
    Ctx->GlobalDecl = Context::shouldBeGloballyIndexed(VD);
  }

  void addExtended(const Scope::Local &Local) override {
    return this->addLocal(Local);
  }

  ~DeclScope() { this->Ctx->GlobalDecl = OldGlobalDecl; }

private:
  Program::DeclScope Scope;
  bool OldGlobalDecl;
};

/// Scope used to handle initialization methods.
@@ -1198,21 +1204,30 @@ bool ByteCodeExprGen<Emitter>::VisitMaterializeTemporaryExpr(
  if (DiscardResult)
    return this->discard(SubExpr);

  // When we're initializing a global variable *or* the storage duration of
  // the temporary is explicitly static, create a global variable.
  std::optional<PrimType> SubExprT = classify(SubExpr);
  if (E->getStorageDuration() == SD_Static) {
  bool IsStatic = E->getStorageDuration() == SD_Static;
  if (GlobalDecl || IsStatic) {
    std::optional<unsigned> GlobalIndex = P.createGlobal(E);
    if (!GlobalIndex)
      return false;

    const LifetimeExtendedTemporaryDecl *TempDecl =
        E->getLifetimeExtendedTemporaryDecl();
    if (IsStatic)
      assert(TempDecl);

    if (SubExprT) {
      if (!this->visit(SubExpr))
        return false;
      if (IsStatic) {
        if (!this->emitInitGlobalTemp(*SubExprT, *GlobalIndex, TempDecl, E))
          return false;
      } else {
        if (!this->emitInitGlobal(*SubExprT, *GlobalIndex, E))
          return false;
      }
      return this->emitGetPtrGlobal(*GlobalIndex, E);
    }

@@ -1221,7 +1236,9 @@ bool ByteCodeExprGen<Emitter>::VisitMaterializeTemporaryExpr(
      return false;
    if (!this->visitInitializer(SubExpr))
      return false;
    if (IsStatic)
      return this->emitInitGlobalTempComp(TempDecl, E);
    return true;
  }

  // For everyhing else, use local variables.
+3 −0
Original line number Diff line number Diff line
@@ -304,6 +304,9 @@ protected:
  /// Flag inidicating if we're initializing an already created
  /// variable. This is set in visitInitializer().
  bool Initializing = false;

  /// Flag indicating if we're initializing a global variable.
  bool GlobalDecl = false;
};

extern template class ByteCodeExprGen<ByteCodeEmitter>;
+16 −0
Original line number Diff line number Diff line
@@ -1064,6 +1064,22 @@ namespace ParenInit {
  };

  constexpr B b(A(1),2);


  struct O {
    int &&j;
  };

  /// Not constexpr!
  O o1(0);
  constinit O o2(0); // ref-error {{variable does not have a constant initializer}} \
                     // ref-note {{required by 'constinit' specifier}} \
                     // ref-note {{reference to temporary is not a constant expression}} \
                     // ref-note {{temporary created here}} \
                     // expected-error {{variable does not have a constant initializer}} \
                     // expected-note {{required by 'constinit' specifier}} \
                     // expected-note {{reference to temporary is not a constant expression}} \
                     // expected-note {{temporary created here}}
}
#endif

+2 −0
Original line number Diff line number Diff line
// RUN: %clang_cc1 -verify -std=c++20 %s -fsyntax-only
// RUN: %clang_cc1 -verify -std=c++20 %s -fsyntax-only -fexperimental-new-constant-interpreter
// RUN: %clang_cc1 -verify=expected,beforecxx20 -Wc++20-extensions -std=c++20 %s -fsyntax-only -fexperimental-new-constant-interpreter
// RUN: %clang_cc1 -verify=expected,beforecxx20 -Wc++20-extensions -std=c++20 %s -fsyntax-only

struct A { // expected-note 4{{candidate constructor}}