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:
Richard Smith 2014-07-31 06:31:19 +00:00
parent c537bd2da4
commit 77be48ac47
8 changed files with 172 additions and 15 deletions

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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.");

View File

@ -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;

View File

@ -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

View File

@ -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 };
}
}

View File

@ -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}}
}