diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h index 44658507990e..9f342b2407aa 100644 --- a/clang/include/clang/Sema/Initialization.h +++ b/clang/include/clang/Sema/Initialization.h @@ -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 diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 9bd630a272fd..538fc57672f2 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -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()) + 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. diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index b1406cdd7cca..4cf94c033bb4 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -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 diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index b2f08c32d92e..b508dcb446fb 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -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()) { + 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."); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 947be780e02a..d9f86c6e4fc3 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -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(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()) { + 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; diff --git a/clang/test/CodeGen/c11atomics.c b/clang/test/CodeGen/c11atomics.c index 5c761b137498..f4c9522cbdb2 100644 --- a/clang/test/CodeGen/c11atomics.c +++ b/clang/test/CodeGen/c11atomics.c @@ -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 diff --git a/clang/test/CodeGenCXX/atomicinit.cpp b/clang/test/CodeGenCXX/atomicinit.cpp index ee2e9e4cf244..f453194468dd 100644 --- a/clang/test/CodeGenCXX/atomicinit.cpp +++ b/clang/test/CodeGenCXX/atomicinit.cpp @@ -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 }; + } +} diff --git a/clang/test/SemaCXX/atomic-type.cpp b/clang/test/SemaCXX/atomic-type.cpp index f522d92f39b1..ae18eab5b4a9 100644 --- a/clang/test/SemaCXX/atomic-type.cpp +++ b/clang/test/SemaCXX/atomic-type.cpp @@ -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 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}} +}