Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2934,8 +2934,9 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
// For everyhing else, use local variables.
if (SubExprT) {
bool IsConst = SubExpr->getType().isConstQualified();
unsigned LocalIndex =
allocateLocalPrimitive(E, *SubExprT, IsConst, E->getExtendingDecl());
bool IsVolatile = SubExpr->getType().isVolatileQualified();
unsigned LocalIndex = allocateLocalPrimitive(
E, *SubExprT, IsConst, IsVolatile, E->getExtendingDecl());
if (!this->visit(SubExpr))
return false;
if (!this->emitSetLocal(*SubExprT, LocalIndex, E))
Expand Down Expand Up @@ -4452,6 +4453,9 @@ bool Compiler<Emitter>::visitAssignment(const Expr *LHS, const Expr *RHS,
if (!this->visit(LHS))
return false;

if (LHS->getType().isVolatileQualified())
return this->emitInvalidStore(LHS->getType().getTypePtr(), E);

// We don't support assignments in C.
if (!Ctx.getLangOpts().CPlusPlus && !this->emitInvalid(E))
return false;
Expand Down Expand Up @@ -4560,13 +4564,14 @@ bool Compiler<Emitter>::emitConst(const APSInt &Value, const Expr *E) {

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

if (VarT) {
unsigned Offset = this->allocateLocalPrimitive(
VD, *VarT, VD->getType().isConstQualified(), nullptr, ScopeKind::Block,
VD, *VarT, VD->getType().isConstQualified(),
VD->getType().isVolatileQualified(), nullptr, ScopeKind::Block,
IsConstexprUnknown);
if (Init) {
// If this is a toplevel declaration, create a scope for the
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ByteCode/Compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,

/// Creates a local primitive value.
unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsConst,
bool IsVolatile = false,
const ValueDecl *ExtendingDecl = nullptr,
ScopeKind SC = ScopeKind::Block,
bool IsConstexprUnknown = false);
Expand Down
15 changes: 13 additions & 2 deletions clang/lib/AST/ByteCode/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -521,9 +521,15 @@ const Function *Context::getOrCreateFunction(const FunctionDecl *FuncDecl) {
// Assign descriptors to all parameters.
// Composite objects are lowered to pointers.
for (const ParmVarDecl *PD : FuncDecl->parameters()) {
bool IsConst = PD->getType().isConstQualified();
bool IsVolatile = PD->getType().isVolatileQualified();

OptPrimType T = classify(PD->getType());
PrimType PT = T.value_or(PT_Ptr);
Descriptor *Desc = P->createDescriptor(PD, PT);
Descriptor *Desc = P->createDescriptor(PD, PT, nullptr, std::nullopt,
IsConst, /*IsTemporary=*/false,
/*IsMutable=*/false, IsVolatile);

ParamDescriptors.insert({ParamOffset, {PT, Desc}});
ParamOffsets.push_back(ParamOffset);
ParamOffset += align(primSize(PT));
Expand All @@ -549,9 +555,14 @@ const Function *Context::getOrCreateObjCBlock(const BlockExpr *E) {
// Assign descriptors to all parameters.
// Composite objects are lowered to pointers.
for (const ParmVarDecl *PD : BD->parameters()) {
bool IsConst = PD->getType().isConstQualified();
bool IsVolatile = PD->getType().isVolatileQualified();

OptPrimType T = classify(PD->getType());
PrimType PT = T.value_or(PT_Ptr);
Descriptor *Desc = P->createDescriptor(PD, PT);
Descriptor *Desc = P->createDescriptor(PD, PT, nullptr, std::nullopt,
IsConst, /*IsTemporary=*/false,
/*IsMutable=*/false, IsVolatile);
ParamDescriptors.insert({ParamOffset, {PT, Desc}});
ParamOffsets.push_back(ParamOffset);
ParamOffset += align(primSize(PT));
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/ByteCode/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,8 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return false;
if (!CheckConst(S, OpPC, Ptr))
return false;
if (!CheckVolatile(S, OpPC, Ptr, AK_Assign))
return false;
if (!S.inConstantContext() && isConstexprUnknown(Ptr))
return false;
return true;
Expand Down
15 changes: 13 additions & 2 deletions clang/lib/AST/ByteCode/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1730,9 +1730,8 @@ inline bool GetPtrLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
}

inline bool GetPtrParam(InterpState &S, CodePtr OpPC, uint32_t I) {
if (S.checkingPotentialConstantExpression()) {
if (S.Current->isBottomFrame())
return false;
}
S.Stk.push<Pointer>(S.Current->getParamPointer(I));
return true;
}
Expand Down Expand Up @@ -3344,6 +3343,18 @@ inline bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind,
return false;
}

inline bool InvalidStore(InterpState &S, CodePtr OpPC, const Type *T) {
if (S.getLangOpts().CPlusPlus) {
QualType VolatileType = QualType(T, 0).withVolatile();
S.FFDiag(S.Current->getSource(OpPC),
diag::note_constexpr_access_volatile_type)
<< AK_Assign << VolatileType;
} else {
S.FFDiag(S.Current->getSource(OpPC));
}
return false;
}

inline bool InvalidDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR,
bool InitializerFailed) {
assert(DR);
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/ByteCode/InterpFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ Pointer InterpFrame::getParamPointer(unsigned Off) {
if (auto Pt = Params.find(Off); Pt != Params.end())
return Pointer(reinterpret_cast<Block *>(Pt->second.get()));

assert(!isBottomFrame());

// Allocate memory to store the parameter and the block metadata.
const auto &Desc = Func->getParamDescriptor(Off);
size_t BlockSize = sizeof(Block) + Desc.second->getAllocSize();
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ByteCode/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,7 @@ def SideEffect : Opcode {}
def InvalidCast : Opcode {
let Args = [ArgCastKind, ArgBool];
}
def InvalidStore : Opcode { let Args = [ArgTypePtr]; }
def CheckPseudoDtor : Opcode {}

def InvalidDeclRef : Opcode {
Expand Down
61 changes: 57 additions & 4 deletions clang/test/AST/ByteCode/cxx23.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// UNSUPPORTED: target={{.*}}-zos{{.*}}
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=ref,ref20,all,all20 %s
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=ref,ref23,all,all23 %s
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected20,all,all20 %s -fexperimental-new-constant-interpreter
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=expected23,all,all23 %s -fexperimental-new-constant-interpreter
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -Wno-deprecated-volatile -verify=ref,ref20,all,all20 %s
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -Wno-deprecated-volatile -verify=ref,ref23,all,all23 %s
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -Wno-deprecated-volatile -verify=expected20,all,all20 %s -fexperimental-new-constant-interpreter
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -Wno-deprecated-volatile -verify=expected23,all,all23 %s -fexperimental-new-constant-interpreter


#define assert_active(F) if (!__builtin_is_within_lifetime(&F)) (1/0);
Expand Down Expand Up @@ -393,6 +393,59 @@ namespace UnionMemberCallDiags {
static_assert(g()); // all-error {{not an integral constant expression}} \
// all-note {{in call to}}
}
#endif

namespace VolatileWrites {
constexpr void test1() {// all20-error {{never produces a constant expression}}
int k;
volatile int &m = k;
m = 10; // all20-note {{assignment to volatile-qualified type 'volatile int'}}
}

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

k = 13; // all20-note {{assignment to volatile-qualified type 'volatile int'}}
}

constexpr void test3() { // all20-error {{never produces a constant expression}}
volatile int k = 12; // all20-note {{volatile object declared here}}

*((int *)&k) = 13; // all20-note {{assignment to volatile object 'k' is not allowed in a constant expression}}
}

constexpr void test4() { // all20-error {{never produces a constant expression}}
int k = 12;

*((volatile int *)&k) = 13; // all20-note {{assignment to volatile-qualified type 'volatile int' is not allowed in a constant expression}}
}

#if __cplusplus >= 202302L
struct S {
volatile int k;
};
constexpr int test5() {
S s;
s.k = 12; // all-note {{assignment to volatile-qualified type 'volatile int' is not}}

return 0;
}
static_assert(test5() == 0); // all-error{{not an integral constant expression}} \
// all-note {{in call to}}
#endif

constexpr bool test6(volatile int k) { // ref20-error {{never produces a constant expression}}
k = 14; // ref20-note {{assignment to volatile-qualified type 'volatile int' is not}} \
// all-note {{assignment to volatile-qualified type 'volatile int' is not}}
return true;
}
static_assert(test6(5)); // all-error {{not an integral constant expression}} \
// all-note {{in call to}}

constexpr bool test7(volatile int k) { // all-note {{declared here}}
*((int *)&k) = 13; // all-note {{assignment to volatile object 'k' is not allowed in a constant expression}}
return true;
}
static_assert(test7(12)); // all-error {{not an integral constant expression}} \
// all-note {{in call to}}
}
2 changes: 1 addition & 1 deletion clang/test/AST/ByteCode/invalid.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -fcxx-exceptions -std=c++20 -fexperimental-new-constant-interpreter -verify=expected,both %s
// RUN: %clang_cc1 -fcxx-exceptions -std=c++20 -verify=ref,both %s
// RUN: %clang_cc1 -fcxx-exceptions -std=c++20 -verify=ref,both %s

namespace Throw {

Expand Down