Skip to content

Conversation

@tbaederr
Copy link
Contributor

No description provided.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:bytecode Issues for the clang bytecode constexpr interpreter labels Sep 23, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 23, 2025

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/160350.diff

7 Files Affected:

  • (modified) clang/lib/AST/ByteCode/Compiler.cpp (+9-4)
  • (modified) clang/lib/AST/ByteCode/Compiler.h (+1)
  • (modified) clang/lib/AST/ByteCode/Interp.cpp (+2)
  • (modified) clang/lib/AST/ByteCode/Interp.h (+13)
  • (modified) clang/lib/AST/ByteCode/Opcodes.td (+1)
  • (modified) clang/test/AST/ByteCode/cxx23.cpp (+42)
  • (modified) clang/test/AST/ByteCode/invalid.cpp (+1-1)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index b4da99957ee88..a81107289d91d 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -4452,6 +4452,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;
@@ -4560,13 +4563,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 *>()))
@@ -4874,7 +4878,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
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 09599b3547888..5c46f75af4da3 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -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);
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 0f322f6ed42ac..6a737c1c5e550 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -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;
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index b3b4b998439cc..d98ae8853147e 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -3344,6 +3344,19 @@ 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);
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index 7af2df5318106..532c4448e6f40 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -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 {
diff --git a/clang/test/AST/ByteCode/cxx23.cpp b/clang/test/AST/ByteCode/cxx23.cpp
index 72c751d627a44..0cc6e3b25fcef 100644
--- a/clang/test/AST/ByteCode/cxx23.cpp
+++ b/clang/test/AST/ByteCode/cxx23.cpp
@@ -393,6 +393,48 @@ 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; // all20-note {{volatile object declared here}}
+
+    *((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
+}
diff --git a/clang/test/AST/ByteCode/invalid.cpp b/clang/test/AST/ByteCode/invalid.cpp
index affb40eada870..00db27419e36b 100644
--- a/clang/test/AST/ByteCode/invalid.cpp
+++ b/clang/test/AST/ByteCode/invalid.cpp
@@ -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 {
 

@tbaederr tbaederr force-pushed the volatile-writes branch 2 times, most recently from 9b7c1aa to 1cd424c Compare September 24, 2025 08:47
@github-actions
Copy link

github-actions bot commented Sep 25, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@tbaederr tbaederr force-pushed the volatile-writes branch 2 times, most recently from ed58b80 to c950c3d Compare September 25, 2025 09:59
@tbaederr tbaederr merged commit c750487 into llvm:main Sep 28, 2025
9 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 28, 2025

LLVM Buildbot has detected a new failure on builder lldb-aarch64-windows running on linaro-armv8-windows-msvc-05 while building clang at step 6 "test".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/141/builds/11859

Here is the relevant piece of the build log for the reference
Step 6 (test) failure: build (failure)
...
UNSUPPORTED: lldb-api :: python_api/class_members/TestSBTypeClassMembers.py (1087 of 2314)
PASS: lldb-api :: python_api/compile_unit/TestCompileUnitAPI.py (1088 of 2314)
PASS: lldb-api :: python_api/debugger/TestDebuggerAPI.py (1089 of 2314)
PASS: lldb-api :: python_api/default-constructor/TestDefaultConstructorForAPIObjects.py (1090 of 2314)
PASS: lldb-api :: python_api/disassemble-raw-data/TestDisassembleRawData.py (1091 of 2314)
PASS: lldb-api :: python_api/disassemble-raw-data/TestDisassemble_VST1_64.py (1092 of 2314)
UNSUPPORTED: lldb-api :: python_api/event/TestEvents.py (1093 of 2314)
UNSUPPORTED: lldb-api :: python_api/exprpath_synthetic/TestExprPathSynthetic.py (1094 of 2314)
PASS: lldb-api :: python_api/file_handle/TestFileHandle.py (1095 of 2314)
TIMEOUT: lldb-api :: functionalities/step-vrs-interrupt/TestStepVrsInterruptTimeout.py (1096 of 2314)
******************** TEST 'lldb-api :: functionalities/step-vrs-interrupt/TestStepVrsInterruptTimeout.py' FAILED ********************
Script:
--
C:/Users/tcwg/scoop/apps/python/current/python.exe C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/llvm-project/lldb\test\API\dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./lib --env LLVM_INCLUDE_DIR=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/include --env LLVM_TOOLS_DIR=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin --arch aarch64 --build-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/lldb-test-build.noindex --lldb-module-cache-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/lldb-test-build.noindex/module-cache-lldb\lldb-api --clang-module-cache-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/lldb-test-build.noindex/module-cache-clang\lldb-api --executable C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin/lldb.exe --compiler C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin/clang.exe --dsymutil C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin/dsymutil.exe --make C:/Users/tcwg/scoop/shims/make.exe --llvm-tools-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin --lldb-obj-root C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/tools/lldb --lldb-libs-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./lib --cmake-build-type Release --skip-category=watchpoint C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\test\API\functionalities\step-vrs-interrupt -p TestStepVrsInterruptTimeout.py
--
Exit Code: 15
Timeout: Reached timeout of 600 seconds

Command Output (stdout):
--
lldb version 22.0.0git (https://github.com/llvm/llvm-project.git revision c7504872927486bb70c26aa71f795b1331cbbd38)
  clang revision c7504872927486bb70c26aa71f795b1331cbbd38
  llvm revision c7504872927486bb70c26aa71f795b1331cbbd38

--

********************
PASS: lldb-api :: python_api/find_in_memory/TestFindInMemory.py (1097 of 2314)
PASS: lldb-api :: python_api/findvalue_duplist/TestSBFrameFindValue.py (1098 of 2314)
PASS: lldb-api :: python_api/format/TestFormat.py (1099 of 2314)
PASS: lldb-api :: python_api/formatters/TestFormattersSBAPI.py (1100 of 2314)
PASS: lldb-api :: python_api/find_in_memory/TestFindRangesInMemory.py (1101 of 2314)
PASS: lldb-api :: python_api/frame/TestFrames.py (1102 of 2314)
PASS: lldb-api :: python_api/frame/get-variables/TestGetVariables.py (1103 of 2314)
PASS: lldb-api :: python_api/frame/inlines/TestInlinedFrame.py (1104 of 2314)
UNSUPPORTED: lldb-api :: python_api/function_symbol/TestDisasmAPI.py (1105 of 2314)
UNSUPPORTED: lldb-api :: python_api/function_symbol/TestSymbolAPI.py (1106 of 2314)
UNSUPPORTED: lldb-api :: python_api/global_module_cache/TestGlobalModuleCache.py (1107 of 2314)
PASS: lldb-api :: python_api/get-value-32bit-int/TestGetValue32BitInt.py (1108 of 2314)
PASS: lldb-api :: python_api/interpreter/TestCommandInterpreterAPI.py (1109 of 2314)
PASS: lldb-api :: python_api/interpreter/TestCommandOverrideCallback.py (1110 of 2314)
PASS: lldb-api :: python_api/interpreter/TestRunCommandInterpreterAPI.py (1111 of 2314)
PASS: lldb-api :: python_api/hello_world/TestHelloWorld.py (1112 of 2314)
PASS: lldb-api :: python_api/interpreter_callback/TestCommandInterepterPrintCallback.py (1113 of 2314)
PASS: lldb-api :: python_api/lldbutil/TestSwigVersion.py (1114 of 2314)
PASS: lldb-api :: python_api/lldbutil/frame/TestFrameUtils.py (1115 of 2314)
PASS: lldb-api :: python_api/lldbutil/iter/TestRegistersIterator.py (1116 of 2314)
PASS: lldb-api :: python_api/lldbutil/iter/TestLLDBIterator.py (1117 of 2314)
PASS: lldb-api :: python_api/lldbutil/process/TestPrintStackTraces.py (1118 of 2314)

mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Oct 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:bytecode Issues for the clang bytecode constexpr interpreter clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants