Index: clang/include/clang/AST/Decl.h =================================================================== --- clang/include/clang/AST/Decl.h +++ clang/include/clang/AST/Decl.h @@ -887,7 +887,10 @@ CallInit, /// Direct list-initialization (C++11) - ListInit + ListInit, + + /// Parenthesized list-initialization (C++20) + ParenListInit }; /// Kinds of thread-local storage. Index: clang/include/clang/AST/ExprCXX.h =================================================================== --- clang/include/clang/AST/ExprCXX.h +++ clang/include/clang/AST/ExprCXX.h @@ -4680,6 +4680,86 @@ } }; +/// Represents a list-initialization with parenthesis. +/// +/// As per P0960R3, this is a C++20 feature that allows aggregate to +/// be initialized with a parenthesized list of values: +/// ``` +/// struct A { +/// int a; +/// double b; +/// }; +/// +/// void foo() { +/// A a1(0); // legal in C++20 +/// A a2(1.5, 1.0); // legal in C++20 +/// } +/// ``` +/// It has some sort of similiarity to braced +/// list-initialization, with some differences such as +/// it allows narrowing conversion whilst braced +/// list-initialization doesn't. +/// ``` +/// struct A { +/// char a; +/// }; +/// void foo() { +/// A a(1.5); // legal in C++20 +/// A b{1.5}; // illegal ! +/// } +/// ``` +class CXXParenListInitExpr final + : public Expr, + private llvm::TrailingObjects { + friend class TrailingObjects; + + unsigned NumExprs; + SourceLocation Loc; + + CXXParenListInitExpr(ArrayRef Args, QualType T, SourceLocation Loc) + : Expr(CXXParenListInitExprClass, T, + T->isLValueReferenceType() ? VK_LValue + : T->isRValueReferenceType() ? VK_PRValue + : VK_XValue, + OK_Ordinary), + NumExprs(Args.size()), Loc(Loc) { + std::copy(Args.begin(), Args.end(), getTrailingObjects()); + } + + size_t numTrailingObjects(OverloadToken) const { return NumExprs; } + +public: + static CXXParenListInitExpr *Create(ASTContext &C, ArrayRef Args, + QualType T, SourceLocation Loc); + + ArrayRef getInitExprs() { + return {getTrailingObjects(), NumExprs}; + } + + SourceLocation getBeginLoc() const LLVM_READONLY { return Loc; } + + SourceLocation getEndLoc() const LLVM_READONLY { return Loc; } + + SourceRange getSourceRange() const LLVM_READONLY { + return SourceRange(getBeginLoc(), getEndLoc()); + } + + child_range children() { + Stmt **Begin = reinterpret_cast(getTrailingObjects()); + return child_range(Begin, Begin + NumExprs); + } + + const_child_range children() const { + Stmt *const *Begin = + reinterpret_cast(getTrailingObjects()); + return const_child_range(Begin, Begin + NumExprs); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXParenListInitExprClass; + } +}; + /// Represents an expression that might suspend coroutine execution; /// either a co_await or co_yield expression. /// Index: clang/include/clang/AST/RecursiveASTVisitor.h =================================================================== --- clang/include/clang/AST/RecursiveASTVisitor.h +++ clang/include/clang/AST/RecursiveASTVisitor.h @@ -2855,6 +2855,7 @@ DEF_TRAVERSE_STMT(FunctionParmPackExpr, {}) DEF_TRAVERSE_STMT(CXXFoldExpr, {}) DEF_TRAVERSE_STMT(AtomicExpr, {}) +DEF_TRAVERSE_STMT(CXXParenListInitExpr, {}) DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, { if (S->getLifetimeExtendedTemporaryDecl()) { Index: clang/include/clang/Basic/StmtNodes.td =================================================================== --- clang/include/clang/Basic/StmtNodes.td +++ clang/include/clang/Basic/StmtNodes.td @@ -160,6 +160,7 @@ def MaterializeTemporaryExpr : StmtNode; def LambdaExpr : StmtNode; def CXXFoldExpr : StmtNode; +def CXXParenListInitExpr: StmtNode; // C++ Coroutines TS expressions def CoroutineSuspendExpr : StmtNode; Index: clang/include/clang/Sema/Initialization.h =================================================================== --- clang/include/clang/Sema/Initialization.h +++ clang/include/clang/Sema/Initialization.h @@ -923,7 +923,11 @@ SK_OCLSamplerInit, /// Initialize an opaque OpenCL type (event_t, queue_t, etc.) with zero - SK_OCLZeroOpaqueType + SK_OCLZeroOpaqueType, + + /// Initialize an aggreagate with parenthesized list of values. + /// This is a C++20 feature. + SK_ParenthesizedListInit }; /// A single step in the initialization sequence. @@ -1099,6 +1103,10 @@ /// List-copy-initialization chose an explicit constructor. FK_ExplicitConstructor, + + /// Parenthesized list initialization failed at some point. + /// This is a C++20 feature. + FK_ParenthesizedListInitFailed, }; private: @@ -1357,6 +1365,8 @@ /// from a zero constant. void AddOCLZeroOpaqueTypeStep(QualType T); + void AddParenthesizedListInitStep(QualType T); + /// Add steps to unwrap a initializer list for a reference around a /// single element and rewrap it at the end. void RewrapReferenceInitList(QualType T, InitListExpr *Syntactic); Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ clang/lib/AST/Expr.cpp @@ -3562,6 +3562,7 @@ case ShuffleVectorExprClass: case ConvertVectorExprClass: case AsTypeExprClass: + case CXXParenListInitExprClass: // These have a side-effect if any subexpression does. break; Index: clang/lib/AST/ExprCXX.cpp =================================================================== --- clang/lib/AST/ExprCXX.cpp +++ clang/lib/AST/ExprCXX.cpp @@ -1746,3 +1746,11 @@ alignof(CUDAKernelCallExpr)); return new (Mem) CUDAKernelCallExpr(NumArgs, HasFPFeatures, Empty); } + +CXXParenListInitExpr *CXXParenListInitExpr::Create(ASTContext &C, + ArrayRef Args, + QualType T, + SourceLocation Loc) { + void *Mem = C.Allocate(totalSizeToAlloc(Args.size())); + return new (Mem) CXXParenListInitExpr(Args, T, Loc); +} Index: clang/lib/AST/ExprClassification.cpp =================================================================== --- clang/lib/AST/ExprClassification.cpp +++ clang/lib/AST/ExprClassification.cpp @@ -442,6 +442,10 @@ case Expr::SYCLUniqueStableNameExprClass: return Cl::CL_PRValue; break; + + case Expr::CXXParenListInitExprClass: + // FIXME: unimplemented + llvm_unreachable("unimplemented"); } llvm_unreachable("unhandled expression kind in classification"); Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -15475,6 +15475,7 @@ case Expr::DependentCoawaitExprClass: case Expr::CoyieldExprClass: case Expr::SYCLUniqueStableNameExprClass: + case Expr::CXXParenListInitExprClass: return ICEDiag(IK_NotICE, E->getBeginLoc()); case Expr::InitListExprClass: { Index: clang/lib/AST/ItaniumMangle.cpp =================================================================== --- clang/lib/AST/ItaniumMangle.cpp +++ clang/lib/AST/ItaniumMangle.cpp @@ -5126,6 +5126,9 @@ Out << "E"; break; } + case Expr::CXXParenListInitExprClass: + // FIXME: unimplemented + llvm_unreachable("unimplemented"); } if (AsTemplateArg && !IsPrimaryExpr) Index: clang/lib/AST/JSONNodeDumper.cpp =================================================================== --- clang/lib/AST/JSONNodeDumper.cpp +++ clang/lib/AST/JSONNodeDumper.cpp @@ -833,6 +833,9 @@ case VarDecl::CInit: JOS.attribute("init", "c"); break; case VarDecl::CallInit: JOS.attribute("init", "call"); break; case VarDecl::ListInit: JOS.attribute("init", "list"); break; + case VarDecl::ParenListInit: + JOS.attribute("init", "paren-list"); + break; } } attributeOnlyIfTrue("isParameterPack", VD->isParameterPack()); Index: clang/lib/AST/StmtPrinter.cpp =================================================================== --- clang/lib/AST/StmtPrinter.cpp +++ clang/lib/AST/StmtPrinter.cpp @@ -2456,6 +2456,11 @@ OS << ")"; } +void StmtPrinter::VisitCXXParenListInitExpr(CXXParenListInitExpr *Node) { + // FIXME: unimplemented + llvm_unreachable("unimplemented"); +} + void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) { NestedNameSpecifierLoc NNS = E->getNestedNameSpecifierLoc(); if (NNS) Index: clang/lib/AST/StmtProfile.cpp =================================================================== --- clang/lib/AST/StmtProfile.cpp +++ clang/lib/AST/StmtProfile.cpp @@ -2181,6 +2181,11 @@ ID.AddInteger(S->getOperator()); } +void StmtProfiler::VisitCXXParenListInitExpr(const CXXParenListInitExpr *S) { + // FIXME: unimplemented + llvm_unreachable("unimplemented"); +} + void StmtProfiler::VisitCoroutineBodyStmt(const CoroutineBodyStmt *S) { VisitStmt(S); } Index: clang/lib/AST/TextNodeDumper.cpp =================================================================== --- clang/lib/AST/TextNodeDumper.cpp +++ clang/lib/AST/TextNodeDumper.cpp @@ -1792,6 +1792,8 @@ case VarDecl::ListInit: OS << " listinit"; break; + case VarDecl::ParenListInit: + OS << " parenlistinit"; } } if (D->needsDestruction(D->getASTContext())) Index: clang/lib/CodeGen/CGExprAgg.cpp =================================================================== --- clang/lib/CodeGen/CGExprAgg.cpp +++ clang/lib/CodeGen/CGExprAgg.cpp @@ -205,6 +205,7 @@ } void VisitVAArgExpr(VAArgExpr *E); + void VisitCXXParenListInitExpr(CXXParenListInitExpr *E); void EmitInitializationToLValue(Expr *E, LValue Address); void EmitNullInitializationToLValue(LValue Address); @@ -1591,6 +1592,49 @@ } } +void AggExprEmitter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) { + + ArrayRef InitExprs = E->getInitExprs(); + AggValueSlot Dest = EnsureSlot(E->getType()); + + if (const ArrayType *AT = dyn_cast(E->getType())) { + for (auto Pair : llvm::enumerate(InitExprs)) { + // Initialization for every element of the array. + Address V = Builder.CreateConstArrayGEP(Dest.getAddress(), Pair.index()); + LValue LV = CGF.MakeAddrLValue(V, AT->getElementType()); + EmitInitializationToLValue(Pair.value(), LV); + } + } else if (CXXRecordDecl *RD = dyn_cast( + E->getType()->castAs()->getDecl())) { + + unsigned Index = 0; + LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType()); + + // Initialize the base classes first. + for (auto &Base : RD->bases()) { + if (Index < InitExprs.size()) { + const CXXRecordDecl *BaseRD = Base.getType()->getAsCXXRecordDecl(); + Address V = CGF.GetAddressOfDirectBaseInCompleteClass( + Dest.getAddress(), RD, BaseRD, + /*isBaseVirtual*/ false); + AggValueSlot AggSlot = AggValueSlot::forAddr( + V, Qualifiers(), AggValueSlot::IsDestructed, + AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsNotAliased, + CGF.getOverlapForBaseInit(RD, BaseRD, Base.isVirtual())); + CGF.EmitAggExpr(InitExprs[Index++], AggSlot); + } + } + + // Then initialize the fields. + for (const FieldDecl *F : RD->fields()) { + if (Index < InitExprs.size()) { + LValue LV = CGF.EmitLValueForFieldInitialization(DestLV, F); + EmitInitializationToLValue(InitExprs[Index++], LV); + } + } + } +} + void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { #if 0 // FIXME: Assess perf here? Figure out what cases are worth optimizing here Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -12838,6 +12838,7 @@ // Perform the initialization. ParenListExpr *CXXDirectInit = dyn_cast(Init); + bool IsParenListInit = false; if (!VDecl->isInvalidDecl()) { InitializedEntity Entity = InitializedEntity::InitializeVariable(VDecl); InitializationKind Kind = InitializationKind::CreateForInit( @@ -12880,6 +12881,8 @@ } Init = Result.getAs(); + IsParenListInit = InitSeq.step_begin()->Kind == + InitializationSequence::SK_ParenthesizedListInit; } // Check for self-references within variable initializers. @@ -13128,7 +13131,8 @@ // class type. if (CXXDirectInit) { assert(DirectInit && "Call-style initializer must be direct init."); - VDecl->setInitStyle(VarDecl::CallInit); + VDecl->setInitStyle(IsParenListInit ? VarDecl::ParenListInit + : VarDecl::CallInit); } else if (DirectInit) { // This must be list-initialization. No other way is direct-initialization. VDecl->setInitStyle(VarDecl::ListInit); Index: clang/lib/Sema/SemaExceptionSpec.cpp =================================================================== --- clang/lib/Sema/SemaExceptionSpec.cpp +++ clang/lib/Sema/SemaExceptionSpec.cpp @@ -1411,6 +1411,10 @@ case Expr::MSPropertySubscriptExprClass: llvm_unreachable("Invalid class for expression"); + case Expr::CXXParenListInitExprClass: + // FIXME: unimplemented + llvm_unreachable("unimplemented"); + // Most statements can throw if any substatement can throw. case Stmt::AttributedStmtClass: case Stmt::BreakStmtClass: Index: clang/lib/Sema/SemaInit.cpp =================================================================== --- clang/lib/Sema/SemaInit.cpp +++ clang/lib/Sema/SemaInit.cpp @@ -3519,11 +3519,13 @@ case SK_StdInitializerListConstructorCall: case SK_OCLSamplerInit: case SK_OCLZeroOpaqueType: + case SK_ParenthesizedListInit: break; case SK_ConversionSequence: case SK_ConversionSequenceNoNarrowing: delete ICS; + break; } } @@ -3578,6 +3580,7 @@ case FK_PlaceholderType: case FK_ExplicitConstructor: case FK_AddressOfUnaddressableFunction: + case FK_ParenthesizedListInitFailed: return false; case FK_ReferenceInitOverloadFailed: @@ -3813,6 +3816,13 @@ Steps.push_back(S); } +void InitializationSequence::AddParenthesizedListInitStep(QualType T) { + Step S; + S.Kind = SK_ParenthesizedListInit; + S.Type = T; + Steps.push_back(S); +} + void InitializationSequence::RewrapReferenceInitList(QualType T, InitListExpr *Syntactic) { assert(Syntactic->getNumInits() == 1 && @@ -4056,6 +4066,110 @@ return CandidateSet.BestViableFunction(S, DeclLoc, Best); } +static void TryOrBuildParenListInitialization( + Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, + ArrayRef Args, InitializationSequence &Sequence, bool VerifyOnly, + ExprResult *Result = nullptr) { + + unsigned Index = 0; + SmallVector InitExprs; + QualType ResultType; + + auto InitHelper = [&](auto Range) -> bool { + for (InitializedEntity SubEntity : Range) { + if (Index == Args.size()) + // All the initializers are consumed, time to leave. + return true; + + Expr *E = Args[Index++]; + InitializationKind SubKind = InitializationKind::CreateForInit( + E->getExprLoc(), /*isDirectInit*/ false, E); + InitializationSequence Seq(S, SubEntity, SubKind, E); + + if (Seq.Failed()) { + if (!VerifyOnly) + Seq.Diagnose(S, SubEntity, SubKind, E); + else + Sequence.SetFailed( + InitializationSequence::FK_ParenthesizedListInitFailed); + + return false; + } + if (!VerifyOnly) { + ExprResult ER = Seq.Perform(S, SubEntity, SubKind, E); + InitExprs.push_back(ER.get()); + } + } + return true; + }; + + if (const ArrayType *AT = + S.getASTContext().getAsArrayType(Entity.getType())) { + + SmallVector ElementEntities; + uint64_t ArrayLength; + if (const ConstantArrayType *CAT = + S.getASTContext().getAsConstantArrayType(Entity.getType())) + ArrayLength = CAT->getSize().getZExtValue(); + else + ArrayLength = Args.size(); + + if (ArrayLength >= Args.size()) { + for (uint64_t I = 0; I < ArrayLength; ++I) + ElementEntities.push_back( + InitializedEntity::InitializeElement(S.getASTContext(), I, Entity)); + + if (!InitHelper(ElementEntities)) + return; + + ResultType = S.Context.getConstantArrayType( + AT->getElementType(), llvm::APInt(32, ArrayLength), nullptr, + ArrayType::Normal, 0); + } + } else if (auto *RT = Entity.getType()->getAs()) { + const CXXRecordDecl *RD = cast(RT->getDecl()); + + auto BaseRange = map_range(RD->bases(), [&S](auto &base) { + return InitializedEntity::InitializeBase(S.getASTContext(), &base, false); + }); + auto FieldRange = map_range(RD->fields(), [](auto *field) { + return InitializedEntity::InitializeMember(field); + }); + + if (!InitHelper(BaseRange)) + return; + + if (!InitHelper(FieldRange)) + return; + + ResultType = Entity.getType(); + } + + if (Index != Args.size()) { + if (!VerifyOnly) { + QualType T = Entity.getType(); + // FIXME: Union is unsupported. + int InitKind = T->isArrayType() ? 0 : 4; + S.Diag(Kind.getLocation(), diag::err_excess_initializers) + << InitKind << Args[Index]->getSourceRange(); + } else { + Sequence.SetFailed( + InitializationSequence::FK_ParenthesizedListInitFailed); + } + return; + } + + if (VerifyOnly) { + Sequence.setSequenceKind(InitializationSequence::NormalSequence); + Sequence.AddParenthesizedListInitStep(Entity.getType()); + } else if (Result) { + *Result = CXXParenListInitExpr::Create(S.getASTContext(), InitExprs, + ResultType, Kind.getLocation()); + } + + return; +} + /// Attempt initialization by constructor (C++ [dcl.init]), which /// enumerates the constructors of the initialized entity and performs overload /// resolution to select the best. @@ -5905,7 +6019,10 @@ TryListInitialization(S, Entity, Kind, cast(Initializer), *this, TreatUnavailableAsInvalid); AddParenthesizedArrayInitStep(DestType); - } else if (DestAT->getElementType()->isCharType()) + } else if (S.getLangOpts().CPlusPlus20 && !TopLevelOfInitList) + TryOrBuildParenListInitialization(S, Entity, Kind, Args, *this, + /*VerifyOnly=*/true); + else if (DestAT->getElementType()->isCharType()) SetFailed(FK_ArrayNeedsInitListOrStringLiteral); else if (IsWideCharCompatible(DestAT->getElementType(), Context)) SetFailed(FK_ArrayNeedsInitListOrWideStringLiteral); @@ -5952,18 +6069,52 @@ if (Kind.getKind() == InitializationKind::IK_Direct || (Kind.getKind() == InitializationKind::IK_Copy && (Context.hasSameUnqualifiedType(SourceType, DestType) || - S.IsDerivedFrom(Initializer->getBeginLoc(), SourceType, DestType)))) - TryConstructorInitialization(S, Entity, Kind, Args, - DestType, 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 - // used) to a derived class thereof are enumerated as described in - // 13.3.1.4, and the best one is chosen through overload resolution - // (13.3). - else + S.IsDerivedFrom(Initializer->getBeginLoc(), SourceType, DestType)))) { + TryConstructorInitialization(S, Entity, Kind, Args, DestType, DestType, + *this); + + // We fall back to the "no matching constructor "path iff the + // failed candidate set has function other than the three default + // constructors. For example, conversion function. + if (const CXXRecordDecl *RD = + dyn_cast(DestType->getAs()->getDecl()); + S.getLangOpts().CPlusPlus20 && RD && RD->isAggregate() && + Failure == FK_ConstructorOverloadFailed && + getFailedCandidateSet().size() == 3) { + // const ValueDecl *VD = Entity.getDecl(); + // if (const VarDecl *VarD = dyn_cast_or_null(VD); + // VarD && VarD->hasInit() && !VarD->getInit()->containsErrors()) + // 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, the object is initialized as follows. Let e1, + // ..., en be the elements of the aggregate . Let x1, ..., xk be + // the elements of the expression-list. If k is greater than n, + // the program is ill-formed. The element ei is copy-initialized + // with xi for 1 <= i <= k. The remaining elements are + // initialized with their default member initializers, if any, + // and otherwise are value-initialized. For each 1 <= i < j <= n, + // every value computation and side effect associated with the + // initialization of ei is sequenced before those associated with + // the initialization of ej. Note: By contrast with + // direct-list-initialization, narrowing conversions (9.4.4) are + // permitted, designators are not permitted, a temporary object + // bound to a reference does not have its lifetime extended + // (6.7.7), and there is no brace elision. + TryOrBuildParenListInitialization(S, Entity, Kind, Args, *this, + /*VerifyOnly=*/true); + } + } else { + // - 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 used) to a derived class thereof are enumerated as + // described in 13.3.1.4, and the best one is chosen through + // overload resolution (13.3). TryUserDefinedConversion(S, DestType, Kind, Initializer, *this, TopLevelOfInitList); + } return; } @@ -8213,6 +8364,7 @@ case SK_ConstructorInitializationFromList: case SK_StdInitializerListConstructorCall: case SK_ZeroInitialization: + case SK_ParenthesizedListInit: break; } @@ -8902,6 +9054,14 @@ CurInit.get()->getValueKind()); break; } + case SK_ParenthesizedListInit: { + CurInit = false; + TryOrBuildParenListInitialization(S, Entity, Kind, Args, *this, + /*VerifyOnly=*/false, &CurInit); + if (CurInit.get() && ResultType) + *ResultType = CurInit.get()->getType(); + break; + } } } @@ -9500,6 +9660,10 @@ diag::note_explicit_ctor_deduction_guide_here) << false; break; } + + case FK_ParenthesizedListInitFailed: + TryOrBuildParenListInitialization(S, Entity, Kind, Args, *this, + /*VerifyOnly=*/false); } PrintInitLocationNote(S, Entity); @@ -9666,6 +9830,10 @@ case FK_ExplicitConstructor: OS << "list copy initialization chose explicit constructor"; break; + + case FK_ParenthesizedListInitFailed: + OS << "parenthesized list initialization failed"; + break; } OS << '\n'; return; @@ -9837,6 +10005,9 @@ case SK_OCLZeroOpaqueType: OS << "OpenCL opaque type from zero"; break; + case SK_ParenthesizedListInit: + OS << "initialization from a parenthesized list of values"; + break; } OS << " [" << S->Type << ']'; Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -13884,6 +13884,13 @@ return Result; } +template +ExprResult +TreeTransform::TransformCXXParenListInitExpr(CXXParenListInitExpr *E) { + // FIXME: unimplemented + llvm_unreachable("unimplemented"); +} + template ExprResult TreeTransform::TransformCXXStdInitializerListExpr( Index: clang/lib/Serialization/ASTReaderStmt.cpp =================================================================== --- clang/lib/Serialization/ASTReaderStmt.cpp +++ clang/lib/Serialization/ASTReaderStmt.cpp @@ -2167,6 +2167,11 @@ E->Opcode = (BinaryOperatorKind)Record.readInt(); } +void ASTStmtReader::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) { + // FIXME: unimplemented + llvm_unreachable("unimplemented"); +} + void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) { VisitExpr(E); E->SourceExpr = Record.readSubExpr(); Index: clang/lib/Serialization/ASTWriterStmt.cpp =================================================================== --- clang/lib/Serialization/ASTWriterStmt.cpp +++ clang/lib/Serialization/ASTWriterStmt.cpp @@ -2077,6 +2077,11 @@ Code = serialization::EXPR_CXX_FOLD; } +void ASTStmtWriter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) { + // FIXME: unimplemented + llvm_unreachable("unimplemented"); +} + void ASTStmtWriter::VisitOpaqueValueExpr(OpaqueValueExpr *E) { VisitExpr(E); Record.AddStmt(E->getSourceExpr()); Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1579,6 +1579,7 @@ case Stmt::ConceptSpecializationExprClass: case Stmt::CXXRewrittenBinaryOperatorClass: case Stmt::RequiresExprClass: + case Expr::CXXParenListInitExprClass: // Fall through. // Cases we intentionally don't evaluate, since they don't need Index: clang/test/CXX/class/class.compare/class.spaceship/p1.cpp =================================================================== --- clang/test/CXX/class/class.compare/class.spaceship/p1.cpp +++ clang/test/CXX/class/class.compare/class.spaceship/p1.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -std=c++2a -verify %s -fcxx-exceptions namespace std { - struct strong_ordering { // expected-note 6{{candidate}} + struct strong_ordering { int n; constexpr operator int() const { return n; } static const strong_ordering less, equal, greater; @@ -103,7 +103,7 @@ Cmp() <=> Cmp(), // expected-note-re {{in defaulted three-way comparison operator for '{{.*}}Cmp<{{.*}}G1>' first required here}}j // expected-error@#cmp {{value of type 'void' is not contextually convertible to 'bool'}} Cmp() <=> Cmp(), // expected-note-re {{in defaulted three-way comparison operator for '{{.*}}Cmp<{{.*}}G2>' first required here}}j - // expected-error@#cmp {{no matching conversion for static_cast from 'void' to 'std::strong_ordering'}} + // expected-error@#cmp {{static_cast from 'void' to 'std::strong_ordering' is not allowed}} Cmp() <=> Cmp(), // expected-note-re {{in defaulted three-way comparison operator for '{{.*}}Cmp<{{.*}}H>' first required here}}j 0 ); @@ -134,7 +134,7 @@ CmpArray() <=> CmpArray(), // expected-note-re {{in defaulted three-way comparison operator for '{{.*}}CmpArray<{{.*}}G1>' first required here}}j // expected-error@#cmparray {{value of type 'void' is not contextually convertible to 'bool'}} CmpArray() <=> CmpArray(), // expected-note-re {{in defaulted three-way comparison operator for '{{.*}}CmpArray<{{.*}}G2>' first required here}}j - // expected-error@#cmparray {{no matching conversion for static_cast from 'void' to 'std::strong_ordering'}} + // expected-error@#cmparray {{static_cast from 'void' to 'std::strong_ordering' is not allowed}} CmpArray() <=> CmpArray(), // expected-note-re {{in defaulted three-way comparison operator for '{{.*}}CmpArray<{{.*}}H>' first required here}}j 0 ); Index: clang/test/CXX/drs/dr2xx.cpp =================================================================== --- clang/test/CXX/drs/dr2xx.cpp +++ clang/test/CXX/drs/dr2xx.cpp @@ -1,8 +1,8 @@ -// RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++20 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++98 %s -verify=expected,pre20 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++11 %s -verify=expected,pre20 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++14 %s -verify=expected,pre20 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++17 %s -verify=expected,pre20 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++20 %s -verify=expected,pro20 -fexceptions -fcxx-exceptions -pedantic-errors // PR13819 -- __SIZE_TYPE__ is incompatible. typedef __SIZE_TYPE__ size_t; // expected-error 0-1 {{extension}} @@ -157,7 +157,8 @@ void test3(A::S as) { using A::f; f(as); } // ok void test4(A::S as) { using B::f; f(as); } // ok void test5(A::S as) { int f; f(as); } // expected-error {{called object type 'int'}} - void test6(A::S as) { struct f {}; (void) f(as); } // expected-error {{no matching conversion}} expected-note +{{}} + void test6(A::S as) { struct f {}; (void) f(as); } // pre20-error {{no matching conversion}} pre20-note +{{}} + // pro20-error@-1 {{functional-style cast from 'A::S' to 'f' is not allowed}} }; namespace D { Index: clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp =================================================================== --- clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp +++ clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fexceptions -fcxx-exceptions -verify %s -// RUN: %clang_cc1 -std=c++2a -fsyntax-only -fexceptions -fcxx-exceptions -verify %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fexceptions -fcxx-exceptions -verify=expected,pre20 %s +// RUN: %clang_cc1 -std=c++2a -fsyntax-only -fexceptions -fcxx-exceptions -verify=expected,pro20 %s template struct tuple; template struct int_c; @@ -99,12 +99,12 @@ HasMixins(int i); }; -struct A { }; // expected-note{{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'int' to 'const A' for 1st argument}} \ -// expected-note{{candidate constructor (the implicit move constructor) not viable: no known conversion from 'int' to 'A' for 1st argument}} \ -// expected-note{{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} -struct B { }; // expected-note{{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'int' to 'const B' for 1st argument}} \ -// expected-note{{candidate constructor (the implicit move constructor) not viable: no known conversion from 'int' to 'B' for 1st argument}} \ -// expected-note{{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} +struct A { }; // pre20-note{{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'int' to 'const A' for 1st argument}} \ +// pre20-note{{candidate constructor (the implicit move constructor) not viable: no known conversion from 'int' to 'A' for 1st argument}} \ +// pre20-note{{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} +struct B { }; // pre20-note{{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'int' to 'const B' for 1st argument}} \ +// pre20-note{{candidate constructor (the implicit move constructor) not viable: no known conversion from 'int' to 'B' for 1st argument}} \ +// pre20-note{{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} struct C { }; struct D { }; @@ -126,8 +126,10 @@ template HasMixins::HasMixins(int i): Mixins(i)... { } -// expected-error@-1 {{no matching constructor for initialization of 'A'}} -// expected-error@-2 {{no matching constructor for initialization of 'B'}} +// pre20-error@-1 {{no matching constructor for initialization of 'A'}} +// pre20-error@-2 {{no matching constructor for initialization of 'B'}} +// pro20-error@-3 {{excess elements in struct initializer}} +// pro20-error@-4 {{excess elements in struct initializer}} void test_has_mixins() { HasMixins ab; Index: clang/test/CodeGen/P0960R3.cpp =================================================================== --- /dev/null +++ clang/test/CodeGen/P0960R3.cpp @@ -0,0 +1,73 @@ +// RUN: %clang_cc1 -std=c++20 %s -emit-llvm -o - | FileCheck %s + +struct A { + char i; + double j; +}; + +struct B { + A a; + int b; +}; + +struct C : public B, public A {}; + +// CHECK-LABEL: entry: +// CHECK-NEXT: [[A1:%.*]] = alloca [[STRUCT_A:%.*]], align 8 +// CHECK-NEXT: [[B1:%.*]] = alloca [[STRUCT_B:%.*]], align 8 +// CHECK-NEXT: [[C1:%.*]] = alloca [[STRUCT_C:%.*]], align 8 +// CHECK-NEXT: [[REF_TMP:%.*]] = alloca [[STRUCT_B]], align 8 +// CHECK-NEXT: [[REF_TMP5:%.*]] = alloca [[STRUCT_A]], align 8 +// CHECK-NEXT: [[I:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[A1]], i32 0, i32 0 +// CHECK-NEXT: store i8 3, ptr [[I]], align 8 +// CHECK-NEXT: [[J:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[A1]], i32 0, i32 1 +// CHECK-NEXT: store double 2.000000e+00, ptr [[J]], align 8 +// CHECK-NEXT: [[A:%.*]] = getelementptr inbounds [[STRUCT_B]], ptr [[B1]], i32 0, i32 0 +// CHECK-NEXT: [[I1:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[A]], i32 0, i32 0 +// CHECK-NEXT: store i8 99, ptr [[I1]], align 8 +// CHECK-NEXT: [[A2:%.*]] = getelementptr inbounds [[STRUCT_B]], ptr [[REF_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[I3:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[A2]], i32 0, i32 0 +// CHECK-NEXT: store i8 1, ptr [[I3]], align 8 +// CHECK-NEXT: [[J4:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[A2]], i32 0, i32 1 +// CHECK-NEXT: store double 1.000000e+00, ptr [[J4]], align 8 +// CHECK-NEXT: [[B:%.*]] = getelementptr inbounds [[STRUCT_B]], ptr [[REF_TMP]], i32 0, i32 1 +// CHECK-NEXT: store i32 1, ptr [[B]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[C1]], ptr align 8 [[REF_TMP]], i64 24, i1 false) +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[C1]], i64 24 +// CHECK-NEXT: [[I6:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[REF_TMP5]], i32 0, i32 0 +// CHECK-NEXT: store i8 97, ptr [[I6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[REF_TMP5]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void foo1() { + A a1(3.1, 2.0); + B b1(A('c')); + C c1(B(A(1, 1), 1), A('a')); +} + + +// CHECK-LABEL: entry: +// CHECK-NEXT: [[ARR1:%.*]] = alloca [4 x i32], align 16 +// CHECK-NEXT: [[ARR2:%.*]] = alloca [5 x i32], align 16 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds [4 x i32], ptr [[ARR1]], i64 0, i64 0 +// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 16 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [4 x i32], ptr [[ARR1]], i64 0, i64 1 +// CHECK-NEXT: store i32 2, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [4 x i32], ptr [[ARR1]], i64 0, i64 2 +// CHECK-NEXT: store i32 3, ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds [5 x i32], ptr [[ARR2]], i64 0, i64 0 +// CHECK-NEXT: store i32 0, ptr [[TMP3]], align 16 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds [5 x i32], ptr [[ARR2]], i64 0, i64 1 +// CHECK-NEXT: store i32 1, ptr [[TMP4]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds [5 x i32], ptr [[ARR2]], i64 0, i64 2 +// CHECK-NEXT: store i32 2, ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds [5 x i32], ptr [[ARR2]], i64 0, i64 3 +// CHECK-NEXT: store i32 3, ptr [[TMP6]], align 4 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds [5 x i32], ptr [[ARR2]], i64 0, i64 4 +// CHECK-NEXT: store i32 4, ptr [[TMP7]], align 16 +// CHECK-NEXT: ret void +// +void foo2() { + int arr1[4](1, 2, 3); + int arr2[](0, 1, 2, 3, 4); +} Index: clang/test/SemaCXX/P0960R3.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/P0960R3.cpp @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -verify -std=c++20 %s -fsyntax-only + +struct A { // expected-note 4{{candidate constructor}} + char i; + double j; +}; + +struct B { + A a; + int b[20]; + int &&c; // expected-note {{reference member declared here}} +}; + +struct C { // expected-note 2{{candidate constructor}} + A a; + int b[20]; +}; + +struct D : public C, public A { + int a; +}; + +void foo() { + A a(1954, 9, 21); + // expected-error@-1 {{excess elements in struct initializer}} + A b(2.1); + // expected-warning@-1 {{implicit conversion from 'double' to 'char'}} + A e(-1.2, 9.8); + // expected-warning@-1 {{implicit conversion from 'double' to 'char'}} + A s = static_cast(1.1); + // expected-warning@-1 {{implicit conversion from 'double' to 'char'}} + A h = (A)3.1; + // expected-warning@-1 {{implicit conversion from 'double' to 'char'}} + A i = A(8.7); + // expected-warning@-1 {{implicit conversion from 'double' to 'char'}} + + B n(2022, {7, 8}); + // expected-error@-1 {{no viable conversion from 'int' to 'A'}} + B z(A(1), {}, 1); + // expected-error@-1 {{reference member 'c' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}} + + C o(A(1), 1, 2, 3, 4); + // expected-error@-1 {{excess elements in struct initializer}} + D R(1); + // expected-error@-1 {{no viable conversion from 'int' to 'C'}} + D I(C(1)); + // expected-error@-1 {{functional-style cast from 'int' to 'C' is not allowed}} + D P(C(A(1)), 1); + // expected-error@-1 {{no viable conversion from 'int' to 'A'}} + + int arr1[](0, 1, 2, A(1)); + // expected-error@-1 {{no viable conversion from 'A' to 'int'}} + int arr2[2](0, 1, 2); + // expected-error@-1 {{excess elements in array initializer}} +} Index: clang/test/SemaCXX/cxx2a-explicit-bool.cpp =================================================================== --- clang/test/SemaCXX/cxx2a-explicit-bool.cpp +++ clang/test/SemaCXX/cxx2a-explicit-bool.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -std=c++17 -fsyntax-only %s -verify -Wno-c++2a-extensions -// RUN: %clang_cc1 -std=c++2a -fsyntax-only %s -verify +// RUN: %clang_cc1 -std=c++17 -fsyntax-only %s -verify=expected,pre20 -Wno-c++2a-extensions +// RUN: %clang_cc1 -std=c++2a -fsyntax-only %s -verify=expected,pro20 template struct enable_ifv {}; @@ -20,7 +20,7 @@ template struct A { -// expected-note@-1+ {{candidate constructor}} +// pre20-note@-1+ {{candidate constructor}} explicit(1 << a) // expected-note@-1 {{negative shift count -1}} // expected-error@-2 {{explicit specifier argument is not a constant expression}} @@ -28,8 +28,9 @@ }; A<-1> a(0); -// expected-error@-1 {{no matching constructor}} -// expected-note@-2 {{in instantiation of template class}} +// pre20-error@-1 {{no matching constructor}} +// pro20-error@-2 {{excess elements in struct initializer}} +// expected-note@-3 {{in instantiation of template class}} template struct B {