Author: Timm Baeder Date: 2025-07-18T11:20:48+02:00 New Revision: b7660a54157fd45e6276acf35176851196f5df71
URL: https://github.com/llvm/llvm-project/commit/b7660a54157fd45e6276acf35176851196f5df71 DIFF: https://github.com/llvm/llvm-project/commit/b7660a54157fd45e6276acf35176851196f5df71.diff LOG: [clang][bytecode] Fix const-in-mutable fields (#149286) For mutable and const fields, we have two bits in InlineDescriptor, which both get inherited down the hierarchy. When a field is both const and mutable, we CAN read from it if it is a mutable-in-const field, but we _can't_ read from it if it is a const-in-mutable field. We need another bit to distinguish the two cases. Added: Modified: clang/lib/AST/ByteCode/Descriptor.cpp clang/lib/AST/ByteCode/Descriptor.h clang/lib/AST/ByteCode/Disasm.cpp clang/lib/AST/ByteCode/Interp.cpp clang/lib/AST/ByteCode/Pointer.h clang/test/AST/ByteCode/mutable.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp index c89eca9bef440..5b9f44518fcc2 100644 --- a/clang/lib/AST/ByteCode/Descriptor.cpp +++ b/clang/lib/AST/ByteCode/Descriptor.cpp @@ -162,6 +162,8 @@ static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable, Desc->IsConst = IsConst || D->IsConst; Desc->IsFieldMutable = IsMutable || D->IsMutable; Desc->IsVolatile = IsVolatile || D->IsVolatile; + // True if this field is const AND the parent is mutable. + Desc->IsConstInMutable = Desc->IsConst && IsMutable; if (auto Fn = D->CtorFn) Fn(B, Ptr + FieldOffset, Desc->IsConst, Desc->IsFieldMutable, diff --git a/clang/lib/AST/ByteCode/Descriptor.h b/clang/lib/AST/ByteCode/Descriptor.h index 4591eabb69bb4..0227e4c0c7e38 100644 --- a/clang/lib/AST/ByteCode/Descriptor.h +++ b/clang/lib/AST/ByteCode/Descriptor.h @@ -101,6 +101,10 @@ struct InlineDescriptor { /// Flag indicating if the field is mutable (if in a record). LLVM_PREFERRED_TYPE(bool) unsigned IsFieldMutable : 1; + /// Flag indicating if this field is a const field nested in + /// a mutable parent field. + LLVM_PREFERRED_TYPE(bool) + unsigned IsConstInMutable : 1; /// Flag indicating if the field is an element of a composite array. LLVM_PREFERRED_TYPE(bool) unsigned IsArrayElement : 1; diff --git a/clang/lib/AST/ByteCode/Disasm.cpp b/clang/lib/AST/ByteCode/Disasm.cpp index f64501f4a31e8..74399d177b5a2 100644 --- a/clang/lib/AST/ByteCode/Disasm.cpp +++ b/clang/lib/AST/ByteCode/Disasm.cpp @@ -445,6 +445,7 @@ LLVM_DUMP_METHOD void InlineDescriptor::dump(llvm::raw_ostream &OS) const { OS << "InUnion: " << InUnion << "\n"; OS << "IsFieldMutable: " << IsFieldMutable << "\n"; OS << "IsArrayElement: " << IsArrayElement << "\n"; + OS << "IsConstInMutable: " << IsConstInMutable << '\n'; OS << "Desc: "; if (Desc) Desc->dump(OS); diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index e8b519478c026..df5e3be83d741 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -566,7 +566,10 @@ bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { assert(Ptr.isLive() && "Pointer is not live"); - if (!Ptr.isConst() || Ptr.isMutable()) + if (!Ptr.isConst()) + return true; + + if (Ptr.isMutable() && !Ptr.isConstInMutable()) return true; if (!Ptr.isBlockPointer()) diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index e6a64e6658f06..da74013cf83a6 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -576,6 +576,11 @@ class Pointer { return true; return isRoot() ? getDeclDesc()->IsConst : getInlineDesc()->IsConst; } + bool isConstInMutable() const { + if (!isBlockPointer()) + return false; + return isRoot() ? false : getInlineDesc()->IsConstInMutable; + } /// Checks if an object or a subfield is volatile. bool isVolatile() const { diff --git a/clang/test/AST/ByteCode/mutable.cpp b/clang/test/AST/ByteCode/mutable.cpp index aebbea920578c..35c5a0389921e 100644 --- a/clang/test/AST/ByteCode/mutable.cpp +++ b/clang/test/AST/ByteCode/mutable.cpp @@ -1,11 +1,7 @@ -// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++11 -verify=expected,expected11,both,both11 %s -// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++14 -verify=expected,expected14,both %s -// RUN: %clang_cc1 -std=c++11 -verify=ref,ref11,both,both11 %s -// RUN: %clang_cc1 -std=c++14 -verify=ref,ref14,both %s - - - - +// RUN: %clang_cc1 -std=c++11 -verify=expected,expected11,both,both11 %s -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -std=c++14 -verify=expected,expected14,both %s -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -std=c++11 -verify=ref,ref11,both,both11 %s +// RUN: %clang_cc1 -std=c++14 -verify=ref,ref14,both %s namespace Simple { struct S { @@ -26,3 +22,47 @@ namespace Simple { static_assert(s2.a2 == 12, ""); // both11-error {{not an integral constant expression}} \ // both11-note {{initializer of 's2' is not a constant expression}} } +#if __cplusplus >= 201402L +namespace ConstInMutable { + class B { + public: + + const int f; + constexpr B() : f(12) {} + }; + class A { + public: + mutable B b; + constexpr A() = default; + }; + constexpr int constInMutable() { + A a; + + int *m = (int*)&a.b.f; + *m = 12; // both-note {{modification of object of const-qualified type 'const int' is not allowed in a constant expression}} + return 1; + } + static_assert(constInMutable() == 1, ""); // both-error {{not an integral constant expression}} \ + // both-note {{in call to}} +} + +namespace MutableInConst { + class C { + public: + mutable int c; + constexpr C() : c(50) {} + }; + class D { + public: + C c; + constexpr D() {} + }; + constexpr int mutableInConst() { + const D d{}; + int *m = (int*)&d.c.c; + *m = 12; + return 1; + } + static_assert(mutableInConst() == 1, ""); +} +#endif _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits