diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -627,6 +627,10 @@ - Do not hide templated base members introduced via using-decl in derived class (useful specially for constrained members). Fixes `GH50886 `_. +- Implemented `P0960R3: `_ + and `P1975R0: `_, + which allows parenthesized aggregate-initialization. + C++2b Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -1531,7 +1531,13 @@ */ CXCursor_RequiresExpr = 154, - CXCursor_LastExpr = CXCursor_RequiresExpr, + /** + * Expression that references a C++20 parenthesized list aggregate + * initializer. + */ + CXCursor_CXXParenListInitExpr = 155, + + CXCursor_LastExpr = CXCursor_CXXParenListInitExpr, /* Statements */ CXCursor_FirstStmt = 200, diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -79,6 +79,7 @@ class CXXDependentScopeMemberExpr; class MaterializeTemporaryExpr; class CXXFoldExpr; +class CXXParenListInitExpr; class TypeTraitExpr; class ConceptSpecializationExpr; class SYCLUniqueStableNameExpr; @@ -168,6 +169,7 @@ ExprDependence computeDependence(CXXDependentScopeMemberExpr *E); ExprDependence computeDependence(MaterializeTemporaryExpr *E); ExprDependence computeDependence(CXXFoldExpr *E); +ExprDependence computeDependence(CXXParenListInitExpr *E); ExprDependence computeDependence(TypeTraitExpr *E); ExprDependence computeDependence(ConceptSpecializationExpr *E, bool ValueDependent); diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -889,7 +889,10 @@ CallInit, /// Direct list-initialization (C++11) - ListInit + ListInit, + + /// Parenthesized list-initialization (C++20) + ParenListInit }; /// Kinds of thread-local storage. diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -4714,6 +4714,101 @@ } }; +/// 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); // Well-formed in C++20 +/// A a2(1.5, 1.0); // Well-formed 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); // Well-formed in C++20 +/// A b{1.5}; // Ill-formed ! +/// } +/// ``` +class CXXParenListInitExpr final + : public Expr, + private llvm::TrailingObjects { + friend class TrailingObjects; + friend class ASTStmtReader; + friend class ASTStmtWriter; + + unsigned NumExprs; + SourceLocation Loc; + + CXXParenListInitExpr(ArrayRef Args, QualType T, SourceLocation Loc) + : Expr(CXXParenListInitExprClass, T, + T->isLValueReferenceType() ? VK_LValue + : T->isRValueReferenceType() ? VK_XValue + : VK_PRValue, + OK_Ordinary), + NumExprs(Args.size()), Loc(Loc) { + std::copy(Args.begin(), Args.end(), getTrailingObjects()); + setDependence(computeDependence(this)); + } + + size_t numTrailingObjects(OverloadToken) const { return NumExprs; } + +public: + static CXXParenListInitExpr *Create(ASTContext &C, ArrayRef Args, + QualType T, SourceLocation Loc); + + static CXXParenListInitExpr *CreateEmpty(ASTContext &C, unsigned numExprs, + EmptyShell Empty); + + explicit CXXParenListInitExpr(EmptyShell Empty) + : Expr(CXXParenListInitExprClass, Empty) {} + + void updateDependence() { setDependence(computeDependence(this)); } + + ArrayRef getInitExprs() { + return llvm::makeArrayRef(getTrailingObjects(), NumExprs); + } + + const ArrayRef getInitExprs() const { + return llvm::makeArrayRef(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. /// diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2850,6 +2850,7 @@ DEF_TRAVERSE_STMT(FunctionParmPackExpr, {}) DEF_TRAVERSE_STMT(CXXFoldExpr, {}) DEF_TRAVERSE_STMT(AtomicExpr, {}) +DEF_TRAVERSE_STMT(CXXParenListInitExpr, {}) DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, { if (S->getLifetimeExtendedTemporaryDecl()) { diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2162,6 +2162,9 @@ def warn_cxx20_compat_aggregate_init_with_ctors : Warning< "aggregate initialization of type %0 with user-declared constructors " "is incompatible with C++20">, DefaultIgnore, InGroup; +def warn_cxx17_compat_aggregate_init_paren_list : Warning< + "aggregate initialization of type %0 from a parenthesized list of values " + "is a C++20 extension">, DefaultIgnore, InGroup; def err_reference_bind_to_bitfield : Error< "%select{non-const|volatile}0 reference cannot bind to " diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td --- a/clang/include/clang/Basic/StmtNodes.td +++ b/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; diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h --- a/clang/include/clang/Sema/Initialization.h +++ b/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); diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1855,6 +1855,9 @@ /// A CXXBoolLiteralExpr record. EXPR_CXX_BOOL_LITERAL, + /// A CXXParenListInitExpr record. + EXPR_CXX_PAREN_LIST_INIT, + EXPR_CXX_NULL_PTR_LITERAL, // CXXNullPtrLiteralExpr EXPR_CXX_TYPEID_EXPR, // CXXTypeidExpr (of expr). EXPR_CXX_TYPEID_TYPE, // CXXTypeidExpr (of type). diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -831,6 +831,13 @@ return D; } +ExprDependence clang::computeDependence(CXXParenListInitExpr *E) { + auto D = ExprDependence::None; + for (const auto *A : E->getInitExprs()) + D |= A->getDependence(); + return D; +} + ExprDependence clang::computeDependence(TypeTraitExpr *E) { auto D = ExprDependence::None; for (const auto *A : E->getArgs()) diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3644,6 +3644,7 @@ case ShuffleVectorExprClass: case ConvertVectorExprClass: case AsTypeExprClass: + case CXXParenListInitExprClass: // These have a side-effect if any subexpression does. break; diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1758,3 +1758,18 @@ 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); +} + +CXXParenListInitExpr *CXXParenListInitExpr::CreateEmpty(ASTContext &C, + unsigned NumExprs, + EmptyShell Empty) { + void *Mem = C.Allocate(totalSizeToAlloc(NumExprs)); + return new (Mem) CXXParenListInitExpr(Empty); +} \ No newline at end of file diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -442,6 +442,11 @@ case Expr::SYCLUniqueStableNameExprClass: return Cl::CL_PRValue; break; + + case Expr::CXXParenListInitExprClass: + if (isa(E->getType())) + return Cl::CL_ArrayTemporary; + return Cl::CL_ClassTemporary; } llvm_unreachable("unhandled expression kind in classification"); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -9833,6 +9833,9 @@ bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T); bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E); bool VisitBinCmp(const BinaryOperator *E); + bool VisitCXXParenListInitExpr(const CXXParenListInitExpr *E); + bool VisitCXXParenListOrInitListExpr(const Expr *ExprToVisit, + ArrayRef Args); }; } @@ -9951,8 +9954,13 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { if (E->isTransparent()) return Visit(E->getInit(0)); + return VisitCXXParenListOrInitListExpr(E, E->inits()); +} - const RecordDecl *RD = E->getType()->castAs()->getDecl(); +bool RecordExprEvaluator::VisitCXXParenListOrInitListExpr( + const Expr *ExprToVisit, ArrayRef Args) { + const RecordDecl *RD = + ExprToVisit->getType()->castAs()->getDecl(); if (RD->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); auto *CXXRD = dyn_cast(RD); @@ -9963,7 +9971,24 @@ CXXRD && CXXRD->getNumBases()); if (RD->isUnion()) { - const FieldDecl *Field = E->getInitializedFieldInUnion(); + const FieldDecl *Field; + if (auto *ILE = dyn_cast(ExprToVisit)) { + Field = ILE->getInitializedFieldInUnion(); + } else if (isa(ExprToVisit)) { + assert(Args.size() == 1 && + "Unions should have exactly 1 initializer in a C++ paren list"); + + for (const FieldDecl *FD : RD->fields()) { + if (FD->isUnnamedBitfield()) + continue; + + Field = FD; + break; + } + } else + llvm_unreachable( + "Expression is neither an init list nor a C++ paren list"); + Result = APValue(Field); if (!Field) return true; @@ -9974,7 +9999,7 @@ // Is this difference ever observable for initializer lists which // we don't build? ImplicitValueInitExpr VIE(Field->getType()); - const Expr *InitExpr = E->getNumInits() ? E->getInit(0) : &VIE; + const Expr *InitExpr = Args.size() ? Args[0] : &VIE; LValue Subobject = This; if (!HandleLValueMember(Info, InitExpr, Subobject, Field, &Layout)) @@ -10003,8 +10028,8 @@ // Initialize base classes. if (CXXRD && CXXRD->getNumBases()) { for (const auto &Base : CXXRD->bases()) { - assert(ElementNo < E->getNumInits() && "missing init for base class"); - const Expr *Init = E->getInit(ElementNo); + assert(ElementNo < Args.size() && "missing init for base class"); + const Expr *Init = Args[ElementNo]; LValue Subobject = This; if (!HandleLValueBase(Info, Init, Subobject, CXXRD, &Base)) @@ -10031,18 +10056,18 @@ LValue Subobject = This; - bool HaveInit = ElementNo < E->getNumInits(); + bool HaveInit = ElementNo < Args.size(); // FIXME: Diagnostics here should point to the end of the initializer // list, not the start. - if (!HandleLValueMember(Info, HaveInit ? E->getInit(ElementNo) : E, + if (!HandleLValueMember(Info, HaveInit ? Args[ElementNo] : ExprToVisit, Subobject, Field, &Layout)) return false; // Perform an implicit value-initialization for members beyond the end of // the initializer list. ImplicitValueInitExpr VIE(HaveInit ? Info.Ctx.IntTy : Field->getType()); - const Expr *Init = HaveInit ? E->getInit(ElementNo++) : &VIE; + const Expr *Init = HaveInit ? Args[ElementNo++] : &VIE; if (Field->getType()->isIncompleteArrayType()) { if (auto *CAT = Info.Ctx.getAsConstantArrayType(Init->getType())) { @@ -10676,6 +10701,7 @@ expandStringLiteral(Info, E, Result, AllocType); return true; } + bool VisitCXXParenListInitExpr(const CXXParenListInitExpr *E); }; } // end anonymous namespace @@ -10921,6 +10947,26 @@ .VisitCXXConstructExpr(E, Type); } +bool ArrayExprEvaluator::VisitCXXParenListInitExpr( + const CXXParenListInitExpr *E) { + const auto *CAT = dyn_cast(E->getType()); + assert(CAT && "Expression result is not a constant array type"); + + const ArrayRef InitExprs = E->getInitExprs(); + unsigned NumElts = InitExprs.size(); + Result = APValue(APValue::UninitArray(), NumElts, NumElts); + + LValue Subobject = This; + Subobject.addArray(Info, E, CAT); + + for (unsigned Index = 0; Index < NumElts; Index++) + if (!EvaluateInPlace(Result.getArrayInitializedElt(Index), Info, Subobject, + InitExprs[Index])) + return false; + + return true; +} + //===----------------------------------------------------------------------===// // Integer Evaluation // @@ -13155,6 +13201,11 @@ }); } +bool RecordExprEvaluator::VisitCXXParenListInitExpr( + const CXXParenListInitExpr *E) { + return VisitCXXParenListOrInitListExpr(E, E->getInitExprs()); +} + bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { // We don't support assignment in C. C++ assignments don't get here because // assignment is an lvalue in C++. @@ -15589,6 +15640,7 @@ case Expr::DependentCoawaitExprClass: case Expr::CoyieldExprClass: case Expr::SYCLUniqueStableNameExprClass: + case Expr::CXXParenListInitExprClass: return ICEDiag(IK_NotICE, E->getBeginLoc()); case Expr::InitListExprClass: { diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4249,6 +4249,7 @@ case Expr::OMPArrayShapingExprClass: case Expr::OMPIteratorExprClass: case Expr::CXXInheritedCtorInitExprClass: + case Expr::CXXParenListInitExprClass: llvm_unreachable("unexpected statement kind"); case Expr::ConstantExprClass: diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -849,6 +849,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()); diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2465,6 +2465,13 @@ OS << ")"; } +void StmtPrinter::VisitCXXParenListInitExpr(CXXParenListInitExpr *Node) { + OS << "("; + llvm::interleaveComma(Node->getInitExprs(), OS, + [&](Expr *E) { PrintExpr(E); }); + OS << ")"; +} + void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) { NestedNameSpecifierLoc NNS = E->getNestedNameSpecifierLoc(); if (NNS) diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2184,6 +2184,10 @@ ID.AddInteger(S->getOperator()); } +void StmtProfiler::VisitCXXParenListInitExpr(const CXXParenListInitExpr *S) { + VisitExpr(S); +} + void StmtProfiler::VisitCoroutineBodyStmt(const CoroutineBodyStmt *S) { VisitStmt(S); } diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1806,6 +1806,8 @@ case VarDecl::ListInit: OS << " listinit"; break; + case VarDecl::ParenListInit: + OS << " parenlistinit"; } } if (D->needsDestruction(D->getASTContext())) diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -87,8 +87,8 @@ void EmitMoveFromReturnSlot(const Expr *E, RValue Src); - void EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, - QualType ArrayQTy, InitListExpr *E); + void EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, QualType ArrayQTy, + Expr *ExprToVisit, ArrayRef Args); AggValueSlot::NeedsGCBarriers_t needsGC(QualType T) { if (CGF.getLangOpts().getGC() && TypeRequiresGCollection(T)) @@ -172,6 +172,8 @@ void VisitAbstractConditionalOperator(const AbstractConditionalOperator *CO); void VisitChooseExpr(const ChooseExpr *CE); void VisitInitListExpr(InitListExpr *E); + void VisitCXXParenListOrInitListExpr(Expr *ExprToVisit, ArrayRef Args, + FieldDecl *InitializedFieldInUnion); void VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E, llvm::Value *outerBegin = nullptr); void VisitImplicitValueInitExpr(ImplicitValueInitExpr *E); @@ -205,6 +207,7 @@ } void VisitVAArgExpr(VAArgExpr *E); + void VisitCXXParenListInitExpr(CXXParenListInitExpr *E); void EmitInitializationToLValue(Expr *E, LValue Address); void EmitNullInitializationToLValue(LValue Address); @@ -473,8 +476,9 @@ /// Emit initialization of an array from an initializer list. void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, - QualType ArrayQTy, InitListExpr *E) { - uint64_t NumInitElements = E->getNumInits(); + QualType ArrayQTy, Expr *ExprToVisit, + ArrayRef Args) { + uint64_t NumInitElements = Args.size(); uint64_t NumArrayElements = AType->getNumElements(); assert(NumInitElements <= NumArrayElements); @@ -503,7 +507,8 @@ CodeGen::CodeGenModule &CGM = CGF.CGM; ConstantEmitter Emitter(CGF); LangAS AS = ArrayQTy.getAddressSpace(); - if (llvm::Constant *C = Emitter.tryEmitForInitializer(E, AS, ArrayQTy)) { + if (llvm::Constant *C = + Emitter.tryEmitForInitializer(ExprToVisit, AS, ArrayQTy)) { auto GV = new llvm::GlobalVariable( CGM.getModule(), C->getType(), CGM.isTypeConstant(ArrayQTy, /* ExcludeCtorDtor= */ true), @@ -568,11 +573,13 @@ LValue elementLV = CGF.MakeAddrLValue( Address(element, llvmElementType, elementAlign), elementType); - EmitInitializationToLValue(E->getInit(i), elementLV); + EmitInitializationToLValue(Args[i], elementLV); } // Check whether there's a non-trivial array-fill expression. - Expr *filler = E->getArrayFiller(); + Expr *filler = nullptr; + if (auto *ILE = dyn_cast(ExprToVisit)) + filler = ILE->getArrayFiller(); bool hasTrivialFiller = isTrivialFiller(filler); // Any remaining elements need to be zero-initialized, possibly @@ -1591,46 +1598,75 @@ } } +void AggExprEmitter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) { + ArrayRef InitExprs = E->getInitExprs(); + FieldDecl *InitializedFieldInUnion = nullptr; + if (E->getType()->isUnionType()) { + auto *RD = + dyn_cast(E->getType()->castAs()->getDecl()); + for (FieldDecl *FD : RD->fields()) { + if (FD->isUnnamedBitfield()) + continue; + InitializedFieldInUnion = FD; + break; + } + } + + VisitCXXParenListOrInitListExpr(E, InitExprs, InitializedFieldInUnion); +} + void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { + if (E->hadArrayRangeDesignator()) + CGF.ErrorUnsupported(E, "GNU array range designator extension"); + + if (E->isTransparent()) + return Visit(E->getInit(0)); + + VisitCXXParenListOrInitListExpr(E, E->inits(), + E->getInitializedFieldInUnion()); +} + +void AggExprEmitter::VisitCXXParenListOrInitListExpr( + Expr *ExprToVisit, ArrayRef InitExprs, + FieldDecl *InitializedFieldInUnion) { #if 0 // FIXME: Assess perf here? Figure out what cases are worth optimizing here // (Length of globals? Chunks of zeroed-out space?). // // If we can, prefer a copy from a global; this is a lot less code for long // globals, and it's easier for the current optimizers to analyze. - if (llvm::Constant* C = CGF.CGM.EmitConstantExpr(E, E->getType(), &CGF)) { + if (llvm::Constant *C = + CGF.CGM.EmitConstantExpr(ExprToVisit, ExprToVisit->getType(), &CGF)) { llvm::GlobalVariable* GV = new llvm::GlobalVariable(CGF.CGM.getModule(), C->getType(), true, llvm::GlobalValue::InternalLinkage, C, ""); - EmitFinalDestCopy(E->getType(), CGF.MakeAddrLValue(GV, E->getType())); + EmitFinalDestCopy(ExprToVisit->getType(), + CGF.MakeAddrLValue(GV, ExprToVisit->getType())); return; } #endif - if (E->hadArrayRangeDesignator()) - CGF.ErrorUnsupported(E, "GNU array range designator extension"); - if (E->isTransparent()) - return Visit(E->getInit(0)); + AggValueSlot Dest = EnsureSlot(ExprToVisit->getType()); - AggValueSlot Dest = EnsureSlot(E->getType()); - - LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType()); + LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), ExprToVisit->getType()); // Handle initialization of an array. - if (E->getType()->isArrayType()) { + if (ExprToVisit->getType()->isArrayType()) { auto AType = cast(Dest.getAddress().getElementType()); - EmitArrayInit(Dest.getAddress(), AType, E->getType(), E); + EmitArrayInit(Dest.getAddress(), AType, ExprToVisit->getType(), ExprToVisit, + InitExprs); return; } - assert(E->getType()->isRecordType() && "Only support structs/unions here!"); + assert(ExprToVisit->getType()->isRecordType() && + "Only support structs/unions here!"); // Do struct initialization; this code just sets each individual member // to the approprate value. This makes bitfield support automatic; // the disadvantage is that the generated code is more difficult for // the optimizer, especially with bitfields. - unsigned NumInitElements = E->getNumInits(); - RecordDecl *record = E->getType()->castAs()->getDecl(); + unsigned NumInitElements = InitExprs.size(); + RecordDecl *record = ExprToVisit->getType()->castAs()->getDecl(); // We'll need to enter cleanup scopes in case any of the element // initializers throws an exception. @@ -1648,7 +1684,7 @@ // Emit initialization of base classes. if (auto *CXXRD = dyn_cast(record)) { - assert(E->getNumInits() >= CXXRD->getNumBases() && + assert(NumInitElements >= CXXRD->getNumBases() && "missing initializer for base class"); for (auto &Base : CXXRD->bases()) { assert(!Base.isVirtual() && "should not see vbases here"); @@ -1662,7 +1698,7 @@ AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsNotAliased, CGF.getOverlapForBaseInit(CXXRD, BaseRD, Base.isVirtual())); - CGF.EmitAggExpr(E->getInit(curInitIndex++), AggSlot); + CGF.EmitAggExpr(InitExprs[curInitIndex++], AggSlot); if (QualType::DestructionKind dtorKind = Base.getType().isDestructedType()) { @@ -1678,7 +1714,7 @@ if (record->isUnion()) { // Only initialize one field of a union. The field itself is // specified by the initializer list. - if (!E->getInitializedFieldInUnion()) { + if (!InitializedFieldInUnion) { // Empty union; we have nothing to do. #ifndef NDEBUG @@ -1691,12 +1727,12 @@ } // FIXME: volatility - FieldDecl *Field = E->getInitializedFieldInUnion(); + FieldDecl *Field = InitializedFieldInUnion; LValue FieldLoc = CGF.EmitLValueForFieldInitialization(DestLV, Field); if (NumInitElements) { // Store the initializer into the field - EmitInitializationToLValue(E->getInit(0), FieldLoc); + EmitInitializationToLValue(InitExprs[0], FieldLoc); } else { // Default-initialize to null. EmitNullInitializationToLValue(FieldLoc); @@ -1720,7 +1756,7 @@ // have a zeroed object, and the rest of the fields are // zero-initializable. if (curInitIndex == NumInitElements && Dest.isZeroed() && - CGF.getTypes().isZeroInitializable(E->getType())) + CGF.getTypes().isZeroInitializable(ExprToVisit->getType())) break; @@ -1730,7 +1766,7 @@ if (curInitIndex < NumInitElements) { // Store the initializer into the field. - EmitInitializationToLValue(E->getInit(curInitIndex++), LV); + EmitInitializationToLValue(InitExprs[curInitIndex++], LV); } else { // We're out of initializers; default-initialize to null EmitNullInitializationToLValue(LV); diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -674,7 +674,7 @@ // C++20 features. if (LangOpts.CPlusPlus20) { - // Builder.defineMacro("__cpp_aggregate_paren_init", "201902L"); + Builder.defineMacro("__cpp_aggregate_paren_init", "201902L"); // P0848 is implemented, but we're still waiting for other concepts // issues to be addressed before bumping __cpp_concepts up to 202002L. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -12980,6 +12980,7 @@ // Perform the initialization. ParenListExpr *CXXDirectInit = dyn_cast(Init); + bool IsParenListInit = false; if (!VDecl->isInvalidDecl()) { InitializedEntity Entity = InitializedEntity::InitializeVariable(VDecl); InitializationKind Kind = InitializationKind::CreateForInit( @@ -13022,6 +13023,8 @@ } Init = Result.getAs(); + IsParenListInit = InitSeq.step_begin()->Kind == + InitializationSequence::SK_ParenthesizedListInit; } // Check for self-references within variable initializers. @@ -13270,7 +13273,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); diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1289,6 +1289,7 @@ case Expr::StmtExprClass: case Expr::ConvertVectorExprClass: case Expr::VAArgExprClass: + case Expr::CXXParenListInitExprClass: return canSubStmtsThrow(*this, S); case Expr::CompoundLiteralExprClass: diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -3529,6 +3529,7 @@ case SK_StdInitializerListConstructorCall: case SK_OCLSamplerInit: case SK_OCLZeroOpaqueType: + case SK_ParenthesizedListInit: break; case SK_ConversionSequence: @@ -3588,6 +3589,7 @@ case FK_PlaceholderType: case FK_ExplicitConstructor: case FK_AddressOfUnaddressableFunction: + case FK_ParenthesizedListInitFailed: return false; case FK_ReferenceInitOverloadFailed: @@ -3823,6 +3825,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 && @@ -5260,6 +5269,186 @@ } } +static void TryOrBuildParenListInitialization( + Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, + ArrayRef Args, InitializationSequence &Sequence, bool VerifyOnly, + ExprResult *Result = nullptr) { + unsigned ArgIndexToProcess = 0; + SmallVector InitExprs; + QualType ResultType; + + // Process entities (i.e. array members, base classes, or class fields) by + // adding an initialization expression to InitExprs for each entity to + // initialize. + auto ProcessEntities = [&](auto Range) -> bool { + for (InitializedEntity SubEntity : Range) { + // Unions should only have one initializer expression. + // If there are more initializers than it will be caught when we check + // whether Index equals Args.size(). + if (ArgIndexToProcess == 1 && Entity.getType()->isUnionType()) + return true; + + // Unnamed bitfields should not be initialized at all, either with an arg + // or by default. + if (SubEntity.getKind() == InitializedEntity::EK_Member && + cast(SubEntity.getDecl())->isUnnamedBitfield()) + continue; + + if (ArgIndexToProcess < Args.size()) { + // There are still expressions in Args that haven't been processed. + // Let's match them to the current entity to initialize. + Expr *E = Args[ArgIndexToProcess++]; + + // Incomplete array types indicate flexible array members. Do not allow + // paren list initializations of structs with these members, as GCC + // doesn't either. + if (SubEntity.getKind() == InitializedEntity::EK_Member) { + auto *FD = cast(SubEntity.getDecl()); + if (FD->getType()->isIncompleteArrayType()) { + if (!VerifyOnly) { + S.Diag(E->getBeginLoc(), diag::err_flexible_array_init) + << SourceRange(E->getBeginLoc(), E->getEndLoc()); + S.Diag(FD->getLocation(), diag::note_flexible_array_member) << FD; + } + Sequence.SetFailed( + InitializationSequence::FK_ParenthesizedListInitFailed); + return false; + } + } + + 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()); + } + } else { + // We've processed all of the args, but there are still entities that + // have to be initialized. + if (SubEntity.getKind() == InitializedEntity::EK_Member) { + // C++ [dcl.init]p17.6.2.2 + // The remaining elements are initialized with their default member + // initializers, if any + const auto *FD = cast(SubEntity.getDecl()); + if (Expr *ICE = FD->getInClassInitializer()) { + InitExprs.push_back(ICE); + continue; + }; + } + // Remaining class elements without default member initializers and + // array elements are value initialized: + // + // C++ [dcl.init]p17.6.2.2 + // The remaining elements...otherwise are value initialzed + // + // C++ [dcl.init]p17.5 + // if the destination type is an array, the object is initialized as + // . follows. Let x1, . . . , xk be the elements of the expression-list + // ...Let n denote the array size...the ith array element is...value- + // initialized for each k < i <= n. + InitializationKind SubKind = InitializationKind::CreateValue( + Kind.getLocation(), Kind.getLocation(), Kind.getLocation()); + InitializationSequence Seq(S, SubEntity, SubKind, None); + TryValueInitialization(S, SubEntity, SubKind, Seq); + if (Seq.Failed()) { + if (!VerifyOnly) + Seq.Diagnose(S, SubEntity, SubKind, None); + return false; + } + if (!VerifyOnly) { + ExprResult ER = Seq.Perform(S, SubEntity, SubKind, None); + InitExprs.push_back(ER.get()); + } + } + } + return true; + }; + + if (const ArrayType *AT = + S.getASTContext().getAsArrayType(Entity.getType())) { + + SmallVector ElementEntities; + uint64_t ArrayLength; + // C++ [dcl.init]p17.5 + // if the destination type is an array, the object is initialized as + // follows. Let x1, . . . , xk be the elements of the expression-list. If + // the destination type is an array of unknown bound, it is define as + // having k elements. + 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 (!ProcessEntities(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 (!ProcessEntities(BaseRange)) + return; + + if (!ProcessEntities(FieldRange)) + return; + + ResultType = Entity.getType(); + } + + // Not all of the args have been processed, so there must've been more args + // then were required to initialize the element. + if (ArgIndexToProcess < Args.size()) { + Sequence.SetFailed(InitializationSequence::FK_ParenthesizedListInitFailed); + if (!VerifyOnly) { + QualType T = Entity.getType(); + int InitKind = T->isArrayType() ? 0 : T->isUnionType() ? 3 : 4; + S.Diag(Kind.getLocation(), diag::err_excess_initializers) + << InitKind << Args[ArgIndexToProcess]->getSourceRange(); + } + return; + } + + if (VerifyOnly) { + Sequence.setSequenceKind(InitializationSequence::NormalSequence); + Sequence.AddParenthesizedListInitStep(Entity.getType()); + } else if (Result) { + *Result = CXXParenListInitExpr::Create(S.getASTContext(), InitExprs, + ResultType, Kind.getLocation()); + S.Diag(Kind.getLocation(), + diag::warn_cxx17_compat_aggregate_init_paren_list) + << Kind.getLocation() << ResultType; + } + + return; +} + /// Attempt a user-defined conversion between two types (C++ [dcl.init]), /// which enumerates all conversion functions and performs overload resolution /// to select the best. @@ -5915,7 +6104,11 @@ TryListInitialization(S, Entity, Kind, cast(Initializer), *this, TreatUnavailableAsInvalid); AddParenthesizedArrayInitStep(DestType); - } else if (DestAT->getElementType()->isCharType()) + } else if (S.getLangOpts().CPlusPlus20 && !TopLevelOfInitList && + Kind.getKind() == InitializationKind::IK_Direct) + 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); @@ -5962,18 +6155,49 @@ 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 if the + // failed candidate set has functions 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) { + // 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; } @@ -8233,6 +8457,7 @@ case SK_ConstructorInitializationFromList: case SK_StdInitializerListConstructorCall: case SK_ZeroInitialization: + case SK_ParenthesizedListInit: break; } @@ -8922,6 +9147,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; + } } } @@ -9523,6 +9756,10 @@ diag::note_explicit_ctor_deduction_guide_here) << false; break; } + + case FK_ParenthesizedListInitFailed: + TryOrBuildParenListInitialization(S, Entity, Kind, Args, *this, + /*VerifyOnly=*/false); } PrintInitLocationNote(S, Entity); @@ -9689,6 +9926,10 @@ case FK_ExplicitConstructor: OS << "list copy initialization chose explicit constructor"; break; + + case FK_ParenthesizedListInitFailed: + OS << "parenthesized list initialization failed"; + break; } OS << '\n'; return; @@ -9860,6 +10101,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 << ']'; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3819,6 +3819,11 @@ return getSema().BuildEmptyCXXFoldExpr(EllipsisLoc, Operator); } + ExprResult RebuildCXXParenListInitExpr(ArrayRef Args, QualType T, + SourceLocation Loc) { + return CXXParenListInitExpr::Create(getSema().Context, Args, T, Loc); + } + /// Build a new atomic operation expression. /// /// By default, performs semantic analysis to build the new expression. @@ -13957,6 +13962,19 @@ return Result; } +template +ExprResult +TreeTransform::TransformCXXParenListInitExpr(CXXParenListInitExpr *E) { + SmallVector TransformedInits; + ArrayRef InitExprs = E->getInitExprs(); + if (TransformExprs(reinterpret_cast(InitExprs.data()), + InitExprs.size(), true, TransformedInits)) + return ExprError(); + + return getDerived().RebuildCXXParenListInitExpr( + TransformedInits, E->getType(), E->getBeginLoc()); +} + template ExprResult TreeTransform::TransformCXXStdInitializerListExpr( diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2167,6 +2167,15 @@ E->Opcode = (BinaryOperatorKind)Record.readInt(); } +void ASTStmtReader::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) { + VisitExpr(E); + E->Loc = readSourceLocation(); + E->NumExprs = Record.readInt(); + for (unsigned I = 0; I < E->NumExprs; I++) + E->getTrailingObjects()[I] = Record.readSubExpr(); + E->updateDependence(); +} + void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) { VisitExpr(E); E->SourceExpr = Record.readSubExpr(); @@ -3958,6 +3967,11 @@ S = new (Context) CXXFoldExpr(Empty); break; + case EXPR_CXX_PAREN_LIST_INIT: + S = CXXParenListInitExpr::CreateEmpty( + Context, /*numExprs=*/Record[ASTStmtReader::NumExprFields], Empty); + break; + case EXPR_OPAQUE_VALUE: S = new (Context) OpaqueValueExpr(Empty); break; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -735,6 +735,7 @@ RECORD(EXPR_USER_DEFINED_LITERAL); RECORD(EXPR_CXX_STD_INITIALIZER_LIST); RECORD(EXPR_CXX_BOOL_LITERAL); + RECORD(EXPR_CXX_PAREN_LIST_INIT); RECORD(EXPR_CXX_NULL_PTR_LITERAL); RECORD(EXPR_CXX_TYPEID_EXPR); RECORD(EXPR_CXX_TYPEID_TYPE); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -2081,6 +2081,16 @@ Code = serialization::EXPR_CXX_FOLD; } +void ASTStmtWriter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) { + VisitExpr(E); + Record.AddSourceLocation(E->getExprLoc()); + ArrayRef InitExprs = E->getInitExprs(); + Record.push_back(InitExprs.size()); + for (Expr *InitExpr : E->getInitExprs()) + Record.AddStmt(InitExpr); + Code = serialization::EXPR_CXX_PAREN_LIST_INIT; +} + void ASTStmtWriter::VisitOpaqueValueExpr(OpaqueValueExpr *E) { VisitExpr(E); Record.AddStmt(E->getSourceExpr()); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1898,6 +1898,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 diff --git a/clang/test/CXX/class/class.compare/class.spaceship/p1.cpp b/clang/test/CXX/class/class.compare/class.spaceship/p1.cpp --- a/clang/test/CXX/class/class.compare/class.spaceship/p1.cpp +++ b/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 ); diff --git a/clang/test/CXX/drs/dr2xx.cpp b/clang/test/CXX/drs/dr2xx.cpp --- a/clang/test/CXX/drs/dr2xx.cpp +++ b/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 { diff --git a/clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp b/clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp --- a/clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp +++ b/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; diff --git a/clang/test/CodeGen/paren-list-agg-init.cpp b/clang/test/CodeGen/paren-list-agg-init.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/paren-list-agg-init.cpp @@ -0,0 +1,268 @@ +// RUN: %clang_cc1 -std=c++20 %s -emit-llvm -triple x86_64-unknown-linux-gnu -o - | FileCheck %s + +// CHECK-DAG: [[STRUCT_A:%.*]] = type { i8, double } +struct A { + char i; + double j; +}; + +// CHECK-DAG: [[STRUCT_B:%.*]] = type { [[STRUCT_A]], i32 } +struct B { + A a; + int b; +}; + +// CHECK-DAG: [[STRUCT_C:%.*]] = type <{ [[STRUCT_B]], [[STRUCT_A]], i32, [4 x i8] }> +struct C : public B, public A { + int c; +}; + +// CHECK-DAG: [[STRUCT_D:%.*]] = type { [[STRUCT_A]], [[STRUCT_A]], i8, [[STRUCT_A]] } +struct D { + A a; + A b = A{2, 2.0}; + unsigned : 2; + A c; +}; + +// CHECK-DAG: [[UNION_U:%.*]] = type { [[STRUCT_A]] } +union U { + unsigned : 1; + A a; + char b; +}; + +// CHECK-DAG: [[A1:@.*a1.*]] = internal constant [[STRUCT_A]] { i8 3, double 2.000000e+00 }, align 8 +constexpr A a1(3.1, 2.0); +// CHECK-DAG: [[A2:@.*a2.*]] = internal constant [[STRUCT_A]] { i8 99, double 0.000000e+00 }, align 8 +constexpr auto a2 = static_cast('c'); +// CHECK-DAG: [[B1:@.*b1.*]] = internal constant [[STRUCT_B]] { [[STRUCT_A]] { i8 99, double 0.000000e+00 }, i32 0 }, align 8 +constexpr B b1(A('c')); +// CHECK-DAG: [[C1:@.*c1.*]] = internal constant { [[STRUCT_A]], i32, [4 x i8], i8, double, i32 } { [[STRUCT_A]] { i8 99, double 0.000000e+00 }, i32 0, [4 x i8] undef, i8 3, double 2.000000e+00, i32 0 }, align +constexpr C c1(b1, a1); +// CHECK-DAG: [[U1:@.*u1.*]] = internal constant [[UNION_U]] { [[STRUCT_A]] { i8 1, double 1.000000e+00 } }, align 8 +constexpr U u1(A(1, 1)); +// CHECK-DAG: [[D1:@.*d1.*]] = internal constant { [[STRUCT_A]], [[STRUCT_A]], [8 x i8], [[STRUCT_A]] } { [[STRUCT_A]] { i8 2, double 2.000000e+00 }, [[STRUCT_A]] { i8 2, double 2.000000e+00 }, [8 x i8] undef, [[STRUCT_A]] zeroinitializer }, align 8 +constexpr D d1(A(2, 2)); +// CHECK-DAG: [[ARR1:@.*arr1.*]] = internal constant [3 x i32] [i32 1, i32 2, i32 0], align 4 +constexpr int arr1[3](1, 2); +// CHECK-DAG: [[ARR4:@.*arr4.*]] = internal constant [1 x i32] [i32 1], align 4 +constexpr int arr4[](1); +// CHECK-DAG: [[ARR5:@.*arr5.*]] = internal constant [2 x i32] [i32 2, i32 0], align 4 +constexpr int arr5[2](2); + +// CHECK: define dso_local { i8, double } @{{.*foo1.*}} +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_A]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[A1]], i64 16, i1 false) +// CHECK-NEXT: [[TMP_0:%.*]] = load { i8, double }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { i8, double } [[TMP_0]] +A foo1() { + return a1; +} + +// CHECK: define dso_local void @{{.*foo2.*}}(ptr noalias sret([[STRUCT_B]]) align 8 [[AGG_RESULT:%.*]]) +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT]], ptr align 8 [[B1]], i64 24, i1 false) +// CHECK-NEXT: ret void +B foo2() { + return b1; +} + +// CHECK: define dso_local void @{{.*foo3.*}}(ptr noalias sret([[STRUCT_C]]) align 8 [[AGG_RESULT:%.*]]) +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT]], ptr align 8 [[C1]], i64 48, i1 false) +// CHECK-NEXT: ret void +C foo3() { + return c1; +} + +// CHECK: define dso_local void @{{.*foo4.*}} +// CHECK-NEXT: entry: +// CHECK-NEXT: [[C2:%.*]] = alloca [[STRUCT_C:%.*]], align 8 +// CHECK-NEXT: [[REF_TMP:%.*]] = alloca [[STRUCT_B:%.*]], align 8 +// CHECK-NEXT: [[REF_TMP_1:%.*]] = alloca [[STRUCT_A:%.*]], align 8 +// CHECK-NEXT: [[A:%.*]] = getelementptr inbounds [[STRUCT_B]], ptr [[REF_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[I:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[A]], i32 0, i32 0 +// CHECK-NEXT: store i8 1, ptr [[I]], align 8 +// CHECK-NEXT: [[J:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[A]], i32 0, i32 1 +// CHECK-NEXT: store double 1.000000e+00, ptr [[J]], 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 [[C2]], ptr align 8 [[REF_TMP]], i64 24, i1 false) +// CHECK-NEXT: [[TMP_0:%.*]] = getelementptr inbounds i8, ptr [[C2]], i64 24 +// CHECK-NEXT: [[I2:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[REF_TMP_1]], i32 0, i32 0 +// CHECK-NEXT: store i8 97, ptr [[I2]], align 8 +// CHECK-NEXT: [[J3:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[REF_TMP_1]], i32 0, i32 1 +// CHECK-NEXT: store double 0.000000e+00, ptr [[J3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP_0]], ptr align 8 [[REF_TMP_1]], i64 16, i1 false) +// CHECK-NEXT: [[C:%.*]] = getelementptr inbounds %struct.C, ptr %c2, i32 0, i32 2 +// CHECK-NEXT: store i32 2, ptr %c, align 8 +// CHECK-NEXT: ret void +void foo4() { + C c2(B(A(1, 1), 1), A('a'), 2); +} + +// CHECK: define dso_local { i64, double } @{{.*foo5.*}} +// CHECK-NEXT: entry: +// CHECK-NEXT [[RETVAL:%.*]] = alloca [[UNION_U]], align 8 +// CHECK-NEXT call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[U1]], i64 16, i1 false) +// CHECK-NEXT [[COERCE_DIVE:%.*]] = getelementptr inbounds [[UNION_U]], ptr %retval, i32 0, i32 0 +// CHECK-NEXT [[TMP_0:%.*]] = load { i64, double }, ptr [[COERCE_DIVE]], align 8 +// CHECK-NEXT ret { i64, double } [[TMP_0]] +U foo5() { + return u1; +} + + +// CHECK: define dso_local { i64, double } @{{.*foo6.*}}(i8 [[A_COERCE_0:%.*]], double [[A_COERCE_1:%.*]]) +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[UNION_U]], align 8 +// CHECK-NEXT: [[A:%.*]] = alloca [[STRUCT_A]], align 8 +// CHECK-NEXT: [[TMP_0:%.*]] = getelementptr inbounds { i8, double }, ptr [[A]], i32 0, i32 0 +// CHECK-NEXT: store i8 [[A_COERCE_0]], ptr [[TMP_0]], align 8 +// CHECK-NEXT: [[TMP_1:%.*]] = getelementptr inbounds { i8, double }, ptr [[A]], i32 0, i32 1 +// CHECK-NEXT: store double [[A_COERCE_1]], ptr [[TMP_1]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[A]], i64 16, i1 false) +// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds [[UNION_U]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP_2:%.*]] = load { i64, double }, ptr [[COERCE_DIVE:%.*]], align 8 +// CHECK-NEXT: ret { i64, double } [[TMP_2]] +U foo6(A a) { + return U(a); +} + +// CHECK: define dso_local void @{{.*foo7.*}} +// CHECK-NEXT: entry: +// CHECK-NEXT: [[D:%.*]] = alloca [[STRUCT_D:%.*]], align 8 +// CHECK-NEXT: [[A:%.*]] = getelementptr inbounds [[STRUCT_D]], ptr [[D]], i32 0, i32 0 +// CHECK-NEXT: [[I]] = getelementptr inbounds [[STRUCT_A:%.*]], ptr [[A]], i32 0, i32 0 +// CHECK-NEXT: store i8 1, ptr [[I]], align 8 +// CHECK-NEXT: [[J:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[A]], i32 0, i32 1 +// CHECK-NEXT: store double 1.000000e+00, ptr [[J]], align 8 +// CHECK-NEXT: [[B:%.*]] = getelementptr inbounds [[STRUCT_D]], ptr [[D]], i32 0, i32 1 +// CHECK-NEXT: [[I1:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[B]], i32 0, i32 0 +// CHECK-NEXT: store i8 11, ptr [[I1]], align 8 +// CHECK-NEXT: [[J2:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[B]], i32 0, i32 1 +// CHECK-NEXT: store double 1.100000e+01, ptr [[J2]], align 8 +// CHECK-NEXT: [[C:%.*]] = getelementptr inbounds [[STRUCT_D]], ptr [[D]], i32 0, i32 3 +// CHECK-NEXT: [[I3:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[C]], i32 0, i32 0 +// CHECK-NEXT: store i8 111, ptr [[I3]], align 8 +// CHECK-NEXT: [[J4:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[C]], i32 0, i32 1 +// CHECK-NEXT: store double 1.110000e+02, ptr [[J4]], align 8 +// CHECK-NEXT: ret void +void foo7() { + D d(A(1, 1), A(11, 11), A(111, 111)); +} + +// CHECK: dso_local void @{{.*foo8.*}}(ptr noalias sret([[STRUCT_D]]) align 8 [[AGG_RESULT:%.*]]) +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT]], ptr align 8 [[D1]], i64 56, i1 false) +// CHECK-NEXT: ret void +D foo8() { + return d1; +} + +// CHECK: define dso_local void @{{.*foo9.*}} +// CHECK-NEXT: entry: +// CHECK-NEXT: [[D:%.*]] = alloca [[STRUCT_D:%.*]], align 8 +// CHECK-NEXT: [[A:%.*]] = getelementptr inbounds [[STRUCT_D]], ptr [[D]], i32 0, i32 0 +// CHECK-NEXT: [[I]] = getelementptr inbounds [[STRUCT_A:%.*]], ptr [[A]], i32 0, i32 0 +// CHECK-NEXT: store i8 1, ptr [[I]], align 8 +// CHECK-NEXT: [[J:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[A]], i32 0, i32 1 +// CHECK-NEXT: store double 1.000000e+00, ptr [[J]], align 8 +// CHECK-NEXT: [[B:%.*]] = getelementptr inbounds [[STRUCT_D]], ptr [[D]], i32 0, i32 1 +// CHECK-NEXT: [[I1:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[B]], i32 0, i32 0 +// CHECK-NEXT: store i8 2, ptr [[I1]], align 8 +// CHECK-NEXT: [[J2:%.*]] = getelementptr inbounds [[STRUCT_A]], ptr [[B]], i32 0, i32 1 +// CHECK-NEXT: store double 2.000000e+00, ptr [[J2]], align 8 +// CHECK-NEXT: [[C:%.*]] = getelementptr inbounds [[STRUCT_D]], ptr [[D]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[C]], i8 0, i64 16, i1 false) +// CHECK-NEXT: ret void +void foo9() { + D d(A(1, 1)); +} + +// CHECK: define dso_local noundef ptr @{{.*foo10.*}}() +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr [[ARR1]] +const int* foo10() { + return arr1; +} + +// CHECK: define dso_local void @{{.*foo11.*}} +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR_2:%.*]] = alloca [3 x i32], align 4 +// CHECK-NEXT: [[ARR_3:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: store i32 [[A:%.*]], ptr [[A_ADDR]], align 4 +// CHECK-NEXT: store i32 [[B:%.*]], ptr [[B_ADDR]], align 4 +// CHECK-NEXT: [[ARRINIT_BEGIN:%.*]] = getelementptr inbounds [3 x i32], ptr [[ARR_2]], i64 0, i64 0 +// CHECK-NEXT: [[TMP_0:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP_0]], ptr [[ARRINIT_BEGIN]], align 4 +// CHECK-NEXT: [[ARRINIT_ELEM:%.*]] = getelementptr inbounds i32, ptr [[ARRINIT_BEGIN]], i64 1 +// CHECK-NEXT: [[TMP_1:%.*]] = load i32, ptr [[B_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP_1]], ptr [[ARRINIT_ELEM]], align 4 +// CHECK-NEXT: [[ARRINIT_ELEM_1:%.*]] = getelementptr inbounds i32, ptr [[ARRINIT_ELEM]], i64 1 +// CHECK-NEXT: store i32 0, ptr [[ARRINIT_ELEM_1]], align 4 +// CHECK-NEXT: [[ARRINIT_BEGIN_2:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARR_3]], i64 0, i64 0 +// CHECK-NEXT: [[TMP_2:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP_2]], ptr [[ARRINIT_BEGIN_2]], align 4 +// CHECK-NEXT: [[ARRINIT_ELEM_3:%.*]] = getelementptr inbounds i32, ptr [[ARRINIT_BEGIN_2]], i64 1 +// CHECK-NEXT: [[TMP_3:%.*]] = load i32, ptr [[B_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP_3]], ptr [[ARRINIT_ELEM_3]], align 4 +// CHECK-NEXT: ret void +void foo11(int a, int b) { + int arr2[3](a, b); + int arr3[](a, b); +} + +// CHECK: define dso_local { i8, double } @{{.*foo12.*}} +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_A]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[A2]], i64 16, i1 false) +// CHECK-NEXT: [[TMP_0:%.*]] = load { i8, double }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { i8, double } [[TMP_0]] +A foo12() { + return a2; +} + +// CHECK: define dso_local noundef ptr @{{.*foo13.*}}() +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr [[ARR4]] +const int* foo13() { + return arr4; +} + +// CHECK: define dso_local noundef ptr @{{.*foo14.*}}() +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr [[ARR5]] +const int* foo14() { + return arr5; +} + +// CHECK: define dso_local void @{{.*foo15.*}} +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR_6:%.*arr6.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[REF_TMP:%.*]] = alloca [1 x i32], align 4 +// CHECK-NEXT: [[ARRINIT_BEGIN:%.*]] = getelementptr inbounds [1 x i32], ptr [[REF_TMP]], i64 0, i64 0 +// CHECK-NEXT: store i32 3, ptr [[ARRINIT_BEGIN]], align 4 +// CHECK-NEXT: store ptr [[REF_TMP]], ptr [[ARR_6]], align 8 +// CHECK-NEXT: ret void +void foo15() { + int ((&&arr6))[] = static_cast(3); +} + +// CHECK: define dso_local void @{{.*foo16.*}} +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR_7:%.*arr7.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[REF_TMP:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: [[ARRINIT_BEGIN:%.*]] = getelementptr inbounds [2 x i32], ptr [[REF_TMP]], i64 0, i64 0 +// CHECK-NEXT: store i32 4, ptr [[ARRINIT_BEGIN]], align 4 +// CHECK-NEXT: [[ARRINIT_ELEM:%.*]] = getelementptr inbounds i32, ptr [[ARRINIT_BEGIN]], i64 1 +// CHECK-NEXT: store i32 0, ptr [[ARRINIT_ELEM]], align 4 +// CHECK-NEXT: store ptr [[REF_TMP]], ptr [[ARR_7]], align 8 +// CHECK-NEXT: ret void +void foo16() { + int (&&arr7)[2] = static_cast(4); +} diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp --- a/clang/test/Lexer/cxx-features.cpp +++ b/clang/test/Lexer/cxx-features.cpp @@ -61,8 +61,7 @@ // --- C++20 features --- -#if check(aggregate_paren_init, 0, 0, 0, 0, 0, 0) -// FIXME: 201902 in C++20 +#if check(aggregate_paren_init, 0, 0, 0, 0, 201902, 201902) #error "wrong value for __cpp_aggregate_paren_init" #endif diff --git a/clang/test/PCH/cxx_paren_init.h b/clang/test/PCH/cxx_paren_init.h new file mode 100644 --- /dev/null +++ b/clang/test/PCH/cxx_paren_init.h @@ -0,0 +1,3 @@ +struct S { int i, j; }; + +constexpr S foo(int i, int j) { return S(i, j); }; diff --git a/clang/test/PCH/cxx_paren_init.cpp b/clang/test/PCH/cxx_paren_init.cpp new file mode 100644 --- /dev/null +++ b/clang/test/PCH/cxx_paren_init.cpp @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -x c++ -std=c++20 -emit-pch -o %t %S/cxx_paren_init.h +// RUN: %clang_cc1 -x c++ -std=c++20 -include-pch %t %s -S -emit-llvm -o - | FileCheck %s + +// CHECK: [[STRUCT_S:%.*]] = type { i32, i32 } +// CHECK: @{{.*s.*}} = {{(dso_local )?}}global [[STRUCT_S]] { i32 1, i32 2 }, align 4 +S s = foo(1, 2); diff --git a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp b/clang/test/SemaCXX/cxx2a-explicit-bool.cpp --- a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp +++ b/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 { diff --git a/clang/test/SemaCXX/paren-list-agg-init.cpp b/clang/test/SemaCXX/paren-list-agg-init.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/paren-list-agg-init.cpp @@ -0,0 +1,172 @@ +// RUN: %clang_cc1 -verify -std=c++20 %s -fsyntax-only +// RUN: %clang_cc1 -verify=expected,beforecxx20 -Wc++20-extensions -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; +}; + +struct E { // expected-note 3{{candidate constructor}} + struct F { + F(int, int); + }; + int a; + F f; +}; + +int getint(); // expected-note {{declared here}} + +struct F { + int a; + int b = getint(); // expected-note {{non-constexpr function 'getint' cannot be used in a constant expression}} +}; + +template +struct G { + T t1; + T t2; +}; + +struct H { + virtual void foo() = 0; +}; + +struct I : public H { // expected-note 3{{candidate constructor}} + int i, j; + void foo() override {} +}; + +struct J { + int a; + int b[]; // expected-note {{initialized flexible array member 'b' is here}} +}; + +union U { + int a; + char* b; +}; + +template +void bar() { + T t = 0; + A a(CH, 1.1); // OK; C++ paren list constructors are supported in semantic tree transformations. + // beforecxx20-warning@-1 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} +} + +template +T Construct(Args... args) { + return T(args...); // OK; variadic arguments can be used in paren list initializers. + // beforecxx20-warning@-1 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} +} + +void foo() { + A a1(1954, 9, 21); + // expected-error@-1 {{excess elements in struct initializer}} + A a2(2.1); + // expected-warning@-1 {{implicit conversion from 'double' to 'char'}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} + A a3(-1.2, 9.8); + // expected-warning@-1 {{implicit conversion from 'double' to 'char'}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} + A a4 = static_cast(1.1); + // expected-warning@-1 {{implicit conversion from 'double' to 'char'}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} + A a5 = (A)3.1; + // expected-warning@-1 {{implicit conversion from 'double' to 'char'}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} + A a6 = A(8.7); + // expected-warning@-1 {{implicit conversion from 'double' to 'char'}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} + + B b1(2022, {7, 8}); + // expected-error@-1 {{no viable conversion from 'int' to 'A'}} + B b2(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}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} + // beforecxx20-warning@-3 {{aggregate initialization of type 'B' from a parenthesized list of values is a C++20 extension}} + + C c(A(1), 1, 2, 3, 4); + // expected-error@-1 {{array initializer must be an initializer list}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} + D d1(1); + // expected-error@-1 {{no viable conversion from 'int' to 'C'}} + D d2(C(1)); + // expected-error@-1 {{functional-style cast from 'int' to 'C' is not allowed}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'D' from a parenthesized list of values is a C++20 extension}} + D d3(C(A(1)), 1); + // expected-error@-1 {{no viable conversion from 'int' to 'A'}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} + // beforecxx20-warning@-3 {{aggregate initialization of type 'C' from a parenthesized list of values is a C++20 extension}} + + int arr1[](0, 1, 2, A(1)); + // expected-error@-1 {{no viable conversion from 'A' to 'int'}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} + + int arr2[2](0, 1, 2); + // expected-error@-1 {{excess elements in array initializer}} + + // We should not build paren list initilizations for IK_COPY. + int arr3[1] = 1; + // expected-error@-1 {{array initializer must be an initializer list}} + + U u1("abcd"); + // expected-error@-1 {{cannot initialize a member subobject of type 'int' with an lvalue of type 'const char[5]'}} + U u2(1, "efgh"); + // expected-error@-1 {{excess elements in union initializer}} + + E e1(1); + // expected-error@-1 {{no matching constructor for initialization of 'E'}} + + constexpr F f1(1); + // expected-error@-1 {{constexpr variable 'f1' must be initialized by a constant expression}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'const F' from a parenthesized list of values is a C++20 extension}} + + constexpr F f2(1, 1); // OK: f2.b is initialized by a constant expression. + // beforecxx20-warning@-1 {{aggregate initialization of type 'const F' from a parenthesized list of values is a C++20 extension}} + + bar(); + + G g('b', 'b'); + // beforecxx20-warning@-1 {{aggregate initialization of type 'G' from a parenthesized list of values is a C++20 extension}} + + A a7 = Construct('i', 2.2); + // beforecxx20-note@-1 {{in instantiation of function template specialization 'Construct' requested here}} + + int arr4[](1, 2); + // beforecxx20-warning@-1 {{aggregate initialization of type 'int[2]' from a parenthesized list of values is a C++20 extension}} + + int arr5[2](1, 2); + // beforecxx20-warning@-1 {{aggregate initialization of type 'int[2]' from a parenthesized list of values is a C++20 extension}} + + I i(1, 2); + // expected-error@-1 {{no matching constructor for initialization of 'I'}} + + J j(1, {2, 3}); + // expected-error@-1 {{initialization of flexible array member is not allowed}} + + static_assert(__is_trivially_constructible(A, char, double)); + static_assert(__is_trivially_constructible(A, char, int)); + static_assert(__is_trivially_constructible(A, char)); + + static_assert(__is_trivially_constructible(D, C, A, int)); + static_assert(__is_trivially_constructible(D, C)); + + static_assert(__is_trivially_constructible(int[2], int, int)); + static_assert(__is_trivially_constructible(int[2], int, double)); + static_assert(__is_trivially_constructible(int[2], int)); +} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -2139,6 +2139,7 @@ void VisitLambdaExpr(const LambdaExpr *E); void VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E); void VisitRequiresExpr(const RequiresExpr *E); + void VisitCXXParenListInitExpr(const CXXParenListInitExpr *E); void VisitOMPExecutableDirective(const OMPExecutableDirective *D); void VisitOMPLoopBasedDirective(const OMPLoopBasedDirective *D); void VisitOMPLoopDirective(const OMPLoopDirective *D); @@ -3000,6 +3001,9 @@ for (ParmVarDecl *VD : E->getLocalParameters()) AddDecl(VD); } +void EnqueueVisitor::VisitCXXParenListInitExpr(const CXXParenListInitExpr *E) { + EnqueueChildren(E); +} void EnqueueVisitor::VisitPseudoObjectExpr(const PseudoObjectExpr *E) { // Treat the expression like its syntactic form. Visit(E->getSyntacticForm()); @@ -5579,6 +5583,8 @@ return cxstring::createRef("ConceptSpecializationExpr"); case CXCursor_RequiresExpr: return cxstring::createRef("RequiresExpr"); + case CXCursor_CXXParenListInitExpr: + return cxstring::createRef("CXXParenListInitExpr"); case CXCursor_UnexposedStmt: return cxstring::createRef("UnexposedStmt"); case CXCursor_DeclStmt: diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -643,6 +643,10 @@ K = CXCursor_RequiresExpr; break; + case Stmt::CXXParenListInitExprClass: + K = CXCursor_CXXParenListInitExpr; + break; + case Stmt::MSDependentExistsStmtClass: K = CXCursor_UnexposedStmt; break; diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1156,7 +1156,7 @@ Parenthesized initialization of aggregates P0960R3 - No + Clang 16 P1975R0