From cd9ec3b4fb3d042f89aa5b572de7df3ef9ee4a80 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 22 Feb 2011 18:29:51 +0000 Subject: [PATCH] Implement the GNU C extension which permits the initialization of an array from a constant array compound literal. Fixes PR9261. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@126230 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 7 ++ include/clang/Sema/Initialization.h | 17 +++- lib/Sema/SemaInit.cpp | 105 ++++++++++++++++++++- test/Sema/array-init.c | 14 +++ 4 files changed, 137 insertions(+), 6 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 284698222f..0715eba69e 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2464,6 +2464,13 @@ def err_typecheck_incomplete_array_needs_initializer : Error< def err_array_init_not_init_list : Error< "array initializer must be an initializer " "list%select{| or string literal}0">; +def err_array_init_different_type : Error< + "cannot initialize array of type %0 with array of type %1">; +def err_array_init_non_constant_array : Error< + "cannot initialize array of type %0 with non-constant array of type %1">; +def ext_array_init_copy : Extension< + "initialization of an array of type %0 from a compound literal of type %1 is " + "a GNU extension">; def warn_deprecated_string_literal_conversion : Warning< "conversion from string literal to %0 is deprecated">, InGroup; def err_realimag_invalid_type : Error<"invalid type %0 to %1 operator">; diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index c191565990..bdf0d8e7b6 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -467,7 +467,10 @@ public: CAssignment, /// \brief String initialization - StringInit + StringInit, + + /// \brief Array initialization from another array (GNU C extension). + ArrayInit }; /// \brief Describes the kind of a particular step in an initialization @@ -513,7 +516,10 @@ public: SK_StringInit, /// \brief An initialization that "converts" an Objective-C object /// (not a point to an object) to another Objective-C object type. - SK_ObjCObjectConversion + SK_ObjCObjectConversion, + /// \brief Array initialization (from an array rvalue). + /// This is a GNU C extension. + SK_ArrayInit }; /// \brief A single step in the initialization sequence. @@ -563,6 +569,10 @@ public: /// \brief Array must be initialized with an initializer list or a /// string literal. FK_ArrayNeedsInitListOrStringLiteral, + /// \brief Array type mismatch. + FK_ArrayTypeMismatch, + /// \brief Non-constant array initializer + FK_NonConstantArrayInit, /// \brief Cannot resolve the address of an overloaded function. FK_AddressOfOverloadFailed, /// \brief Overloading due to reference initialization failed. @@ -775,6 +785,9 @@ public: /// always a no-op. void AddObjCObjectConversionStep(QualType T); + /// \brief Add an array initialization step. + void AddArrayInitStep(QualType T); + /// \brief Note that this initialization sequence failed. void SetFailed(FailureKind Failure) { SequenceKind = FailedSequence; diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index de6b962aa3..6a1bc97de7 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -2074,6 +2074,7 @@ void InitializationSequence::Step::Destroy() { case SK_CAssignment: case SK_StringInit: case SK_ObjCObjectConversion: + case SK_ArrayInit: break; case SK_ConversionSequence: @@ -2105,6 +2106,8 @@ bool InitializationSequence::isAmbiguous() const { case FK_InitListBadDestinationType: case FK_DefaultInitOfConst: case FK_Incomplete: + case FK_ArrayTypeMismatch: + case FK_NonConstantArrayInit: return false; case FK_ReferenceInitOverloadFailed: @@ -2247,6 +2250,13 @@ void InitializationSequence::AddObjCObjectConversionStep(QualType T) { Steps.push_back(S); } +void InitializationSequence::AddArrayInitStep(QualType T) { + Step S; + S.Kind = SK_ArrayInit; + S.Type = T; + Steps.push_back(S); +} + void InitializationSequence::SetOverloadFailure(FailureKind Failure, OverloadingResult Result) { SequenceKind = FailedSequence; @@ -3069,6 +3079,25 @@ static void TryUserDefinedConversion(Sema &S, } } +/// \brief Determine whether we have compatible array types for the +/// purposes of GNU by-copy array initialization. +static bool hasCompatibleArrayTypes(ASTContext &Context, + const ArrayType *Dest, + const ArrayType *Source) { + // If the source and destination array types are equivalent, we're + // done. + if (Context.hasSameType(QualType(Dest, 0), QualType(Source, 0))) + return true; + + // Make sure that the element types are the same. + if (!Context.hasSameType(Dest->getElementType(), Source->getElementType())) + return false; + + // The only mismatch we allow is when the destination is an + // incomplete array type and the source is a constant array type. + return Source->isConstantArrayType() && Dest->isIncompleteArrayType(); +} + InitializationSequence::InitializationSequence(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, @@ -3142,13 +3171,29 @@ InitializationSequence::InitializationSequence(Sema &S, // initializer is a string literal, see 8.5.2. // - Otherwise, if the destination type is an array, the program is // ill-formed. - if (const ArrayType *arrayType = Context.getAsArrayType(DestType)) { - if (Initializer && IsStringInit(Initializer, arrayType, Context)) { + if (const ArrayType *DestAT = Context.getAsArrayType(DestType)) { + if (Initializer && IsStringInit(Initializer, DestAT, Context)) { TryStringLiteralInitialization(S, Entity, Kind, Initializer, *this); return; } - if (arrayType->getElementType()->isAnyCharacterType()) + // Note: as an GNU C extension, we allow initialization of an + // array from a compound literal that creates an array of the same + // type, so long as the initializer has no side effects. + if (!S.getLangOptions().CPlusPlus && Initializer && + isa(Initializer->IgnoreParens()) && + Initializer->getType()->isArrayType()) { + const ArrayType *SourceAT + = Context.getAsArrayType(Initializer->getType()); + if (!hasCompatibleArrayTypes(S.Context, DestAT, SourceAT)) + SetFailed(FK_ArrayTypeMismatch); + else if (Initializer->HasSideEffects(S.Context)) + SetFailed(FK_NonConstantArrayInit); + else { + setSequenceKind(ArrayInit); + AddArrayInitStep(DestType); + } + } else if (DestAT->getElementType()->isAnyCharacterType()) SetFailed(FK_ArrayNeedsInitListOrStringLiteral); else SetFailed(FK_ArrayNeedsInitList); @@ -3631,7 +3676,8 @@ InitializationSequence::Perform(Sema &S, case SK_ListInitialization: case SK_CAssignment: case SK_StringInit: - case SK_ObjCObjectConversion: { + case SK_ObjCObjectConversion: + case SK_ArrayInit: { assert(Args.size() == 1); Expr *CurInitExpr = Args.get()[0]; if (!CurInitExpr) return ExprError(); @@ -4056,6 +4102,30 @@ InitializationSequence::Perform(Sema &S, CurInit.release(); CurInit = S.Owned(CurInitExpr); break; + + case SK_ArrayInit: + // Okay: we checked everything before creating this step. Note that + // this is a GNU extension. + S.Diag(Kind.getLocation(), diag::ext_array_init_copy) + << Step->Type << CurInitExpr->getType() + << CurInitExpr->getSourceRange(); + + // If the destination type is an incomplete array type, update the + // type accordingly. + if (ResultType) { + if (const IncompleteArrayType *IncompleteDest + = S.Context.getAsIncompleteArrayType(Step->Type)) { + if (const ConstantArrayType *ConstantSource + = S.Context.getAsConstantArrayType(CurInitExpr->getType())) { + *ResultType = S.Context.getConstantArrayType( + IncompleteDest->getElementType(), + ConstantSource->getSize(), + ArrayType::Normal, 0); + } + } + } + + break; } } @@ -4097,6 +4167,17 @@ bool InitializationSequence::Diagnose(Sema &S, << (Failure == FK_ArrayNeedsInitListOrStringLiteral); break; + case FK_ArrayTypeMismatch: + case FK_NonConstantArrayInit: + S.Diag(Kind.getLocation(), + (Failure == FK_ArrayTypeMismatch + ? diag::err_array_init_different_type + : diag::err_array_init_non_constant_array)) + << DestType.getNonReferenceType() + << Args[0]->getType() + << Args[0]->getSourceRange(); + break; + case FK_AddressOfOverloadFailed: { DeclAccessPair Found; S.ResolveAddressOfOverloadedFunction(Args[0], @@ -4354,6 +4435,14 @@ void InitializationSequence::dump(llvm::raw_ostream &OS) const { OS << "array requires initializer list or string literal"; break; + case FK_ArrayTypeMismatch: + OS << "array type mismatch"; + break; + + case FK_NonConstantArrayInit: + OS << "non-constant array initializer"; + break; + case FK_AddressOfOverloadFailed: OS << "address of overloaded function failed"; break; @@ -4457,6 +4546,10 @@ void InitializationSequence::dump(llvm::raw_ostream &OS) const { case StringInit: OS << "String initialization: "; break; + + case ArrayInit: + OS << "Array initialization: "; + break; } for (step_iterator S = step_begin(), SEnd = step_end(); S != SEnd; ++S) { @@ -4536,6 +4629,10 @@ void InitializationSequence::dump(llvm::raw_ostream &OS) const { case SK_ObjCObjectConversion: OS << "Objective-C object conversion"; break; + + case SK_ArrayInit: + OS << "array initialization"; + break; } } } diff --git a/test/Sema/array-init.c b/test/Sema/array-init.c index 0ee22c0f19..345ab6981b 100644 --- a/test/Sema/array-init.c +++ b/test/Sema/array-init.c @@ -264,3 +264,17 @@ void test_matrix() { } char badchararray[1] = { badchararray[0], "asdf" }; // expected-warning {{excess elements in array initializer}} expected-error {{initializer element is not a compile-time constant}} + +// Test the GNU extension for initializing an array from an array +// compound literal. PR9261. +typedef int int5[5]; +int a1[5] = (int[]){1, 2, 3, 4, 5}; // expected-warning{{initialization of an array of type 'int [5]' from a compound literal of type 'int [5]' is a GNU extension}} +int a2[5] = (int[5]){1, 2, 3, 4, 5}; // expected-warning{{initialization of an array of type 'int [5]' from a compound literal of type 'int [5]' is a GNU extension}} +int a3[] = ((int[]){1, 2, 3, 4, 5}); // expected-warning{{initialization of an array of type 'int []' from a compound literal of type 'int [5]' is a GNU extension}} +int a4[] = (int[5]){1, 2, 3, 4, 5}; // expected-warning{{initialization of an array of type 'int []' from a compound literal of type 'int [5]' is a GNU extension}} +int a5[] = (int5){1, 2, 3, 4, 5}; // expected-warning{{initialization of an array of type 'int []' from a compound literal of type 'int5' (aka 'int [5]') is a GNU extension}} + +int a6[5] = (int[]){1, 2, 3}; // expected-error{{cannot initialize array of type 'int [5]' with array of type 'int [3]'}} + +int nonconst_value(); +int a7[5] = (int[5]){ 1, 2, 3, 4, nonconst_value() }; // expected-error{{initializer element is not a compile-time constant}} -- 2.40.0