From 7b52d2ad531286ca3e14c3f05da51c91fd71bd0d Mon Sep 17 00:00:00 2001 From: Jonas Hahnfeld Date: Wed, 6 Sep 2023 13:11:57 +0200 Subject: [PATCH] [clang-repl] Emit const variables only once Disable internal linkage for const variables if IncrementalExtensions are enabled. Otherwise the variables are emitted multiple times, with multiple constructions at unique memory locations, during every PTU. --- clang/lib/AST/ASTContext.cpp | 10 ++++++++++ clang/test/Interpreter/const.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 clang/test/Interpreter/const.cpp diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 4b1d9e86797b7..f7438e9be19ee 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -11764,6 +11764,16 @@ GVALinkage ASTContext::GetGVALinkageForFunction(const FunctionDecl *FD) const { static GVALinkage basicGVALinkageForVariable(const ASTContext &Context, const VarDecl *VD) { + // As an extension for interactive REPLs, make sure constant variables are + // only emitted once instead of LinkageComputer::getLVForNamespaceScopeDecl + // marking them as internal. + if (Context.getLangOpts().CPlusPlus && + Context.getLangOpts().IncrementalExtensions && + VD->getType().isConstQualified() && + !VD->getType().isVolatileQualified() && !VD->isInline() && + !isa(VD) && !VD->getDescribedVarTemplate()) + return GVA_DiscardableODR; + if (!VD->isExternallyVisible()) return GVA_Internal; diff --git a/clang/test/Interpreter/const.cpp b/clang/test/Interpreter/const.cpp new file mode 100644 index 0000000000000..a4b610f1a19d8 --- /dev/null +++ b/clang/test/Interpreter/const.cpp @@ -0,0 +1,29 @@ +// UNSUPPORTED: system-aix +// RUN: cat %s | clang-repl | FileCheck %s +// RUN: cat %s | clang-repl -Xcc -O2 | FileCheck %s + +extern "C" int printf(const char*, ...); + +struct A { int val; A(int v); ~A(); void f() const; }; +A::A(int v) : val(v) { printf("A(%d), this = %p\n", val, this); } +A::~A() { printf("~A, this = %p, val = %d\n", this, val); } +void A::f() const { printf("f: this = %p, val = %d\n", this, val); } + +const A a(1); +// CHECK: A(1), this = [[THIS:0x[0-9a-f]+]] +// The constructor must only be called once! +// CHECK-NOT: A(1) + +a.f(); +// CHECK-NEXT: f: this = [[THIS]], val = 1 +a.f(); +// CHECK-NEXT: f: this = [[THIS]], val = 1 + +%quit +// There must still be no other constructor! +// CHECK-NOT: A(1) + +// At the end, we expect exactly one destructor call +// CHECK: ~A +// CHECK-SAME: this = [[THIS]], val = 1 +// CHECK-NOT: ~A