diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 3c4bcebe9cb1b..da0620bea20d5 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3154,8 +3154,15 @@ Sema::PerformObjectMemberConversion(Expr *From, /*IgnoreAccess=*/true)) return ExprError(); - return ImpCastExprToType(From, DestType, CK_UncheckedDerivedToBase, - VK, &BasePath); + // Propagate qualifiers to base subobjects as per: + // C++ [basic.type.qualifier]p1.2: + // A volatile object is [...] a subobject of a volatile object. + Qualifiers FromTypeQuals = FromType.getQualifiers(); + FromTypeQuals.setAddressSpace(DestType.getAddressSpace()); + DestType = Context.getQualifiedType(DestType, FromTypeQuals); + + return ImpCastExprToType(From, DestType, CK_UncheckedDerivedToBase, VK, + &BasePath); } bool Sema::UseArgumentDependentLookup(const CXXScopeSpec &SS, diff --git a/clang/test/CodeGenCXX/derived-to-base.cpp b/clang/test/CodeGenCXX/derived-to-base.cpp index c8dbd5bf5cb05..c09a12b728bbb 100644 --- a/clang/test/CodeGenCXX/derived-to-base.cpp +++ b/clang/test/CodeGenCXX/derived-to-base.cpp @@ -46,4 +46,29 @@ namespace test3 { } } +// Ensure volatile is preserved during derived-to-base conversion. +namespace PR127683 { + +struct Base { + int Val; +}; + +struct Derived : Base { }; + +volatile Derived Obj; + +// CHECK-LABEL: define void @_ZN8PR12768319test_volatile_storeEv() +// CHECK: store volatile i32 0, ptr @_ZN8PR1276833ObjE, align 4 +void test_volatile_store() { + Obj.Val = 0; +} + +// CHECK-LABEL: define void @_ZN8PR12768318test_volatile_loadEv() +// CHECK: %0 = load volatile i32, ptr @_ZN8PR1276833ObjE, align 4 +void test_volatile_load() { + [[maybe_unused]] int Val = Obj.Val; +} + +} + // CHECK: attributes [[NUW]] = { mustprogress noinline nounwind{{.*}} } diff --git a/clang/test/SemaCXX/derived-to-base-propagate-qualifiers.cpp b/clang/test/SemaCXX/derived-to-base-propagate-qualifiers.cpp new file mode 100644 index 0000000000000..8a3b944ab1cb6 --- /dev/null +++ b/clang/test/SemaCXX/derived-to-base-propagate-qualifiers.cpp @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -ast-dump -verify %s | FileCheck %s + +// Ensure qualifiers are preserved during derived-to-base conversion. +namespace PR127683 { + +struct Base { + int Val; +}; + +struct Derived : Base { }; + +// Value-initialize base class subobjects with type qualifiers. +volatile Derived VObj; +const Derived CObj{}; // expected-note{{variable 'CObj' declared const here}} +const volatile Derived CVObj{}; // expected-note{{variable 'CVObj' declared const here}} +__attribute__((address_space(1))) Derived AddrSpaceObj{}; + +void test_store() { + // CHECK: `-ImplicitCastExpr {{.*}} 'volatile PR127683::Base' lvalue + VObj.Val = 0; + + // CHECK: `-ImplicitCastExpr {{.*}} 'const PR127683::Base' lvalue + CObj.Val = 1; // expected-error {{cannot assign to variable 'CObj' with const-qualified type 'const Derived'}} + + // CHECK: `-ImplicitCastExpr {{.*}} 'const volatile PR127683::Base' lvalue + CVObj.Val = 1; // expected-error {{cannot assign to variable 'CVObj' with const-qualified type 'const volatile Derived'}} + + // CHECK: `-ImplicitCastExpr {{.*}} '__attribute__((address_space(1))) PR127683::Base' lvalue + AddrSpaceObj.Val = 1; +} + +void test_load() { + // CHECK: `-ImplicitCastExpr {{.*}} 'volatile PR127683::Base' lvalue + [[maybe_unused]] int Val = VObj.Val; + + // CHECK: `-ImplicitCastExpr {{.*}} 'const PR127683::Base' lvalue + Val = CObj.Val; + + // CHECK: `-ImplicitCastExpr {{.*}} 'const volatile PR127683::Base' lvalue + Val = CVObj.Val; + + // CHECK: `-ImplicitCastExpr {{.*}} '__attribute__((address_space(1))) PR127683::Base' lvalue + Val = AddrSpaceObj.Val; +} + +}