Skip to content

Commit 401cbfd

Browse files
tbaederrmahesh-attarde
authored andcommitted
[clang][bytecode] Diagnose volatile writes (llvm#160350)
1 parent 4eeba53 commit 401cbfd

File tree

9 files changed

+102
-15
lines changed

9 files changed

+102
-15
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2934,8 +2934,9 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
29342934
// For everyhing else, use local variables.
29352935
if (SubExprT) {
29362936
bool IsConst = SubExpr->getType().isConstQualified();
2937-
unsigned LocalIndex =
2938-
allocateLocalPrimitive(E, *SubExprT, IsConst, E->getExtendingDecl());
2937+
bool IsVolatile = SubExpr->getType().isVolatileQualified();
2938+
unsigned LocalIndex = allocateLocalPrimitive(
2939+
E, *SubExprT, IsConst, IsVolatile, E->getExtendingDecl());
29392940
if (!this->visit(SubExpr))
29402941
return false;
29412942
if (!this->emitSetLocal(*SubExprT, LocalIndex, E))
@@ -4452,6 +4453,9 @@ bool Compiler<Emitter>::visitAssignment(const Expr *LHS, const Expr *RHS,
44524453
if (!this->visit(LHS))
44534454
return false;
44544455

4456+
if (LHS->getType().isVolatileQualified())
4457+
return this->emitInvalidStore(LHS->getType().getTypePtr(), E);
4458+
44554459
// We don't support assignments in C.
44564460
if (!Ctx.getLangOpts().CPlusPlus && !this->emitInvalid(E))
44574461
return false;
@@ -4560,13 +4564,14 @@ bool Compiler<Emitter>::emitConst(const APSInt &Value, const Expr *E) {
45604564

45614565
template <class Emitter>
45624566
unsigned Compiler<Emitter>::allocateLocalPrimitive(
4563-
DeclTy &&Src, PrimType Ty, bool IsConst, const ValueDecl *ExtendingDecl,
4564-
ScopeKind SC, bool IsConstexprUnknown) {
4567+
DeclTy &&Src, PrimType Ty, bool IsConst, bool IsVolatile,
4568+
const ValueDecl *ExtendingDecl, ScopeKind SC, bool IsConstexprUnknown) {
45654569
// FIXME: There are cases where Src.is<Expr*>() is wrong, e.g.
45664570
// (int){12} in C. Consider using Expr::isTemporaryObject() instead
45674571
// or isa<MaterializeTemporaryExpr>().
45684572
Descriptor *D = P.createDescriptor(Src, Ty, nullptr, Descriptor::InlineDescMD,
4569-
IsConst, isa<const Expr *>(Src));
4573+
IsConst, isa<const Expr *>(Src),
4574+
/*IsMutable=*/false, IsVolatile);
45704575
D->IsConstexprUnknown = IsConstexprUnknown;
45714576
Scope::Local Local = this->createLocal(D);
45724577
if (auto *VD = dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>()))
@@ -4874,7 +4879,8 @@ Compiler<Emitter>::visitVarDecl(const VarDecl *VD, const Expr *Init,
48744879

48754880
if (VarT) {
48764881
unsigned Offset = this->allocateLocalPrimitive(
4877-
VD, *VarT, VD->getType().isConstQualified(), nullptr, ScopeKind::Block,
4882+
VD, *VarT, VD->getType().isConstQualified(),
4883+
VD->getType().isVolatileQualified(), nullptr, ScopeKind::Block,
48784884
IsConstexprUnknown);
48794885
if (Init) {
48804886
// If this is a toplevel declaration, create a scope for the

clang/lib/AST/ByteCode/Compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
327327

328328
/// Creates a local primitive value.
329329
unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsConst,
330+
bool IsVolatile = false,
330331
const ValueDecl *ExtendingDecl = nullptr,
331332
ScopeKind SC = ScopeKind::Block,
332333
bool IsConstexprUnknown = false);

clang/lib/AST/ByteCode/Context.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -567,9 +567,15 @@ const Function *Context::getOrCreateFunction(const FunctionDecl *FuncDecl) {
567567
// Assign descriptors to all parameters.
568568
// Composite objects are lowered to pointers.
569569
for (const ParmVarDecl *PD : FuncDecl->parameters()) {
570+
bool IsConst = PD->getType().isConstQualified();
571+
bool IsVolatile = PD->getType().isVolatileQualified();
572+
570573
OptPrimType T = classify(PD->getType());
571574
PrimType PT = T.value_or(PT_Ptr);
572-
Descriptor *Desc = P->createDescriptor(PD, PT);
575+
Descriptor *Desc = P->createDescriptor(PD, PT, nullptr, std::nullopt,
576+
IsConst, /*IsTemporary=*/false,
577+
/*IsMutable=*/false, IsVolatile);
578+
573579
ParamDescriptors.insert({ParamOffset, {PT, Desc}});
574580
ParamOffsets.push_back(ParamOffset);
575581
ParamOffset += align(primSize(PT));
@@ -595,9 +601,14 @@ const Function *Context::getOrCreateObjCBlock(const BlockExpr *E) {
595601
// Assign descriptors to all parameters.
596602
// Composite objects are lowered to pointers.
597603
for (const ParmVarDecl *PD : BD->parameters()) {
604+
bool IsConst = PD->getType().isConstQualified();
605+
bool IsVolatile = PD->getType().isVolatileQualified();
606+
598607
OptPrimType T = classify(PD->getType());
599608
PrimType PT = T.value_or(PT_Ptr);
600-
Descriptor *Desc = P->createDescriptor(PD, PT);
609+
Descriptor *Desc = P->createDescriptor(PD, PT, nullptr, std::nullopt,
610+
IsConst, /*IsTemporary=*/false,
611+
/*IsMutable=*/false, IsVolatile);
601612
ParamDescriptors.insert({ParamOffset, {PT, Desc}});
602613
ParamOffsets.push_back(ParamOffset);
603614
ParamOffset += align(primSize(PT));

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,8 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
889889
return false;
890890
if (!CheckConst(S, OpPC, Ptr))
891891
return false;
892+
if (!CheckVolatile(S, OpPC, Ptr, AK_Assign))
893+
return false;
892894
if (!S.inConstantContext() && isConstexprUnknown(Ptr))
893895
return false;
894896
return true;

clang/lib/AST/ByteCode/Interp.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,9 +1730,8 @@ inline bool GetPtrLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
17301730
}
17311731

17321732
inline bool GetPtrParam(InterpState &S, CodePtr OpPC, uint32_t I) {
1733-
if (S.checkingPotentialConstantExpression()) {
1733+
if (S.Current->isBottomFrame())
17341734
return false;
1735-
}
17361735
S.Stk.push<Pointer>(S.Current->getParamPointer(I));
17371736
return true;
17381737
}
@@ -3344,6 +3343,18 @@ inline bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind,
33443343
return false;
33453344
}
33463345

3346+
inline bool InvalidStore(InterpState &S, CodePtr OpPC, const Type *T) {
3347+
if (S.getLangOpts().CPlusPlus) {
3348+
QualType VolatileType = QualType(T, 0).withVolatile();
3349+
S.FFDiag(S.Current->getSource(OpPC),
3350+
diag::note_constexpr_access_volatile_type)
3351+
<< AK_Assign << VolatileType;
3352+
} else {
3353+
S.FFDiag(S.Current->getSource(OpPC));
3354+
}
3355+
return false;
3356+
}
3357+
33473358
inline bool InvalidDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR,
33483359
bool InitializerFailed) {
33493360
assert(DR);

clang/lib/AST/ByteCode/InterpFrame.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ Pointer InterpFrame::getParamPointer(unsigned Off) {
231231
if (auto Pt = Params.find(Off); Pt != Params.end())
232232
return Pointer(reinterpret_cast<Block *>(Pt->second.get()));
233233

234+
assert(!isBottomFrame());
235+
234236
// Allocate memory to store the parameter and the block metadata.
235237
const auto &Desc = Func->getParamDescriptor(Off);
236238
size_t BlockSize = sizeof(Block) + Desc.second->getAllocSize();

clang/lib/AST/ByteCode/Opcodes.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,7 @@ def SideEffect : Opcode {}
797797
def InvalidCast : Opcode {
798798
let Args = [ArgCastKind, ArgBool];
799799
}
800+
def InvalidStore : Opcode { let Args = [ArgTypePtr]; }
800801
def CheckPseudoDtor : Opcode {}
801802

802803
def InvalidDeclRef : Opcode {

clang/test/AST/ByteCode/cxx23.cpp

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// UNSUPPORTED: target={{.*}}-zos{{.*}}
2-
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=ref,ref20,all,all20 %s
3-
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=ref,ref23,all,all23 %s
4-
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected20,all,all20 %s -fexperimental-new-constant-interpreter
5-
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=expected23,all,all23 %s -fexperimental-new-constant-interpreter
2+
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -Wno-deprecated-volatile -verify=ref,ref20,all,all20 %s
3+
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -Wno-deprecated-volatile -verify=ref,ref23,all,all23 %s
4+
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -Wno-deprecated-volatile -verify=expected20,all,all20 %s -fexperimental-new-constant-interpreter
5+
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -Wno-deprecated-volatile -verify=expected23,all,all23 %s -fexperimental-new-constant-interpreter
66

77

88
#define assert_active(F) if (!__builtin_is_within_lifetime(&F)) (1/0);
@@ -393,6 +393,59 @@ namespace UnionMemberCallDiags {
393393
static_assert(g()); // all-error {{not an integral constant expression}} \
394394
// all-note {{in call to}}
395395
}
396+
#endif
397+
398+
namespace VolatileWrites {
399+
constexpr void test1() {// all20-error {{never produces a constant expression}}
400+
int k;
401+
volatile int &m = k;
402+
m = 10; // all20-note {{assignment to volatile-qualified type 'volatile int'}}
403+
}
396404

405+
constexpr void test2() { // all20-error {{never produces a constant expression}}
406+
volatile int k = 12;
397407

408+
k = 13; // all20-note {{assignment to volatile-qualified type 'volatile int'}}
409+
}
410+
411+
constexpr void test3() { // all20-error {{never produces a constant expression}}
412+
volatile int k = 12; // all20-note {{volatile object declared here}}
413+
414+
*((int *)&k) = 13; // all20-note {{assignment to volatile object 'k' is not allowed in a constant expression}}
415+
}
416+
417+
constexpr void test4() { // all20-error {{never produces a constant expression}}
418+
int k = 12;
419+
420+
*((volatile int *)&k) = 13; // all20-note {{assignment to volatile-qualified type 'volatile int' is not allowed in a constant expression}}
421+
}
422+
423+
#if __cplusplus >= 202302L
424+
struct S {
425+
volatile int k;
426+
};
427+
constexpr int test5() {
428+
S s;
429+
s.k = 12; // all-note {{assignment to volatile-qualified type 'volatile int' is not}}
430+
431+
return 0;
432+
}
433+
static_assert(test5() == 0); // all-error{{not an integral constant expression}} \
434+
// all-note {{in call to}}
398435
#endif
436+
437+
constexpr bool test6(volatile int k) { // ref20-error {{never produces a constant expression}}
438+
k = 14; // ref20-note {{assignment to volatile-qualified type 'volatile int' is not}} \
439+
// all-note {{assignment to volatile-qualified type 'volatile int' is not}}
440+
return true;
441+
}
442+
static_assert(test6(5)); // all-error {{not an integral constant expression}} \
443+
// all-note {{in call to}}
444+
445+
constexpr bool test7(volatile int k) { // all-note {{declared here}}
446+
*((int *)&k) = 13; // all-note {{assignment to volatile object 'k' is not allowed in a constant expression}}
447+
return true;
448+
}
449+
static_assert(test7(12)); // all-error {{not an integral constant expression}} \
450+
// all-note {{in call to}}
451+
}

clang/test/AST/ByteCode/invalid.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// RUN: %clang_cc1 -fcxx-exceptions -std=c++20 -fexperimental-new-constant-interpreter -verify=expected,both %s
2-
// RUN: %clang_cc1 -fcxx-exceptions -std=c++20 -verify=ref,both %s
2+
// RUN: %clang_cc1 -fcxx-exceptions -std=c++20 -verify=ref,both %s
33

44
namespace Throw {
55

0 commit comments

Comments
 (0)