[clang] bf9de4c - Reject attempts to initialize non-aggregate types from a designated
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 6 18:01:15 PDT 2023
Author: Richard Smith
Date: 2023-04-06T18:01:01-07:00
New Revision: bf9de4cf131032502c8967a65283a461fe36520d
URL: https://github.com/llvm/llvm-project/commit/bf9de4cf131032502c8967a65283a461fe36520d
DIFF: https://github.com/llvm/llvm-project/commit/bf9de4cf131032502c8967a65283a461fe36520d.diff
LOG: Reject attempts to initialize non-aggregate types from a designated
initializer list.
This previously led to some weird behaviors where we would unwrap the
initializer list expression and then try to use the DesignatedInitExprs
as constructor arguments.
Under the C++20 language rules, it's not valid to initialize a
reference-to-aggregate from a designated initializer list, but we have
historically accepted that, as do other compilers, and we continue to
accept that with this change. I've asked WG21 whether this should be
considered a wording defect.
Added:
Modified:
clang/include/clang/AST/Expr.h
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Initialization.h
clang/lib/Sema/SemaInit.cpp
clang/lib/Sema/SemaOverload.cpp
clang/test/Sema/designated-initializers.c
clang/test/Sema/sizeless-1.c
clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp
clang/test/SemaCXX/sizeless-1.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 65476322c77df..e8f5b70f8f73b 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -4903,6 +4903,13 @@ class InitListExpr : public Expr {
/// has been set.
bool hasArrayFiller() const { return getArrayFiller(); }
+ // Determine whether this initializer list contains a designated initializer.
+ bool hasDesignatedInit() const {
+ return std::any_of(begin(), end(), [](const Stmt *S) {
+ return isa<DesignatedInitExpr>(S);
+ });
+ }
+
/// If this initializes a union, specifies which field in the
/// union to initialize.
///
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c852aeeb401ff..379554baaaa1f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2190,6 +2190,8 @@ def err_reference_has_multiple_inits : Error<
"reference cannot be initialized with multiple values">;
def err_init_non_aggr_init_list : Error<
"initialization of non-aggregate type %0 with an initializer list">;
+def err_designated_init_for_non_aggregate : Error<
+ "initialization of non-aggregate type %0 with a designated initializer list">;
def err_init_reference_member_uninitialized : Error<
"reference member of type %0 uninitialized">;
def note_uninit_reference_member : Note<
diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h
index e5a98ba97f4f1..fcfb56f9731ef 100644
--- a/clang/include/clang/Sema/Initialization.h
+++ b/clang/include/clang/Sema/Initialization.h
@@ -1107,6 +1107,9 @@ class InitializationSequence {
/// Parenthesized list initialization failed at some point.
/// This is a C++20 feature.
FK_ParenthesizedListInitFailed,
+
+ // A designated initializer was provided for a non-aggregate type.
+ FK_DesignatedInitForNonAggregate,
};
private:
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 4b62d9cea9a99..5fe417fa6351d 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -3595,6 +3595,7 @@ bool InitializationSequence::isAmbiguous() const {
case FK_ExplicitConstructor:
case FK_AddressOfUnaddressableFunction:
case FK_ParenthesizedListInitFailed:
+ case FK_DesignatedInitForNonAggregate:
return false;
case FK_ReferenceInitOverloadFailed:
@@ -4432,6 +4433,22 @@ static void TryListInitialization(Sema &S,
return;
}
+ // C++20 [dcl.init.list]p3:
+ // - If the braced-init-list contains a designated-initializer-list, T shall
+ // be an aggregate class. [...] Aggregate initialization is performed.
+ //
+ // We allow arrays here too in order to support array designators.
+ //
+ // FIXME: This check should precede the handling of reference initialization.
+ // We follow other compilers in allowing things like 'Aggr &&a = {.x = 1};'
+ // as a tentative DR resolution.
+ bool IsDesignatedInit = InitList->hasDesignatedInit();
+ if (!DestType->isAggregateType() && IsDesignatedInit) {
+ Sequence.SetFailed(
+ InitializationSequence::FK_DesignatedInitForNonAggregate);
+ return;
+ }
+
// C++11 [dcl.init.list]p3, per DR1467:
// - If T is a class type and the initializer list has a single element of
// type cv U, where U is T or a class derived from T, the object is
@@ -4443,7 +4460,8 @@ static void TryListInitialization(Sema &S,
// (8.5.2 [dcl.init.string]), initialization is performed as described
// in that section.
// - Otherwise, if T is an aggregate, [...] (continue below).
- if (S.getLangOpts().CPlusPlus11 && InitList->getNumInits() == 1) {
+ if (S.getLangOpts().CPlusPlus11 && InitList->getNumInits() == 1 &&
+ !IsDesignatedInit) {
if (DestType->isRecordType()) {
QualType InitType = InitList->getInit(0)->getType();
if (S.Context.hasSameUnqualifiedType(InitType, DestType) ||
@@ -4485,7 +4503,7 @@ static void TryListInitialization(Sema &S,
// - If T is an aggregate, aggregate initialization is performed.
if ((DestType->isRecordType() && !DestType->isAggregateType()) ||
(S.getLangOpts().CPlusPlus11 &&
- S.isStdInitializerList(DestType, nullptr))) {
+ S.isStdInitializerList(DestType, nullptr) && !IsDesignatedInit)) {
if (S.getLangOpts().CPlusPlus11) {
// - Otherwise, if the initializer list has no elements and T is a
// class type with a default constructor, the object is
@@ -9794,6 +9812,12 @@ bool InitializationSequence::Diagnose(Sema &S,
TryOrBuildParenListInitialization(S, Entity, Kind, Args, *this,
/*VerifyOnly=*/false);
break;
+
+ case FK_DesignatedInitForNonAggregate:
+ InitListExpr *InitList = cast<InitListExpr>(Args[0]);
+ S.Diag(Kind.getLocation(), diag::err_designated_init_for_non_aggregate)
+ << Entity.getType() << InitList->getSourceRange();
+ break;
}
PrintInitLocationNote(S, Entity);
@@ -9964,6 +9988,10 @@ void InitializationSequence::dump(raw_ostream &OS) const {
case FK_ParenthesizedListInitFailed:
OS << "parenthesized list initialization failed";
break;
+
+ case FK_DesignatedInitForNonAggregate:
+ OS << "designated initializer for non-aggregate type";
+ break;
}
OS << '\n';
return;
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 5cbd9e8a46234..aa40bff4c575d 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5130,6 +5130,18 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
if (!S.isCompleteType(From->getBeginLoc(), InitTy))
return Result;
+ // C++20 [over.ics.list]/2:
+ // If the initializer list is a designated-initializer-list, a conversion
+ // is only possible if the parameter has an aggregate type
+ //
+ // FIXME: The exception for reference initialization here is not part of the
+ // language rules, but follow other compilers in adding it as a tentative DR
+ // resolution.
+ bool IsDesignatedInit = From->hasDesignatedInit();
+ if (!ToType->isAggregateType() && !ToType->isReferenceType() &&
+ IsDesignatedInit)
+ return Result;
+
// Per DR1467:
// If the parameter type is a class X and the initializer list has a single
// element of type cv U, where U is X or a class derived from X, the
@@ -5140,7 +5152,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
// and the initializer list has a single element that is an
// appropriately-typed string literal (8.5.2 [dcl.init.string]), the
// implicit conversion sequence is the identity conversion.
- if (From->getNumInits() == 1) {
+ if (From->getNumInits() == 1 && !IsDesignatedInit) {
if (ToType->isRecordType()) {
QualType InitType = From->getInit(0)->getType();
if (S.Context.hasSameUnqualifiedType(InitType, ToType) ||
@@ -5178,7 +5190,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
// default-constructible, and if all the elements of the initializer list
// can be implicitly converted to X, the implicit conversion sequence is
// the worst conversion necessary to convert an element of the list to X.
- if (AT || S.isStdInitializerList(ToType, &InitTy)) {
+ if ((AT || S.isStdInitializerList(ToType, &InitTy)) && !IsDesignatedInit) {
unsigned e = From->getNumInits();
ImplicitConversionSequence DfltElt;
DfltElt.setBad(BadConversionSequence::no_conversion, QualType(),
@@ -5320,7 +5332,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
// If the initializer list has a single element that is reference-related
// to the parameter type, we initialize the reference from that.
- if (From->getNumInits() == 1) {
+ if (From->getNumInits() == 1 && !IsDesignatedInit) {
Expr *Init = From->getInit(0);
QualType T2 = Init->getType();
diff --git a/clang/test/Sema/designated-initializers.c b/clang/test/Sema/designated-initializers.c
index e039f18364639..31a3380b5db7d 100644
--- a/clang/test/Sema/designated-initializers.c
+++ b/clang/test/Sema/designated-initializers.c
@@ -70,7 +70,7 @@ struct rect windows[] = {
int windows_size[((sizeof(windows) / sizeof(struct rect)) == 6)? 1 : -1];
struct rect windows_bad[3] = {
- [2].top_left = { { .x = 1.1 } }, // expected-error{{designator in initializer for scalar type}}
+ [2].top_left = { { .x = 1.1 } }, // expected-error{{initialization of non-aggregate type 'double' with a designated initializer list}}
[1].top_left = { .x = 1.1 }
};
diff --git a/clang/test/Sema/sizeless-1.c b/clang/test/Sema/sizeless-1.c
index 8ef30a12f1128..bbc36e32181f1 100644
--- a/clang/test/Sema/sizeless-1.c
+++ b/clang/test/Sema/sizeless-1.c
@@ -89,7 +89,7 @@ void func(int sel) {
svint8_t bad_brace_init_int8_1 = {local_int8, 0}; // expected-warning {{excess elements in initializer for indivisible sizeless type 'svint8_t'}}
svint8_t bad_brace_init_int8_2 = {0}; // expected-error {{incompatible type 'int'}}
svint8_t bad_brace_init_int8_3 = {local_int16}; // expected-error {{incompatible type 'svint16_t'}}
- svint8_t bad_brace_init_int8_4 = {[0] = local_int8}; // expected-error {{designator in initializer for indivisible sizeless type 'svint8_t'}}
+ svint8_t bad_brace_init_int8_4 = {[0] = local_int8}; // expected-error {{initialization of non-aggregate type 'svint8_t' (aka '__SVInt8_t') with a designated initializer list}}
svint8_t bad_brace_init_int8_5 = {{local_int8}}; // expected-warning {{too many braces around initializer}}
svint8_t bad_brace_init_int8_6 = {{local_int8, 0}}; // expected-warning {{too many braces around initializer}}
diff --git a/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp b/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp
index c6339116af8bb..3cc3f9cf6dff4 100644
--- a/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp
+++ b/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp
@@ -156,3 +156,24 @@ namespace deduction {
j3<D, E>({}); // ok, selects E overload by SFINAE (too many braces for D)
}
}
+
+namespace no_unwrap {
+ template<typename T> struct X {
+ static_assert(false, "should not be instantiated");
+ };
+ struct Q {
+ template<typename T, typename U = typename X<T>::type> Q(T&&);
+ };
+
+ // Ensure that we do not try to call 'Q::Q(.a = 1)' here.
+ void g() { Q q = {.a = 1}; } // expected-error {{initialization of non-aggregate type 'Q' with a designated initializer list}}
+
+ struct S { int a; };
+ void h(Q q);
+ void h(S s);
+
+ // OK, does not instantiate X<void&> (!).
+ void i() {
+ h({.a = 1});
+ }
+}
diff --git a/clang/test/SemaCXX/sizeless-1.cpp b/clang/test/SemaCXX/sizeless-1.cpp
index 6c8c873f494f7..ed0416f17995b 100644
--- a/clang/test/SemaCXX/sizeless-1.cpp
+++ b/clang/test/SemaCXX/sizeless-1.cpp
@@ -103,7 +103,7 @@ void func(int sel) {
svint8_t bad_brace_init_int8_1 = {local_int8, 0}; // expected-error {{excess elements in initializer for indivisible sizeless type 'svint8_t'}}
svint8_t bad_brace_init_int8_2 = {0}; // expected-error {{rvalue of type 'int'}}
svint8_t bad_brace_init_int8_3 = {local_int16}; // expected-error {{lvalue of type 'svint16_t'}}
- svint8_t bad_brace_init_int8_4 = {[0] = local_int8}; // expected-error {{designator in initializer for indivisible sizeless type 'svint8_t'}} expected-warning {{array designators are a C99 extension}}
+ svint8_t bad_brace_init_int8_4 = {[0] = local_int8}; // expected-error-re {{initialization of non-aggregate type 'svint8_t'{{.*}} with a designated initializer list}} expected-warning {{array designators are a C99 extension}}
svint8_t bad_brace_init_int8_5 = {{local_int8}}; // expected-warning {{too many braces around initializer}}
svint8_t bad_brace_init_int8_6 = {{local_int8, 0}}; // expected-warning {{too many braces around initializer}}
@@ -422,7 +422,7 @@ void cxx_only(int sel) {
svint8_t bad_brace_init_int8_1{local_int8, 0}; // expected-error {{excess elements in initializer for indivisible sizeless type 'svint8_t'}}
svint8_t bad_brace_init_int8_2{0}; // expected-error {{rvalue of type 'int'}}
svint8_t bad_brace_init_int8_3{local_int16}; // expected-error {{lvalue of type 'svint16_t'}}
- svint8_t bad_brace_init_int8_4{[0] = local_int8}; // expected-error {{designator in initializer for indivisible sizeless type 'svint8_t'}} expected-warning {{array designators are a C99 extension}}
+ svint8_t bad_brace_init_int8_4{[0] = local_int8}; // expected-error-re {{initialization of non-aggregate type 'svint8_t'{{.*}} with a designated initializer list}} expected-warning {{array designators are a C99 extension}}
svint8_t bad_brace_init_int8_5{{local_int8}}; // expected-warning {{too many braces around initializer}}
svint8_t bad_brace_init_int8_6{{local_int8, 0}}; // expected-warning {{too many braces around initializer}}
svint8_t wrapper_init_int8{wrapper<svint8_t>()};
More information about the cfe-commits
mailing list