forked from OSchip/llvm-project
PR18097: Support initializing an _Atomic(T) from an object of C++ class type T
or a class derived from T. We already supported this when initializing _Atomic(T) from T for most (and maybe all) other reasonable values of T. llvm-svn: 214390
This commit is contained in:
parent
c537bd2da4
commit
77be48ac47
|
@ -663,6 +663,8 @@ public:
|
|||
SK_QualificationConversionXValue,
|
||||
/// \brief Perform a qualification conversion, producing an lvalue.
|
||||
SK_QualificationConversionLValue,
|
||||
/// \brief Perform a conversion adding _Atomic to a type.
|
||||
SK_AtomicConversion,
|
||||
/// \brief Perform a load from a glvalue, producing an rvalue.
|
||||
SK_LValueToRValue,
|
||||
/// \brief Perform an implicit conversion sequence.
|
||||
|
@ -999,7 +1001,11 @@ public:
|
|||
/// given type.
|
||||
void AddQualificationConversionStep(QualType Ty,
|
||||
ExprValueKind Category);
|
||||
|
||||
|
||||
/// \brief Add a new step that performs conversion from non-atomic to atomic
|
||||
/// type.
|
||||
void AddAtomicConversionStep(QualType Ty);
|
||||
|
||||
/// \brief Add a new step that performs a load of the given type.
|
||||
///
|
||||
/// Although the term "LValueToRValue" is conventional, this applies to both
|
||||
|
|
|
@ -1353,6 +1353,11 @@ static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc,
|
|||
return false;
|
||||
}
|
||||
|
||||
// We allow _Atomic(T) to be initialized from anything that T can be
|
||||
// initialized from.
|
||||
if (const AtomicType *AT = Type->getAs<AtomicType>())
|
||||
Type = AT->getValueType();
|
||||
|
||||
// Core issue 1454: For a literal constant expression of array or class type,
|
||||
// each subobject of its value shall have been initialized by a constant
|
||||
// expression.
|
||||
|
|
|
@ -1137,6 +1137,16 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (E->getType()->isAtomicType()) {
|
||||
// An _Atomic(T) object can be list-initialized from an expression
|
||||
// of the same type.
|
||||
assert(E->getNumInits() == 1 &&
|
||||
CGF.getContext().hasSameUnqualifiedType(E->getInit(0)->getType(),
|
||||
E->getType()) &&
|
||||
"unexpected list initialization for atomic object");
|
||||
return Visit(E->getInit(0));
|
||||
}
|
||||
|
||||
assert(E->getType()->isRecordType() && "Only support structs/unions here!");
|
||||
|
||||
// Do struct initialization; this code just sets each individual member
|
||||
|
|
|
@ -1045,6 +1045,25 @@ llvm::Constant *CodeGenModule::EmitConstantExpr(const Expr *E,
|
|||
llvm::Constant *CodeGenModule::EmitConstantValue(const APValue &Value,
|
||||
QualType DestType,
|
||||
CodeGenFunction *CGF) {
|
||||
// For an _Atomic-qualified constant, we may need to add tail padding.
|
||||
if (auto *AT = DestType->getAs<AtomicType>()) {
|
||||
QualType InnerType = AT->getValueType();
|
||||
auto *Inner = EmitConstantValue(Value, InnerType, CGF);
|
||||
|
||||
uint64_t InnerSize = Context.getTypeSize(InnerType);
|
||||
uint64_t OuterSize = Context.getTypeSize(DestType);
|
||||
if (InnerSize == OuterSize)
|
||||
return Inner;
|
||||
|
||||
assert(InnerSize < OuterSize && "emitted over-large constant for atomic");
|
||||
llvm::Constant *Elts[] = {
|
||||
Inner,
|
||||
llvm::ConstantAggregateZero::get(
|
||||
llvm::ArrayType::get(Int8Ty, (OuterSize - InnerSize) / 8))
|
||||
};
|
||||
return llvm::ConstantStruct::getAnon(Elts);
|
||||
}
|
||||
|
||||
switch (Value.getKind()) {
|
||||
case APValue::Uninitialized:
|
||||
llvm_unreachable("Constant expressions should be initialized.");
|
||||
|
|
|
@ -2767,6 +2767,7 @@ void InitializationSequence::Step::Destroy() {
|
|||
case SK_QualificationConversionRValue:
|
||||
case SK_QualificationConversionXValue:
|
||||
case SK_QualificationConversionLValue:
|
||||
case SK_AtomicConversion:
|
||||
case SK_LValueToRValue:
|
||||
case SK_ListInitialization:
|
||||
case SK_UnwrapInitList:
|
||||
|
@ -2919,6 +2920,13 @@ void InitializationSequence::AddQualificationConversionStep(QualType Ty,
|
|||
Steps.push_back(S);
|
||||
}
|
||||
|
||||
void InitializationSequence::AddAtomicConversionStep(QualType Ty) {
|
||||
Step S;
|
||||
S.Kind = SK_AtomicConversion;
|
||||
S.Type = Ty;
|
||||
Steps.push_back(S);
|
||||
}
|
||||
|
||||
void InitializationSequence::AddLValueToRValueStep(QualType Ty) {
|
||||
assert(!Ty.hasQualifiers() && "rvalues may not have qualifiers");
|
||||
|
||||
|
@ -4174,12 +4182,11 @@ static void TryDefaultInitialization(Sema &S,
|
|||
/// which enumerates all conversion functions and performs overload resolution
|
||||
/// to select the best.
|
||||
static void TryUserDefinedConversion(Sema &S,
|
||||
const InitializedEntity &Entity,
|
||||
QualType DestType,
|
||||
const InitializationKind &Kind,
|
||||
Expr *Initializer,
|
||||
InitializationSequence &Sequence,
|
||||
bool TopLevelOfInitList) {
|
||||
QualType DestType = Entity.getType();
|
||||
assert(!DestType->isReferenceType() && "References are handled elsewhere");
|
||||
QualType SourceType = Initializer->getType();
|
||||
assert((DestType->isRecordType() || SourceType->isRecordType()) &&
|
||||
|
@ -4596,7 +4603,6 @@ void InitializationSequence::InitializeFrom(Sema &S,
|
|||
Initializer) ||
|
||||
S.ConversionToObjCStringLiteralCheck(DestType, Initializer))
|
||||
Args[0] = Initializer;
|
||||
|
||||
}
|
||||
if (!isa<InitListExpr>(Initializer))
|
||||
SourceType = Initializer->getType();
|
||||
|
@ -4741,7 +4747,7 @@ void InitializationSequence::InitializeFrom(Sema &S,
|
|||
(Context.hasSameUnqualifiedType(SourceType, DestType) ||
|
||||
S.IsDerivedFrom(SourceType, DestType))))
|
||||
TryConstructorInitialization(S, Entity, Kind, Args,
|
||||
Entity.getType(), *this);
|
||||
DestType, *this);
|
||||
// - Otherwise (i.e., for the remaining copy-initialization cases),
|
||||
// user-defined conversion sequences that can convert from the source
|
||||
// type to the destination type or (when a conversion function is
|
||||
|
@ -4749,7 +4755,7 @@ void InitializationSequence::InitializeFrom(Sema &S,
|
|||
// 13.3.1.4, and the best one is chosen through overload resolution
|
||||
// (13.3).
|
||||
else
|
||||
TryUserDefinedConversion(S, Entity, Kind, Initializer, *this,
|
||||
TryUserDefinedConversion(S, DestType, Kind, Initializer, *this,
|
||||
TopLevelOfInitList);
|
||||
return;
|
||||
}
|
||||
|
@ -4763,9 +4769,22 @@ void InitializationSequence::InitializeFrom(Sema &S,
|
|||
// - Otherwise, if the source type is a (possibly cv-qualified) class
|
||||
// type, conversion functions are considered.
|
||||
if (!SourceType.isNull() && SourceType->isRecordType()) {
|
||||
TryUserDefinedConversion(S, Entity, Kind, Initializer, *this,
|
||||
// For a conversion to _Atomic(T) from either T or a class type derived
|
||||
// from T, initialize the T object then convert to _Atomic type.
|
||||
bool NeedAtomicConversion = false;
|
||||
if (const AtomicType *Atomic = DestType->getAs<AtomicType>()) {
|
||||
if (Context.hasSameUnqualifiedType(SourceType, Atomic->getValueType()) ||
|
||||
S.IsDerivedFrom(SourceType, Atomic->getValueType())) {
|
||||
DestType = Atomic->getValueType();
|
||||
NeedAtomicConversion = true;
|
||||
}
|
||||
}
|
||||
|
||||
TryUserDefinedConversion(S, DestType, Kind, Initializer, *this,
|
||||
TopLevelOfInitList);
|
||||
MaybeProduceObjCObject(S, *this, Entity);
|
||||
if (!Failed() && NeedAtomicConversion)
|
||||
AddAtomicConversionStep(Entity.getType());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -4774,16 +4793,16 @@ void InitializationSequence::InitializeFrom(Sema &S,
|
|||
// conversions (Clause 4) will be used, if necessary, to convert the
|
||||
// initializer expression to the cv-unqualified version of the
|
||||
// destination type; no user-defined conversions are considered.
|
||||
|
||||
|
||||
ImplicitConversionSequence ICS
|
||||
= S.TryImplicitConversion(Initializer, Entity.getType(),
|
||||
= S.TryImplicitConversion(Initializer, DestType,
|
||||
/*SuppressUserConversions*/true,
|
||||
/*AllowExplicitConversions*/ false,
|
||||
/*InOverloadResolution*/ false,
|
||||
/*CStyle=*/Kind.isCStyleOrFunctionalCast(),
|
||||
allowObjCWritebackConversion);
|
||||
|
||||
if (ICS.isStandard() &&
|
||||
|
||||
if (ICS.isStandard() &&
|
||||
ICS.Standard.Second == ICK_Writeback_Conversion) {
|
||||
// Objective-C ARC writeback conversion.
|
||||
|
||||
|
@ -4804,7 +4823,7 @@ void InitializationSequence::InitializeFrom(Sema &S,
|
|||
AddConversionSequenceStep(LvalueICS, ICS.Standard.getToType(0));
|
||||
}
|
||||
|
||||
AddPassByIndirectCopyRestoreStep(Entity.getType(), ShouldCopy);
|
||||
AddPassByIndirectCopyRestoreStep(DestType, ShouldCopy);
|
||||
} else if (ICS.isBad()) {
|
||||
DeclAccessPair dap;
|
||||
if (isLibstdcxxPointerReturnFalseHack(S, Entity, Initializer)) {
|
||||
|
@ -4816,7 +4835,7 @@ void InitializationSequence::InitializeFrom(Sema &S,
|
|||
else
|
||||
SetFailed(InitializationSequence::FK_ConversionFailed);
|
||||
} else {
|
||||
AddConversionSequenceStep(ICS, Entity.getType(), TopLevelOfInitList);
|
||||
AddConversionSequenceStep(ICS, DestType, TopLevelOfInitList);
|
||||
|
||||
MaybeProduceObjCObject(S, *this, Entity);
|
||||
}
|
||||
|
@ -5772,6 +5791,7 @@ InitializationSequence::Perform(Sema &S,
|
|||
case SK_QualificationConversionLValue:
|
||||
case SK_QualificationConversionXValue:
|
||||
case SK_QualificationConversionRValue:
|
||||
case SK_AtomicConversion:
|
||||
case SK_LValueToRValue:
|
||||
case SK_ConversionSequence:
|
||||
case SK_ConversionSequenceNoNarrowing:
|
||||
|
@ -6062,6 +6082,13 @@ InitializationSequence::Perform(Sema &S,
|
|||
break;
|
||||
}
|
||||
|
||||
case SK_AtomicConversion: {
|
||||
assert(CurInit.get()->isRValue() && "cannot convert glvalue to atomic");
|
||||
CurInit = S.ImpCastExprToType(CurInit.get(), Step->Type,
|
||||
CK_NonAtomicToAtomic, VK_RValue);
|
||||
break;
|
||||
}
|
||||
|
||||
case SK_LValueToRValue: {
|
||||
assert(CurInit.get()->isGLValue() && "cannot load from a prvalue");
|
||||
CurInit = ImplicitCastExpr::Create(S.Context, Step->Type,
|
||||
|
@ -7033,6 +7060,10 @@ void InitializationSequence::dump(raw_ostream &OS) const {
|
|||
OS << "qualification conversion (lvalue)";
|
||||
break;
|
||||
|
||||
case SK_AtomicConversion:
|
||||
OS << "non-atomic-to-atomic conversion";
|
||||
break;
|
||||
|
||||
case SK_LValueToRValue:
|
||||
OS << "load (lvalue to rvalue)";
|
||||
break;
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
// they're sufficiently rare that it's not worth making sure that the semantics
|
||||
// are correct.
|
||||
|
||||
// CHECK: @testStructGlobal = global {{.*}} { i16 1, i16 2, i16 3, i16 4 }
|
||||
// CHECK: @testPromotedStructGlobal = global {{.*}} { %{{.*}} { i16 1, i16 2, i16 3 }, [2 x i8] zeroinitializer }
|
||||
|
||||
typedef int __attribute__((vector_size(16))) vector;
|
||||
|
||||
_Atomic(_Bool) b;
|
||||
|
@ -224,6 +227,7 @@ void testComplexFloat(_Atomic(_Complex float) *fp) {
|
|||
}
|
||||
|
||||
typedef struct { short x, y, z, w; } S;
|
||||
_Atomic S testStructGlobal = (S){1, 2, 3, 4};
|
||||
// CHECK: define arm_aapcscc void @testStruct([[S:.*]]*
|
||||
void testStruct(_Atomic(S) *fp) {
|
||||
// CHECK: [[FP:%.*]] = alloca [[S]]*, align 4
|
||||
|
@ -272,6 +276,7 @@ void testStruct(_Atomic(S) *fp) {
|
|||
}
|
||||
|
||||
typedef struct { short x, y, z; } PS;
|
||||
_Atomic PS testPromotedStructGlobal = (PS){1, 2, 3};
|
||||
// CHECK: define arm_aapcscc void @testPromotedStruct([[APS:.*]]*
|
||||
void testPromotedStruct(_Atomic(PS) *fp) {
|
||||
// CHECK: [[FP:%.*]] = alloca [[APS]]*, align 4
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
// RUN: %clang_cc1 %s -emit-llvm -O1 -o - -triple=i686-apple-darwin9 | FileCheck %s
|
||||
// RUN: %clang_cc1 %s -emit-llvm -O1 -o - -triple=i686-apple-darwin9 -std=c++11 | FileCheck %s
|
||||
|
||||
// CHECK-DAG: @_ZN7PR180978constant1aE = global {{.*}} { i16 1, i8 6, i8 undef }, align 4
|
||||
// CHECK-DAG: @_ZN7PR180978constant1bE = global {{.*}} { i16 2, i8 6, i8 undef }, align 4
|
||||
// CHECK-DAG: @_ZN7PR180978constant1cE = global {{.*}} { i16 3, i8 6, i8 undef }, align 4
|
||||
// CHECK-DAG: @_ZN7PR180978constant1yE = global {{.*}} { {{.*}} { i16 4, i8 6, i8 undef }, i32 5 }, align 4
|
||||
|
||||
struct A {
|
||||
_Atomic(int) i;
|
||||
A(int j);
|
||||
|
@ -46,3 +52,51 @@ struct AtomicBoolMember {
|
|||
// CHECK-NEXT: ret void
|
||||
AtomicBoolMember::AtomicBoolMember(bool b) : ab(b) { }
|
||||
|
||||
namespace PR18097 {
|
||||
namespace dynamic {
|
||||
struct X {
|
||||
X(int);
|
||||
short n;
|
||||
char c;
|
||||
};
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @__cxx_global_var_init
|
||||
// CHECK: call void @_ZN7PR180977dynamic1XC1Ei({{.*}}* @_ZN7PR180977dynamic1aE, i32 1)
|
||||
_Atomic(X) a = X(1);
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @__cxx_global_var_init
|
||||
// CHECK: call void @_ZN7PR180977dynamic1XC1Ei({{.*}}* @_ZN7PR180977dynamic1bE, i32 2)
|
||||
_Atomic(X) b(X(2));
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @__cxx_global_var_init
|
||||
// CHECK: call void @_ZN7PR180977dynamic1XC1Ei({{.*}}* @_ZN7PR180977dynamic1cE, i32 3)
|
||||
_Atomic(X) c{X(3)};
|
||||
|
||||
struct Y {
|
||||
_Atomic(X) a;
|
||||
_Atomic(int) b;
|
||||
};
|
||||
// CHECK-LABEL: define {{.*}} @__cxx_global_var_init
|
||||
// CHECK: call void @_ZN7PR180977dynamic1XC1Ei({{.*}}* getelementptr inbounds ({{.*}}* @_ZN7PR180977dynamic1yE, i32 0, i32 0), i32 4)
|
||||
// CHECK: store i32 5, i32* getelementptr inbounds ({{.*}}* @_ZN7PR180977dynamic1yE, i32 0, i32 1)
|
||||
Y y = { X(4), 5 };
|
||||
}
|
||||
|
||||
// CHECKs at top of file.
|
||||
namespace constant {
|
||||
struct X {
|
||||
constexpr X(int n) : n(n) {}
|
||||
short n;
|
||||
char c = 6;
|
||||
};
|
||||
_Atomic(X) a = X(1);
|
||||
_Atomic(X) b(X(2));
|
||||
_Atomic(X) c{X(3)};
|
||||
|
||||
struct Y {
|
||||
_Atomic(X) a;
|
||||
_Atomic(int) b;
|
||||
};
|
||||
Y y = { X(4), 5 };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// RUN: %clang_cc1 -verify -pedantic %s
|
||||
// RUN: %clang_cc1 -verify -pedantic %s -std=c++98
|
||||
// RUN: %clang_cc1 -verify -pedantic %s -std=c++11
|
||||
|
||||
template<typename T> struct atomic {
|
||||
_Atomic(T) value;
|
||||
|
@ -56,3 +57,29 @@ typedef _Atomic(int &) atomic_reference_to_int; // expected-error {{_Atomic cann
|
|||
struct S {
|
||||
_Atomic union { int n; }; // expected-warning {{anonymous union cannot be '_Atomic'}}
|
||||
};
|
||||
|
||||
namespace copy_init {
|
||||
struct X {
|
||||
X(int);
|
||||
int n;
|
||||
};
|
||||
_Atomic(X) y = X(0);
|
||||
_Atomic(X) z(X(0));
|
||||
void f() { y = X(0); }
|
||||
|
||||
_Atomic(X) e1(0); // expected-error {{cannot initialize}}
|
||||
#if __cplusplus >= 201103L
|
||||
_Atomic(X) e2{0}; // expected-error {{illegal initializer}}
|
||||
_Atomic(X) a{X(0)};
|
||||
#endif
|
||||
|
||||
struct Y {
|
||||
_Atomic(X) a;
|
||||
_Atomic(int) b;
|
||||
};
|
||||
Y y1 = { X(0), 4 };
|
||||
Y y2 = { 0, 4 }; // expected-error {{cannot initialize}}
|
||||
// FIXME: It's not really clear if we should allow these. Generally, C++11
|
||||
// allows extraneous braces around initializers.
|
||||
Y y3 = { { X(0) }, { 4 } }; // expected-error 2{{illegal initializer type}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue