Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ Bug Fixes to C++ Support
- Fixed an assertion failure affecting code that uses C++23 "deducing this". (#GH130272)
- Clang now properly instantiates destructors for initialized members within non-delegating constructors. (#GH93251)
- Correctly diagnoses if unresolved using declarations shadows template paramters (#GH129411)
- Fixed C++20 aggregate initialization rules being incorrectly applied in certain contexts. (#GH131320)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
106 changes: 65 additions & 41 deletions clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4633,6 +4633,59 @@ static void TryConstructorInitialization(Sema &S,
IsListInit | IsInitListCopy, AsInitializerList);
}

static void TryOrBuildParenListInitialization(
Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind,
ArrayRef<Expr *> Args, InitializationSequence &Sequence, bool VerifyOnly,
ExprResult *Result = nullptr);

/// Attempt to initialize an object of a class type either by
/// direct-initialization, or by copy-initialization from an
/// expression of the same or derived class type. This corresponds
/// to the first two sub-bullets of C++2c [dcl.init.general] p16.6.
///
/// \param IsAggrListInit Is this non-list-initialization being done as
/// part of a list-initialization of an aggregate
/// from a single expression of the same or
/// derived class type (C++2c [dcl.init.list] p3.2)?
static void TryConstructorOrParenListInitialization(
Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind,
MultiExprArg Args, QualType DestType, InitializationSequence &Sequence,
bool IsAggrListInit) {
// C++2c [dcl.init.general] p16.6:
// * Otherwise, if the destination type is a class type:
// * If the initializer expression is a prvalue and
// the cv-unqualified version of the source type is the same
// as the destination type, the initializer expression is used
// to initialize the destination object.
// * Otherwise, if the initialization is direct-initialization,
// or if it is copy-initialization where the cv-unqualified
// version of the source type is the same as or is derived from
// the class of the destination type, constructors are considered.
// The applicable constructors are enumerated, and the best one
// is chosen through overload resolution. Then:
// * If overload resolution is successful, the selected
// constructor is called to initialize the object, with
// the initializer expression or expression-list as its
// argument(s).
TryConstructorInitialization(S, Entity, Kind, Args, DestType, DestType,
Sequence, /*IsListInit=*/false, IsAggrListInit);

// * Otherwise, if no constructor is viable, the destination type
// is an aggregate class, and the initializer is a parenthesized
// expression-list, the object is initialized as follows. [...]
// Parenthesized initialization of aggregates is a C++20 feature.
if (S.getLangOpts().CPlusPlus20 &&
Kind.getKind() == InitializationKind::IK_Direct && Sequence.Failed() &&
Sequence.getFailureKind() ==
InitializationSequence::FK_ConstructorOverloadFailed &&
Sequence.getFailedOverloadResult() == OR_No_Viable_Function &&
(IsAggrListInit || DestType->isAggregateType()))
TryOrBuildParenListInitialization(S, Entity, Kind, Args, Sequence,
/*VerifyOnly=*/true);

// * Otherwise, the initialization is ill-formed.
}

static bool
ResolveOverloadedFunctionForReferenceBinding(Sema &S,
Expr *Initializer,
Expand Down Expand Up @@ -4846,11 +4899,16 @@ static void TryListInitialization(Sema &S,
QualType InitType = InitList->getInit(0)->getType();
if (S.Context.hasSameUnqualifiedType(InitType, DestType) ||
S.IsDerivedFrom(InitList->getBeginLoc(), InitType, DestType)) {
InitializationKind SubKind =
Kind.getKind() == InitializationKind::IK_DirectList
? InitializationKind::CreateDirect(Kind.getLocation(),
InitList->getLBraceLoc(),
InitList->getRBraceLoc())
: Kind;
Expr *InitListAsExpr = InitList;
TryConstructorInitialization(S, Entity, Kind, InitListAsExpr, DestType,
DestType, Sequence,
/*InitListSyntax*/false,
/*IsInitListCopy*/true);
TryConstructorOrParenListInitialization(
S, Entity, SubKind, InitListAsExpr, DestType, Sequence,
/*IsAggrListInit=*/true);
return;
}
}
Expand Down Expand Up @@ -5709,7 +5767,7 @@ static void TryDefaultInitialization(Sema &S,
static void TryOrBuildParenListInitialization(
Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind,
ArrayRef<Expr *> Args, InitializationSequence &Sequence, bool VerifyOnly,
ExprResult *Result = nullptr) {
ExprResult *Result) {
unsigned EntityIndexToProcess = 0;
SmallVector<Expr *, 4> InitExprs;
QualType ResultType;
Expand Down Expand Up @@ -6688,42 +6746,8 @@ void InitializationSequence::InitializeFrom(Sema &S,
(Context.hasSameUnqualifiedType(SourceType, DestType) ||
(Initializer && S.IsDerivedFrom(Initializer->getBeginLoc(),
SourceType, DestType))))) {
TryConstructorInitialization(S, Entity, Kind, Args, DestType, DestType,
*this);

// We fall back to the "no matching constructor" path if the
// failed candidate set has functions other than the three default
// constructors. For example, conversion function.
if (const auto *RD =
dyn_cast<CXXRecordDecl>(DestType->getAs<RecordType>()->getDecl());
// In general, we should call isCompleteType for RD to check its
// completeness, we don't call it here as it was already called in the
// above TryConstructorInitialization.
S.getLangOpts().CPlusPlus20 && RD && RD->hasDefinition() &&
RD->isAggregate() && Failed() &&
getFailureKind() == FK_ConstructorOverloadFailed) {
// Do not attempt paren list initialization if overload resolution
// resolves to a deleted function .
//
// We may reach this condition if we have a union wrapping a class with
// a non-trivial copy or move constructor and we call one of those two
// constructors. The union is an aggregate, but the matched constructor
// is implicitly deleted, so we need to prevent aggregate initialization
// (otherwise, it'll attempt aggregate initialization by initializing
// the first element with a reference to the union).
OverloadCandidateSet::iterator Best;
OverloadingResult OR = getFailedCandidateSet().BestViableFunction(
S, Kind.getLocation(), Best);
if (OR != OverloadingResult::OR_Deleted) {
// C++20 [dcl.init] 17.6.2.2:
// - Otherwise, if no constructor is viable, the destination type is
// an
// aggregate class, and the initializer is a parenthesized
// expression-list.
TryOrBuildParenListInitialization(S, Entity, Kind, Args, *this,
/*VerifyOnly=*/true);
}
}
TryConstructorOrParenListInitialization(S, Entity, Kind, Args, DestType,
*this, /*IsAggrListInit=*/false);
} else {
// - Otherwise (i.e., for the remaining copy-initialization cases),
// user-defined conversion sequences that can convert from the
Expand Down
132 changes: 117 additions & 15 deletions clang/test/CXX/dcl.decl/dcl.init/dcl.init.general/p16-cxx20.cpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,120 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s

// If the initializer is (), the object is value-initialized.

// expected-no-diagnostics
namespace GH69890 {
struct A {
constexpr A() {}
int x;
};

struct B : A {
int y;
};

static_assert(B().x == 0);
static_assert(B().y == 0);
}
// If the initializer is (), the object is value-initialized.
struct A {
constexpr A() {}
int x;
};

struct B : A {
int y;
};

static_assert(B().x == 0);
static_assert(B().y == 0);
} // namespace GH69890

namespace P0960R3 {
struct A { // expected-note 22 {{candidate constructor}}
int i;
operator int() volatile;
};
volatile A va;

A a1(va);
A a2 = va; // expected-error {{no matching constructor for initialization of 'A'}}
A a3 {va};
A a4 = {va}; // expected-error {{no matching constructor for initialization of 'A'}}

A f() {
return va; // expected-error {{no matching constructor for initialization of 'A'}}
return {va}; // expected-error {{no matching constructor for initialization of 'A'}}
}

int g(A); // expected-note 2 {{passing argument to parameter here}}
int i = g(va); // expected-error {{no matching constructor for initialization of 'A'}}
int j = g({va}); // expected-error {{no matching constructor for initialization of 'A'}}

struct Ambig {
operator const A&(); // expected-note {{candidate function}}
operator A&&(); // expected-note {{candidate function}}
operator int();
};

A a5(Ambig {}); // expected-error {{call to constructor of 'A' is ambiguous}}
A a6 = Ambig {}; // expected-error {{conversion from 'Ambig' to 'A' is ambiguous}}
A a7 {Ambig {}};
A a8 = {Ambig {}};

A a9(1);
A a10 = 1; // expected-error {{no viable conversion from 'int' to 'A'}}
A a11 {1};
A a12 = {1};


struct B { // expected-note 12 {{candidate constructor}}
int i;
virtual operator int() volatile;
};
volatile B vb;

B b1(vb); // expected-error {{no matching constructor for initialization of 'B'}}
B b2 = vb; // expected-error {{no matching constructor for initialization of 'B'}}
B b3 {vb}; // expected-error {{no matching constructor for initialization of 'B'}}
B b4 = {vb}; // expected-error {{no matching constructor for initialization of 'B'}}


struct Immovable {
Immovable();
Immovable(const Immovable&) = delete; // #Imm_copy
};

struct C { // #C
int i;
Immovable j; // #C_j

operator int() volatile;
};
C c;
volatile C vc;

C c1(c); // expected-error {{call to implicitly-deleted copy constructor of 'C'}}
C c2 = c; // expected-error {{call to implicitly-deleted copy constructor of 'C'}}
C c3 {c}; // expected-error {{call to implicitly-deleted copy constructor of 'C'}}
C c4 = {c}; // expected-error {{call to implicitly-deleted copy constructor of 'C'}}
// expected-note@#C_j 4 {{copy constructor of 'C' is implicitly deleted}}
// expected-note@#Imm_copy 4 {{'Immovable' has been explicitly marked deleted here}}

C c5(vc);
C c6 = vc; // expected-error {{no matching constructor for initialization of 'C'}}
C c7 {vc};
C c8 = {vc}; // expected-error {{no matching constructor for initialization of 'C'}}
// expected-note@#C 4 {{candidate constructor}}

C c9(C {});
C c10 = C(123);
C c11 {C {0, Immovable()}};
C c12 = {C()};


struct D { // expected-note 6 {{candidate constructor}}
int i;
};

struct DD : private D { // expected-note 4 {{declared private here}}
virtual operator int() volatile;
};
DD dd;
volatile DD vdd;

D d1(dd); // expected-error {{cannot cast 'const DD' to its private base class 'const D'}}
D d2 = dd; // expected-error {{cannot cast 'const DD' to its private base class 'const D'}}
D d3 {dd}; // expected-error {{cannot cast 'const DD' to its private base class 'const D'}}
D d4 = {dd}; // expected-error {{cannot cast 'const DD' to its private base class 'const D'}}

D d5(vdd);
D d6 = vdd; // expected-error {{no matching constructor for initialization of 'D'}}
D d7 {vdd};
D d8 = {vdd}; // expected-error {{no matching constructor for initialization of 'D'}}
} // namespace P0960R3
Loading