Skip to content

Commit 8397045

Browse files
authored
Merge pull request #11022 from hnrklssn/fix-vardecl-init-serialization-fr
[Modules] Record side effect info in EvaluatedStmt
2 parents c606913 + 668ed82 commit 8397045

File tree

11 files changed

+158
-10
lines changed

11 files changed

+158
-10
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -874,13 +874,17 @@ struct EvaluatedStmt {
874874
bool HasICEInit : 1;
875875
bool CheckedForICEInit : 1;
876876

877+
bool HasSideEffects : 1;
878+
bool CheckedForSideEffects : 1;
879+
877880
LazyDeclStmtPtr Value;
878881
APValue Evaluated;
879882

880883
EvaluatedStmt()
881884
: WasEvaluated(false), IsEvaluating(false),
882885
HasConstantInitialization(false), HasConstantDestruction(false),
883-
HasICEInit(false), CheckedForICEInit(false) {}
886+
HasICEInit(false), CheckedForICEInit(false), HasSideEffects(false),
887+
CheckedForSideEffects(false) {}
884888
};
885889

886890
/// Represents a variable declaration or definition.
@@ -1339,6 +1343,13 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
13391343
return const_cast<VarDecl *>(this)->getInitializingDeclaration();
13401344
}
13411345

1346+
/// Checks whether this declaration has an initializer with side effects.
1347+
/// The result is cached. If the result hasn't been computed this can trigger
1348+
/// deserialization and constant evaluation. By running this during
1349+
/// serialization and serializing the result all clients can safely call this
1350+
/// without triggering further deserialization.
1351+
bool hasInitWithSideEffects() const;
1352+
13421353
/// Determine whether this variable's value might be usable in a
13431354
/// constant expression, according to the relevant language standard.
13441355
/// This only checks properties of the declaration, and does not check

clang/include/clang/AST/ExternalASTSource.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class RecordDecl;
5151
class Selector;
5252
class Stmt;
5353
class TagDecl;
54+
class VarDecl;
5455

5556
/// Abstract interface for external sources of AST nodes.
5657
///

clang/lib/AST/ASTContext.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13277,9 +13277,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
1327713277
return true;
1327813278

1327913279
// Variables that have initialization with side-effects are required.
13280-
if (VD->getInit() && VD->getInit()->HasSideEffects(*this) &&
13281-
// We can get a value-dependent initializer during error recovery.
13282-
(VD->getInit()->isValueDependent() || !VD->evaluateValue()))
13280+
if (VD->hasInitWithSideEffects())
1328313281
return true;
1328413282

1328513283
// Likewise, variables with tuple-like bindings are required if their

clang/lib/AST/Decl.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2440,6 +2440,23 @@ VarDecl *VarDecl::getInitializingDeclaration() {
24402440
return Def;
24412441
}
24422442

2443+
bool VarDecl::hasInitWithSideEffects() const {
2444+
if (!hasInit())
2445+
return false;
2446+
2447+
EvaluatedStmt *ES = ensureEvaluatedStmt();
2448+
if (!ES->CheckedForSideEffects) {
2449+
const Expr *E = getInit();
2450+
ES->HasSideEffects =
2451+
E->HasSideEffects(getASTContext()) &&
2452+
// We can get a value-dependent initializer during error recovery.
2453+
(E->isValueDependent() || getType()->isDependentType() ||
2454+
!evaluateValue());
2455+
ES->CheckedForSideEffects = true;
2456+
}
2457+
return ES->HasSideEffects;
2458+
}
2459+
24432460
bool VarDecl::isOutOfLine() const {
24442461
if (Decl::isOutOfLine())
24452462
return true;

clang/lib/AST/ExprConstant.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16269,6 +16269,12 @@ static bool EvaluateInPlace(APValue &Result, EvalInfo &Info, const LValue &This,
1626916269
const Expr *E, bool AllowNonLiteralTypes) {
1627016270
assert(!E->isValueDependent());
1627116271

16272+
// Normally expressions passed to EvaluateInPlace have a type, but not when
16273+
// a VarDecl initializer is evaluated before the untyped ParenListExpr is
16274+
// replaced with a CXXConstructExpr. This can happen in LLDB.
16275+
if (E->getType().isNull())
16276+
return false;
16277+
1627216278
if (!AllowNonLiteralTypes && !CheckLiteralType(Info, E, &This))
1627316279
return false;
1627416280

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,6 +1692,8 @@ void ASTDeclReader::ReadVarDeclInit(VarDecl *VD) {
16921692
Eval->HasConstantInitialization = (Val & 2) != 0;
16931693
Eval->HasConstantDestruction = (Val & 4) != 0;
16941694
Eval->WasEvaluated = (Val & 8) != 0;
1695+
Eval->HasSideEffects = (Val & 16) != 0;
1696+
Eval->CheckedForSideEffects = true;
16951697
if (Eval->WasEvaluated) {
16961698
Eval->Evaluated = Record.readAPValue();
16971699
if (Eval->Evaluated.needsCleanup())

clang/lib/Serialization/ASTWriter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6830,6 +6830,10 @@ void ASTRecordWriter::AddVarDeclInit(const VarDecl *VD) {
68306830

68316831
uint64_t Val = 1;
68326832
if (EvaluatedStmt *ES = VD->getEvaluatedStmt()) {
6833+
// This may trigger evaluation, so run it first
6834+
if (VD->hasInitWithSideEffects())
6835+
Val |= 16;
6836+
assert(ES->CheckedForSideEffects);
68336837
Val |= (ES->HasConstantInitialization ? 2 : 0);
68346838
Val |= (ES->HasConstantDestruction ? 4 : 0);
68356839
APValue *Evaluated = VD->getEvaluatedValue();

clang/lib/Serialization/ASTWriterDecl.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,10 +1207,11 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
12071207
!D->hasExtInfo() && D->getFirstDecl() == D->getMostRecentDecl() &&
12081208
D->getKind() == Decl::Var && !D->isInline() && !D->isConstexpr() &&
12091209
!D->isInitCapture() && !D->isPreviousDeclInSameBlockScope() &&
1210-
!D->isEscapingByref() && !HasDeducedType &&
1211-
D->getStorageDuration() != SD_Static && !D->getDescribedVarTemplate() &&
1212-
!D->getMemberSpecializationInfo() && !D->isObjCForDecl() &&
1213-
!isa<ImplicitParamDecl>(D) && !D->isEscapingByref())
1210+
!D->hasInitWithSideEffects() && !D->isEscapingByref() &&
1211+
!HasDeducedType && D->getStorageDuration() != SD_Static &&
1212+
!D->getDescribedVarTemplate() && !D->getMemberSpecializationInfo() &&
1213+
!D->isObjCForDecl() && !isa<ImplicitParamDecl>(D) &&
1214+
!D->isEscapingByref())
12141215
AbbrevToUse = Writer.getDeclVarAbbrev();
12151216

12161217
Code = serialization::DECL_VAR;
@@ -2529,12 +2530,12 @@ void ASTWriter::WriteDeclAbbrevs() {
25292530
// VarDecl
25302531
Abv->Add(BitCodeAbbrevOp(
25312532
BitCodeAbbrevOp::Fixed,
2532-
21)); // Packed Var Decl bits: Linkage, ModulesCodegen,
2533+
22)); // Packed Var Decl bits: Linkage, ModulesCodegen,
25332534
// SClass, TSCSpec, InitStyle,
25342535
// isARCPseudoStrong, IsThisDeclarationADemotedDefinition,
25352536
// isExceptionVariable, isNRVOVariable, isCXXForRangeDecl,
25362537
// isInline, isInlineSpecified, isConstexpr,
2537-
// isInitCapture, isPrevDeclInSameScope,
2538+
// isInitCapture, isPrevDeclInSameScope, hasInitWithSideEffects,
25382539
// EscapingByref, HasDeducedType, ImplicitParamKind, isObjCForDecl
25392540
Abv->Add(BitCodeAbbrevOp(0)); // VarKind (local enum)
25402541
// Type Source Info
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
5+
// RUN: %clang_cc1 -fsyntax-only -fmodules -fmodules-cache-path=%t -fmodule-map-file=%t/module.modulemap %t/test.cppm -I%t
6+
//
7+
8+
//--- test.cppm
9+
#pragma clang module import Baz
10+
11+
//--- Foo.h
12+
#pragma once
13+
class foo {
14+
char dummy = 1;
15+
16+
public:
17+
static foo var;
18+
19+
};
20+
21+
inline foo foo::var;
22+
23+
//--- Bar.h
24+
#pragma once
25+
#include <Foo.h>
26+
27+
void bar() {
28+
(void) foo::var;
29+
}
30+
31+
//--- Baz.h
32+
#pragma once
33+
#include <Foo.h>
34+
35+
void baz() {
36+
(void) foo::var;
37+
}
38+
39+
#include <Bar.h>
40+
41+
//--- module.modulemap
42+
module Foo {
43+
header "Foo.h"
44+
}
45+
module Bar {
46+
header "Bar.h"
47+
}
48+
module Baz {
49+
header "Baz.h"
50+
}
51+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Tests referencing variable with initializer containing side effect across module boundary
2+
3+
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -o %t
4+
5+
export module Foo;
6+
7+
export template <class Float>
8+
struct Wrapper {
9+
double value;
10+
};
11+
12+
export constexpr Wrapper<double> Compute() {
13+
return Wrapper<double>{1.0};
14+
}
15+
16+
export template <typename Float>
17+
Wrapper<Float> ComputeInFloat() {
18+
const Wrapper<Float> a = Compute();
19+
return a;
20+
}

0 commit comments

Comments
 (0)