From 410306bf6ed906af77978caa199e5d96376bae66 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 12 Dec 2016 02:53:20 +0000 Subject: [PATCH] Add two new AST nodes to represent initialization of an array in terms of initialization of each array element: * ArrayInitLoopExpr is a prvalue of array type with two subexpressions: a common expression (an OpaqueValueExpr) that represents the up-front computation of the source of the initialization, and a subexpression representing a per-element initializer * ArrayInitIndexExpr is a prvalue of type size_t representing the current position in the loop This will be used to replace the creation of explicit index variables in lambda capture of arrays and copy/move construction of classes with array elements, and also C++17 structured bindings of arrays by value (which inexplicably allow copying an array by value, unlike all of C++'s other array declarations). No uses of these nodes are introduced by this change, however. llvm-svn: 289413 --- clang/include/clang/AST/Expr.h | 92 ++++++++++++++ clang/include/clang/AST/RecursiveASTVisitor.h | 2 + clang/include/clang/Basic/StmtNodes.td | 2 + clang/include/clang/Sema/Initialization.h | 46 +++++-- clang/include/clang/Serialization/ASTBitCodes.h | 8 +- clang/lib/AST/ASTDumper.cpp | 10 ++ clang/lib/AST/Expr.cpp | 2 + clang/lib/AST/ExprClassification.cpp | 2 + clang/lib/AST/ExprConstant.cpp | 60 +++++++++ clang/lib/AST/ItaniumMangle.cpp | 2 + clang/lib/AST/StmtPrinter.cpp | 12 ++ clang/lib/AST/StmtProfile.cpp | 8 ++ clang/lib/CodeGen/CGExprAgg.cpp | 80 ++++++++++++ clang/lib/CodeGen/CGExprScalar.cpp | 6 + clang/lib/CodeGen/CodeGenFunction.h | 35 ++++++ clang/lib/Sema/SemaExceptionSpec.cpp | 2 + clang/lib/Sema/SemaInit.cpp | 154 +++++++++++++++++++++--- clang/lib/Sema/TreeTransform.h | 17 +++ clang/lib/Serialization/ASTReaderStmt.cpp | 18 +++ clang/lib/Serialization/ASTWriterStmt.cpp | 12 ++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 2 + clang/tools/libclang/CXCursor.cpp | 2 + 22 files changed, 545 insertions(+), 29 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 895ac2b6..41ae6d2 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -4333,6 +4333,98 @@ public: } }; +/// \brief Represents a loop initializing the elements of an array. +/// +/// The need to initialize the elements of an array occurs in a number of +/// contexts: +/// +/// * in the implicit copy/move constructor for a class with an array member +/// * when a lambda-expression captures an array by value +/// * when a decomposition declaration decomposes an array +/// +/// There are two subexpressions: a common expression (the source array) +/// that is evaluated once up-front, and a per-element initializer that +/// runs once for each array element. +/// +/// Within the per-element initializer, the common expression may be referenced +/// via an OpaqueValueExpr, and the current index may be obtained via an +/// ArrayInitIndexExpr. +class ArrayInitLoopExpr : public Expr { + Stmt *SubExprs[2]; + + explicit ArrayInitLoopExpr(EmptyShell Empty) + : Expr(ArrayInitLoopExprClass, Empty), SubExprs{} {} + +public: + explicit ArrayInitLoopExpr(QualType T, Expr *CommonInit, Expr *ElementInit) + : Expr(ArrayInitLoopExprClass, T, VK_RValue, OK_Ordinary, false, + CommonInit->isValueDependent() || ElementInit->isValueDependent(), + T->isInstantiationDependentType(), + CommonInit->containsUnexpandedParameterPack() || + ElementInit->containsUnexpandedParameterPack()), + SubExprs{CommonInit, ElementInit} {} + + /// Get the common subexpression shared by all initializations (the source + /// array). + OpaqueValueExpr *getCommonExpr() const { + return cast(SubExprs[0]); + } + + /// Get the initializer to use for each array element. + Expr *getSubExpr() const { return cast(SubExprs[1]); } + + llvm::APInt getArraySize() const { + return cast(getType()->castAsArrayTypeUnsafe()) + ->getSize(); + } + + static bool classof(const Stmt *S) { + return S->getStmtClass() == ArrayInitLoopExprClass; + } + + SourceLocation getLocStart() const LLVM_READONLY { + return getCommonExpr()->getLocStart(); + } + SourceLocation getLocEnd() const LLVM_READONLY { + return getCommonExpr()->getLocEnd(); + } + + child_range children() { + return child_range(SubExprs, SubExprs + 2); + } + + friend class ASTReader; + friend class ASTStmtReader; + friend class ASTStmtWriter; +}; + +/// \brief Represents the index of the current element of an array being +/// initialized by an ArrayInitLoopExpr. This can only appear within the +/// subexpression of an ArrayInitLoopExpr. +class ArrayInitIndexExpr : public Expr { + explicit ArrayInitIndexExpr(EmptyShell Empty) + : Expr(ArrayInitIndexExprClass, Empty) {} + +public: + explicit ArrayInitIndexExpr(QualType T) + : Expr(ArrayInitIndexExprClass, T, VK_RValue, OK_Ordinary, + false, false, false, false) {} + + static bool classof(const Stmt *S) { + return S->getStmtClass() == ArrayInitIndexExprClass; + } + + SourceLocation getLocStart() const LLVM_READONLY { return SourceLocation(); } + SourceLocation getLocEnd() const LLVM_READONLY { return SourceLocation(); } + + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + friend class ASTReader; + friend class ASTStmtReader; +}; + /// \brief Represents an implicitly-generated value initialization of /// an object of a given type. /// diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index de712f1..9e79298 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2399,6 +2399,8 @@ DEF_TRAVERSE_STMT(ExtVectorElementExpr, {}) DEF_TRAVERSE_STMT(GNUNullExpr, {}) DEF_TRAVERSE_STMT(ImplicitValueInitExpr, {}) DEF_TRAVERSE_STMT(NoInitExpr, {}) +DEF_TRAVERSE_STMT(ArrayInitLoopExpr, {}) +DEF_TRAVERSE_STMT(ArrayInitIndexExpr, {}) DEF_TRAVERSE_STMT(ObjCBoolLiteralExpr, {}) DEF_TRAVERSE_STMT(ObjCEncodeExpr, { diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index a7916ef..0ccfef31 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -85,6 +85,8 @@ def DesignatedInitExpr : DStmt; def DesignatedInitUpdateExpr : DStmt; def ImplicitValueInitExpr : DStmt; def NoInitExpr : DStmt; +def ArrayInitLoopExpr : DStmt; +def ArrayInitIndexExpr : DStmt; def ParenListExpr : DStmt; def VAArgExpr : DStmt; def GenericSelectionExpr : DStmt; diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h index c7b163e..ab108c8 100644 --- a/clang/include/clang/Sema/Initialization.h +++ b/clang/include/clang/Sema/Initialization.h @@ -120,6 +120,16 @@ private: bool NRVO; }; + struct VD { + /// \brief The VarDecl, FieldDecl, or BindingDecl being initialized. + ValueDecl *VariableOrMember; + + /// \brief When Kind == EK_Member, whether this is an implicit member + /// initialization in a copy or move constructor. These can perform array + /// copies. + bool IsImplicitFieldInit; + }; + struct C { /// \brief The name of the variable being captured by an EK_LambdaCapture. IdentifierInfo *VarID; @@ -129,9 +139,8 @@ private: }; union { - /// \brief When Kind == EK_Variable, EK_Member or EK_Binding, the VarDecl, - /// FieldDecl or BindingDecl, respectively. - ValueDecl *VariableOrMember; + /// \brief When Kind == EK_Variable, EK_Member or EK_Binding, the variable. + VD Variable; /// \brief When Kind == EK_RelatedResult, the ObjectiveC method where /// result type was implicitly changed to accommodate ARC semantics. @@ -165,7 +174,7 @@ private: /// \brief Create the initialization entity for a variable. InitializedEntity(VarDecl *Var, EntityKind EK = EK_Variable) : Kind(EK), Parent(nullptr), Type(Var->getType()), - ManglingNumber(0), VariableOrMember(Var) { } + ManglingNumber(0), Variable{Var, false} { } /// \brief Create the initialization entity for the result of a /// function, throwing an object, performing an explicit cast, or @@ -179,9 +188,11 @@ private: } /// \brief Create the initialization entity for a member subobject. - InitializedEntity(FieldDecl *Member, const InitializedEntity *Parent) + InitializedEntity(FieldDecl *Member, const InitializedEntity *Parent, + bool Implicit) : Kind(EK_Member), Parent(Parent), Type(Member->getType()), - ManglingNumber(0), VariableOrMember(Member) { } + ManglingNumber(0), Variable{Member, Implicit} { + } /// \brief Create the initialization entity for an array element. InitializedEntity(ASTContext &Context, unsigned Index, @@ -299,15 +310,17 @@ public: /// \brief Create the initialization entity for a member subobject. static InitializedEntity InitializeMember(FieldDecl *Member, - const InitializedEntity *Parent = nullptr) { - return InitializedEntity(Member, Parent); + const InitializedEntity *Parent = nullptr, + bool Implicit = false) { + return InitializedEntity(Member, Parent, Implicit); } /// \brief Create the initialization entity for a member subobject. static InitializedEntity InitializeMember(IndirectFieldDecl *Member, - const InitializedEntity *Parent = nullptr) { - return InitializedEntity(Member->getAnonField(), Parent); + const InitializedEntity *Parent = nullptr, + bool Implicit = false) { + return InitializedEntity(Member->getAnonField(), Parent, Implicit); } /// \brief Create the initialization entity for an array element. @@ -401,6 +414,12 @@ public: getType()->getAsArrayTypeUnsafe()); } + /// \brief Is this the implicit initialization of a member of a class from + /// a defaulted constructor? + bool isImplicitMemberInitializer() const { + return getKind() == EK_Member && Variable.IsImplicitFieldInit; + } + /// \brief Determine the location of the 'return' keyword when initializing /// the result of a function call. SourceLocation getReturnLoc() const { @@ -708,6 +727,10 @@ public: /// \brief An initialization that "converts" an Objective-C object /// (not a point to an object) to another Objective-C object type. SK_ObjCObjectConversion, + /// \brief Array indexing for initialization by elementwise copy. + SK_ArrayLoopIndex, + /// \brief Array initialization by elementwise copy. + SK_ArrayLoopInit, /// \brief Array initialization (from an array rvalue). /// This is a GNU C extension. SK_ArrayInit, @@ -1096,6 +1119,9 @@ public: /// always a no-op. void AddObjCObjectConversionStep(QualType T); + /// \brief Add an array initialization loop step. + void AddArrayInitLoopStep(QualType T, QualType EltTy); + /// \brief Add an array initialization step. void AddArrayInitStep(QualType T); diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 48d76b0..32e8c42 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1292,10 +1292,14 @@ namespace clang { EXPR_DESIGNATED_INIT, /// \brief A DesignatedInitUpdateExpr record. EXPR_DESIGNATED_INIT_UPDATE, - /// \brief An ImplicitValueInitExpr record. - EXPR_IMPLICIT_VALUE_INIT, /// \brief An NoInitExpr record. EXPR_NO_INIT, + /// \brief An ArrayInitLoopExpr record. + EXPR_ARRAY_INIT_LOOP, + /// \brief An ArrayInitIndexExpr record. + EXPR_ARRAY_INIT_INDEX, + /// \brief An ImplicitValueInitExpr record. + EXPR_IMPLICIT_VALUE_INIT, /// \brief A VAArgExpr record. EXPR_VA_ARG, /// \brief An AddrLabelExpr record. diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp index 2481288..62261cc 100644 --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -517,6 +517,8 @@ namespace { void VisitFloatingLiteral(const FloatingLiteral *Node); void VisitStringLiteral(const StringLiteral *Str); void VisitInitListExpr(const InitListExpr *ILE); + void VisitArrayInitLoopExpr(const ArrayInitLoopExpr *ILE); + void VisitArrayInitIndexExpr(const ArrayInitIndexExpr *ILE); void VisitUnaryOperator(const UnaryOperator *Node); void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Node); void VisitMemberExpr(const MemberExpr *Node); @@ -2024,6 +2026,14 @@ void ASTDumper::VisitInitListExpr(const InitListExpr *ILE) { } } +void ASTDumper::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E) { + VisitExpr(E); +} + +void ASTDumper::VisitArrayInitIndexExpr(const ArrayInitIndexExpr *E) { + VisitExpr(E); +} + void ASTDumper::VisitUnaryOperator(const UnaryOperator *Node) { VisitExpr(Node); OS << " " << (Node->isPostfix() ? "postfix" : "prefix") diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 776a7f9..0307648 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2899,6 +2899,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case UnaryExprOrTypeTraitExprClass: case AddrLabelExprClass: case GNUNullExprClass: + case ArrayInitIndexExprClass: case NoInitExprClass: case CXXBoolLiteralExprClass: case CXXNullPtrLiteralExprClass: @@ -2975,6 +2976,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case ExtVectorElementExprClass: case DesignatedInitExprClass: case DesignatedInitUpdateExprClass: + case ArrayInitLoopExprClass: case ParenListExprClass: case CXXPseudoDestructorExprClass: case CXXStdInitializerListExprClass: diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index aa4651d..adb74b8 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -185,6 +185,8 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::ObjCIndirectCopyRestoreExprClass: case Expr::AtomicExprClass: case Expr::CXXFoldExprClass: + case Expr::ArrayInitLoopExprClass: + case Expr::ArrayInitIndexExprClass: case Expr::NoInitExprClass: case Expr::DesignatedInitUpdateExprClass: case Expr::CoyieldExprClass: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 949ec63..61bb2b9 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -469,6 +469,10 @@ namespace { /// declaration whose initializer is being evaluated, if any. APValue *EvaluatingDeclValue; + /// The current array initialization index, if we're performing array + /// initialization. + uint64_t ArrayInitIndex = -1; + /// HasActiveDiagnostic - Was the previous diagnostic stored? If so, further /// notes attached to it will also be stored, otherwise they will not be. bool HasActiveDiagnostic; @@ -803,6 +807,20 @@ namespace { bool allowInvalidBaseExpr() const { return EvalMode == EM_DesignatorFold; } + + class ArrayInitLoopIndex { + EvalInfo &Info; + uint64_t OuterIndex; + + public: + ArrayInitLoopIndex(EvalInfo &Info) + : Info(Info), OuterIndex(Info.ArrayInitIndex) { + Info.ArrayInitIndex = 0; + } + ~ArrayInitLoopIndex() { Info.ArrayInitIndex = OuterIndex; } + + operator uint64_t&() { return Info.ArrayInitIndex; } + }; }; /// Object used to treat all foldable expressions as constant expressions. @@ -6190,6 +6208,7 @@ namespace { return handleCallExpr(E, Result, &This); } bool VisitInitListExpr(const InitListExpr *E); + bool VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E); bool VisitCXXConstructExpr(const CXXConstructExpr *E); bool VisitCXXConstructExpr(const CXXConstructExpr *E, const LValue &Subobject, @@ -6272,6 +6291,35 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { FillerExpr) && Success; } +bool ArrayExprEvaluator::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E) { + if (E->getCommonExpr() && + !Evaluate(Info.CurrentCall->createTemporary(E->getCommonExpr(), false), + Info, E->getCommonExpr()->getSourceExpr())) + return false; + + auto *CAT = cast(E->getType()->castAsArrayTypeUnsafe()); + + uint64_t Elements = CAT->getSize().getZExtValue(); + Result = APValue(APValue::UninitArray(), Elements, Elements); + + LValue Subobject = This; + Subobject.addArray(Info, E, CAT); + + bool Success = true; + for (EvalInfo::ArrayInitLoopIndex Index(Info); Index != Elements; ++Index) { + if (!EvaluateInPlace(Result.getArrayInitializedElt(Index), + Info, Subobject, E->getSubExpr()) || + !HandleLValueArrayAdjustment(Info, E, Subobject, + CAT->getElementType(), 1)) { + if (!Info.noteFailure()) + return false; + Success = false; + } + } + + return Success; +} + bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { return VisitCXXConstructExpr(E, This, &Result, E->getType()); } @@ -6427,6 +6475,16 @@ public: bool VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *E) { return Success(E->getValue(), E); } + + bool VisitArrayInitIndexExpr(const ArrayInitIndexExpr *E) { + if (Info.ArrayInitIndex == uint64_t(-1)) { + // We were asked to evaluate this subexpression independent of the + // enclosing ArrayInitLoopExpr. We can't do that. + Info.FFDiag(E); + return false; + } + return Success(Info.ArrayInitIndex, E); + } // Note, GNU defines __null as an integer, not a pointer. bool VisitGNUNullExpr(const GNUNullExpr *E) { @@ -9590,6 +9648,8 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::CompoundLiteralExprClass: case Expr::ExtVectorElementExprClass: case Expr::DesignatedInitExprClass: + case Expr::ArrayInitLoopExprClass: + case Expr::ArrayInitIndexExprClass: case Expr::NoInitExprClass: case Expr::DesignatedInitUpdateExprClass: case Expr::ImplicitValueInitExprClass: diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 0916c01..ab3e49d 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -3301,6 +3301,8 @@ recurse: case Expr::AddrLabelExprClass: case Expr::DesignatedInitUpdateExprClass: case Expr::ImplicitValueInitExprClass: + case Expr::ArrayInitLoopExprClass: + case Expr::ArrayInitIndexExprClass: case Expr::NoInitExprClass: case Expr::ParenListExprClass: case Expr::LambdaExprClass: diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 2c0b74a..65e9f82 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1705,6 +1705,18 @@ void StmtPrinter::VisitInitListExpr(InitListExpr* Node) { OS << "}"; } +void StmtPrinter::VisitArrayInitLoopExpr(ArrayInitLoopExpr *Node) { + // There's no way to express this expression in any of our supported + // languages, so just emit something terse and (hopefully) clear. + OS << "{"; + PrintExpr(Node->getSubExpr()); + OS << "}"; +} + +void StmtPrinter::VisitArrayInitIndexExpr(ArrayInitIndexExpr *Node) { + OS << "*"; +} + void StmtPrinter::VisitParenListExpr(ParenListExpr* Node) { OS << "("; for (unsigned i = 0, e = Node->getNumExprs(); i != e; ++i) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index d8185d3..b552500 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -972,6 +972,14 @@ void StmtProfiler::VisitDesignatedInitUpdateExpr( "initializer"); } +void StmtProfiler::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *S) { + VisitExpr(S); +} + +void StmtProfiler::VisitArrayInitIndexExpr(const ArrayInitIndexExpr *S) { + VisitExpr(S); +} + void StmtProfiler::VisitNoInitExpr(const NoInitExpr *S) { llvm_unreachable("Unexpected NoInitExpr in syntactic form of initializer"); } diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index c098988..d756eca 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -164,6 +164,7 @@ public: void VisitAbstractConditionalOperator(const AbstractConditionalOperator *CO); void VisitChooseExpr(const ChooseExpr *CE); void VisitInitListExpr(InitListExpr *E); + void VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E); void VisitImplicitValueInitExpr(ImplicitValueInitExpr *E); void VisitNoInitExpr(NoInitExpr *E) { } // Do nothing. void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { @@ -1308,6 +1309,85 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { cleanupDominator->eraseFromParent(); } +void AggExprEmitter::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E) { + // Emit the common subexpression. + CodeGenFunction::OpaqueValueMapping binding(CGF, E->getCommonExpr()); + + Address destPtr = EnsureSlot(E->getType()).getAddress(); + uint64_t numElements = E->getArraySize().getZExtValue(); + + if (!numElements) + return; + + // FIXME: Dig through nested ArrayInitLoopExprs to find the overall array + // size, and only emit a single loop for a multidimensional array. + + // destPtr is an array*. Construct an elementType* by drilling down a level. + llvm::Value *zero = llvm::ConstantInt::get(CGF.SizeTy, 0); + llvm::Value *indices[] = {zero, zero}; + llvm::Value *begin = Builder.CreateInBoundsGEP(destPtr.getPointer(), indices, + "arrayinit.begin"); + + QualType elementType = E->getSubExpr()->getType(); + CharUnits elementSize = CGF.getContext().getTypeSizeInChars(elementType); + CharUnits elementAlign = + destPtr.getAlignment().alignmentOfArrayElement(elementSize); + + // Prepare for a cleanup. + QualType::DestructionKind dtorKind = elementType.isDestructedType(); + Address endOfInit = Address::invalid(); + EHScopeStack::stable_iterator cleanup; + llvm::Instruction *cleanupDominator = nullptr; + if (CGF.needsEHCleanup(dtorKind)) { + endOfInit = CGF.CreateTempAlloca(begin->getType(), CGF.getPointerAlign(), + "arrayinit.endOfInit"); + CGF.pushIrregularPartialArrayCleanup(begin, endOfInit, elementType, + elementAlign, + CGF.getDestroyer(dtorKind)); + cleanup = CGF.EHStack.stable_begin(); + } else { + dtorKind = QualType::DK_none; + } + + llvm::BasicBlock *entryBB = Builder.GetInsertBlock(); + llvm::BasicBlock *bodyBB = CGF.createBasicBlock("arrayinit.body"); + + // Jump into the body. + CGF.EmitBlock(bodyBB); + llvm::PHINode *index = + Builder.CreatePHI(zero->getType(), 2, "arrayinit.index"); + index->addIncoming(zero, entryBB); + llvm::Value *element = Builder.CreateInBoundsGEP(begin, index); + + // Tell the EH cleanup that we finished with the last element. + if (endOfInit.isValid()) Builder.CreateStore(element, endOfInit); + + // Emit the actual filler expression. + { + CodeGenFunction::ArrayInitLoopExprScope Scope(CGF, index); + LValue elementLV = + CGF.MakeAddrLValue(Address(element, elementAlign), elementType); + EmitInitializationToLValue(E->getSubExpr(), elementLV); + } + + // Move on to the next element. + llvm::Value *nextIndex = Builder.CreateNUWAdd( + index, llvm::ConstantInt::get(CGF.SizeTy, 1), "arrayinit.next"); + index->addIncoming(nextIndex, Builder.GetInsertBlock()); + + // Leave the loop if we're done. + llvm::Value *done = Builder.CreateICmpEQ( + nextIndex, llvm::ConstantInt::get(CGF.SizeTy, numElements), + "arrayinit.done"); + llvm::BasicBlock *endBB = CGF.createBasicBlock("arrayinit.end"); + Builder.CreateCondBr(done, endBB, bodyBB); + + CGF.EmitBlock(endBB); + + // Leave the partial-array cleanup if we entered one. + if (dtorKind) CGF.DeactivateCleanupBlock(cleanup, cleanupDominator); +} + void AggExprEmitter::VisitDesignatedInitUpdateExpr(DesignatedInitUpdateExpr *E) { AggValueSlot Dest = EnsureSlot(E->getType()); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 2b54370..c15e580 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -311,6 +311,12 @@ public: Value *VisitInitListExpr(InitListExpr *E); + Value *VisitArrayInitIndexExpr(ArrayInitIndexExpr *E) { + assert(CGF.getArrayInitIndex() && + "ArrayInitIndexExpr not inside an ArrayInitLoopExpr?"); + return CGF.getArrayInitIndex(); + } + Value *VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E) { return EmitNullValue(E->getType()); } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 071bcb8..b9b33a8 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -918,6 +918,17 @@ public: e->getCommon()); } + /// Build the opaque value mapping for an OpaqueValueExpr whose source + /// expression is set to the expression the OVE represents. + OpaqueValueMapping(CodeGenFunction &CGF, const OpaqueValueExpr *OV) + : CGF(CGF) { + if (OV) { + assert(OV->getSourceExpr() && "wrong form of OpaqueValueMapping used " + "for OVE with no source expression"); + Data = OpaqueValueMappingData::bind(CGF, OV, OV->getSourceExpr()); + } + } + OpaqueValueMapping(CodeGenFunction &CGF, const OpaqueValueExpr *opaqueValue, LValue lvalue) @@ -1183,6 +1194,23 @@ public: CharUnits OldCXXThisAlignment; }; + /// The scope of an ArrayInitLoopExpr. Within this scope, the value of the + /// current loop index is overridden. + class ArrayInitLoopExprScope { + public: + ArrayInitLoopExprScope(CodeGenFunction &CGF, llvm::Value *Index) + : CGF(CGF), OldArrayInitIndex(CGF.ArrayInitIndex) { + CGF.ArrayInitIndex = Index; + } + ~ArrayInitLoopExprScope() { + CGF.ArrayInitIndex = OldArrayInitIndex; + } + + private: + CodeGenFunction &CGF; + llvm::Value *OldArrayInitIndex; + }; + class InlinedInheritingConstructorScope { public: InlinedInheritingConstructorScope(CodeGenFunction &CGF, GlobalDecl GD) @@ -1251,6 +1279,10 @@ private: /// this expression. Address CXXDefaultInitExprThis = Address::invalid(); + /// The current array initialization index when evaluating an + /// ArrayInitIndexExpr within an ArrayInitLoopExpr. + llvm::Value *ArrayInitIndex = nullptr; + /// The values of function arguments to use when evaluating /// CXXInheritedCtorInitExprs within this context. CallArgList CXXInheritedCtorInitExprArgs; @@ -1953,6 +1985,9 @@ public: return it->second; } + /// Get the index of the current ArrayInitLoopExpr, if any. + llvm::Value *getArrayInitIndex() { return ArrayInitIndex; } + /// getAccessedFieldNo - Given an encoded value and a result number, return /// the input field number being accessed. static unsigned getAccessedFieldNo(unsigned Idx, const llvm::Constant *Elts); diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 3561bd0..781f780 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1166,6 +1166,7 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::ExprWithCleanupsClass: case Expr::ExtVectorElementExprClass: case Expr::InitListExprClass: + case Expr::ArrayInitLoopExprClass: case Expr::MemberExprClass: case Expr::ObjCIsaExprClass: case Expr::ObjCIvarRefExprClass: @@ -1259,6 +1260,7 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::ImaginaryLiteralClass: case Expr::ImplicitValueInitExprClass: case Expr::IntegerLiteralClass: + case Expr::ArrayInitIndexExprClass: case Expr::NoInitExprClass: case Expr::ObjCEncodeExprClass: case Expr::ObjCStringLiteralClass: diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 50e7b8e..3453128 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -2910,7 +2910,7 @@ DeclarationName InitializedEntity::getName() const { case EK_Variable: case EK_Member: case EK_Binding: - return VariableOrMember->getDeclName(); + return Variable.VariableOrMember->getDeclName(); case EK_LambdaCapture: return DeclarationName(Capture.VarID); @@ -2938,7 +2938,7 @@ ValueDecl *InitializedEntity::getDecl() const { case EK_Variable: case EK_Member: case EK_Binding: - return VariableOrMember; + return Variable.VariableOrMember; case EK_Parameter: case EK_Parameter_CF_Audited: @@ -3065,6 +3065,8 @@ void InitializationSequence::Step::Destroy() { case SK_CAssignment: case SK_StringInit: case SK_ObjCObjectConversion: + case SK_ArrayLoopIndex: + case SK_ArrayLoopInit: case SK_ArrayInit: case SK_ParenthesizedArrayInit: case SK_PassByIndirectCopyRestore: @@ -3307,6 +3309,17 @@ void InitializationSequence::AddArrayInitStep(QualType T) { Steps.push_back(S); } +void InitializationSequence::AddArrayInitLoopStep(QualType T, QualType EltT) { + Step S; + S.Kind = SK_ArrayLoopIndex; + S.Type = EltT; + Steps.insert(Steps.begin(), S); + + S.Kind = SK_ArrayLoopInit; + S.Type = T; + Steps.push_back(S); +} + void InitializationSequence::AddParenthesizedArrayInitStep(QualType T) { Step S; S.Kind = SK_ParenthesizedArrayInit; @@ -3552,6 +3565,9 @@ ResolveConstructorOverload(Sema &S, SourceLocation DeclLoc, /// \brief Attempt initialization by constructor (C++ [dcl.init]), which /// enumerates the constructors of the initialized entity and performs overload /// resolution to select the best. +/// \param DestType The destination class type. +/// \param DestArrayType The destination type, which is either DestType or +/// a (possibly multidimensional) array of DestType. /// \param IsListInit Is this list-initialization? /// \param IsInitListCopy Is this non-list-initialization resulting from a /// list-initialization from {x} where x is the same @@ -3560,6 +3576,7 @@ static void TryConstructorInitialization(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, MultiExprArg Args, QualType DestType, + QualType DestArrayType, InitializationSequence &Sequence, bool IsListInit = false, bool IsInitListCopy = false) { @@ -3703,7 +3720,7 @@ static void TryConstructorInitialization(Sema &S, // subsumed by the initialization. bool HadMultipleCandidates = (CandidateSet.size() > 1); Sequence.AddConstructorInitializationStep( - Best->FoundDecl, CtorDecl, DestType, HadMultipleCandidates, + Best->FoundDecl, CtorDecl, DestArrayType, HadMultipleCandidates, IsListInit | IsInitListCopy, AsInitializerList); } @@ -3871,8 +3888,9 @@ static void TryListInitialization(Sema &S, S.IsDerivedFrom(InitList->getLocStart(), InitType, DestType)) { Expr *InitListAsExpr = InitList; TryConstructorInitialization(S, Entity, Kind, InitListAsExpr, DestType, - Sequence, /*InitListSyntax*/ false, - /*IsInitListCopy*/ true); + DestType, Sequence, + /*InitListSyntax*/false, + /*IsInitListCopy*/true); return; } } @@ -3927,7 +3945,7 @@ static void TryListInitialization(Sema &S, // - Otherwise, if T is a class type, constructors are considered. Expr *InitListAsExpr = InitList; TryConstructorInitialization(S, Entity, Kind, InitListAsExpr, DestType, - Sequence, /*InitListSyntax*/ true); + DestType, Sequence, /*InitListSyntax*/true); } else Sequence.SetFailed(InitializationSequence::FK_InitListBadDestinationType); return; @@ -4580,8 +4598,10 @@ static void TryValueInitialization(Sema &S, MultiExprArg Args(&InitListAsExpr, InitList ? 1 : 0); bool InitListSyntax = InitList; - return TryConstructorInitialization(S, Entity, Kind, Args, T, Sequence, - InitListSyntax); + // FIXME: Instead of creating a CXXConstructExpr of non-array type here, + // wrap a class-typed CXXConstructExpr in an ArrayInitLoopExpr. + return TryConstructorInitialization( + S, Entity, Kind, Args, T, Entity.getType(), Sequence, InitListSyntax); } } @@ -4604,7 +4624,8 @@ static void TryDefaultInitialization(Sema &S, // constructor for T is called (and the initialization is ill-formed if // T has no accessible default constructor); if (DestType->isRecordType() && S.getLangOpts().CPlusPlus) { - TryConstructorInitialization(S, Entity, Kind, None, DestType, Sequence); + TryConstructorInitialization(S, Entity, Kind, None, DestType, + Entity.getType(), Sequence); return; } @@ -5030,6 +5051,42 @@ static bool isExprAnUnaddressableFunction(Sema &S, const Expr *E) { cast(DRE->getDecl())); } +/// Determine whether we can perform an elementwise array copy for this kind +/// of entity. +static bool canPerformArrayCopy(const InitializedEntity &Entity) { + switch (Entity.getKind()) { + case InitializedEntity::EK_LambdaCapture: + // C++ [expr.prim.lambda]p24: + // For array members, the array elements are direct-initialized in + // increasing subscript order. + return true; + + case InitializedEntity::EK_Variable: + // C++ [dcl.decomp]p1: + // [...] each element is copy-initialized or direct-initialized from the + // corresponding element of the assignment-expression [...] + return isa(Entity.getDecl()); + + case InitializedEntity::EK_Member: + // C++ [class.copy.ctor]p14: + // - if the member is an array, each element is direct-initialized with + // the corresponding subobject of x + return Entity.isImplicitMemberInitializer(); + + case InitializedEntity::EK_ArrayElement: + // All the above cases are intended to apply recursively, even though none + // of them actually say that. + if (auto *E = Entity.getParent()) + return canPerformArrayCopy(*E); + break; + + default: + break; + } + + return false; +} + void InitializationSequence::InitializeFrom(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, @@ -5152,6 +5209,34 @@ void InitializationSequence::InitializeFrom(Sema &S, } } + // Some kinds of initialization permit an array to be initialized from + // another array of the same type, and perform elementwise initialization. + if (Initializer && isa(DestAT) && + S.Context.hasSameUnqualifiedType(Initializer->getType(), + Entity.getType()) && + canPerformArrayCopy(Entity)) { + // If source is a prvalue, use it directly. + if (Initializer->getValueKind() == VK_RValue) { + // FIXME: This produces a bogus extwarn + AddArrayInitStep(DestType); + return; + } + + // Emit element-at-a-time copy loop. + InitializedEntity Element = + InitializedEntity::InitializeElement(S.Context, 0, Entity); + QualType InitEltT = + Context.getAsArrayType(Initializer->getType())->getElementType(); + OpaqueValueExpr OVE(SourceLocation(), InitEltT, + Initializer->getValueKind()); + Expr *OVEAsExpr = &OVE; + InitializeFrom(S, Element, Kind, OVEAsExpr, TopLevelOfInitList, + TreatUnavailableAsInvalid); + if (!Failed()) + AddArrayInitLoopStep(Entity.getType(), InitEltT); + return; + } + // Note: as an GNU C extension, we allow initialization of an // array from a compound literal that creates an array of the same // type, so long as the initializer has no side effects. @@ -5225,7 +5310,7 @@ void InitializationSequence::InitializeFrom(Sema &S, (Context.hasSameUnqualifiedType(SourceType, DestType) || S.IsDerivedFrom(Initializer->getLocStart(), SourceType, DestType)))) TryConstructorInitialization(S, Entity, Kind, Args, - DestType, *this); + 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 @@ -5842,7 +5927,7 @@ PerformConstructorInitialization(Sema &S, // If the entity allows NRVO, mark the construction as elidable // unconditionally. if (Entity.allowsNRVO()) - CurInit = S.BuildCXXConstructExpr(Loc, Entity.getType(), + CurInit = S.BuildCXXConstructExpr(Loc, Step.Type, Step.Function.FoundDecl, Constructor, /*Elidable=*/true, ConstructorArgs, @@ -5853,7 +5938,7 @@ PerformConstructorInitialization(Sema &S, ConstructKind, ParenOrBraceRange); else - CurInit = S.BuildCXXConstructExpr(Loc, Entity.getType(), + CurInit = S.BuildCXXConstructExpr(Loc, Step.Type, Step.Function.FoundDecl, Constructor, ConstructorArgs, @@ -6403,6 +6488,7 @@ InitializationSequence::Perform(Sema &S, Entity.getType(); ExprResult CurInit((Expr *)nullptr); + SmallVector ArrayLoopCommonExprs; // For initialization steps that start with a single initializer, // grab the only argument out the Args and place it into the "current" @@ -6430,6 +6516,8 @@ InitializationSequence::Perform(Sema &S, case SK_CAssignment: case SK_StringInit: case SK_ObjCObjectConversion: + case SK_ArrayLoopIndex: + case SK_ArrayLoopInit: case SK_ArrayInit: case SK_ParenthesizedArrayInit: case SK_PassByIndirectCopyRestore: @@ -6813,13 +6901,15 @@ InitializationSequence::Perform(Sema &S, bool UseTemporary = Entity.getType()->isReferenceType(); bool IsStdInitListInit = Step->Kind == SK_StdInitializerListConstructorCall; + Expr *Source = CurInit.get(); CurInit = PerformConstructorInitialization( - S, UseTemporary ? TempEntity : Entity, Kind, Args, *Step, + S, UseTemporary ? TempEntity : Entity, Kind, + Source ? MultiExprArg(Source) : Args, *Step, ConstructorInitRequiresZeroInit, - /*IsListInitialization*/IsStdInitListInit, - /*IsStdInitListInitialization*/IsStdInitListInit, - /*LBraceLoc*/SourceLocation(), - /*RBraceLoc*/SourceLocation()); + /*IsListInitialization*/ IsStdInitListInit, + /*IsStdInitListInitialization*/ IsStdInitListInit, + /*LBraceLoc*/ SourceLocation(), + /*RBraceLoc*/ SourceLocation()); break; } @@ -6898,6 +6988,28 @@ InitializationSequence::Perform(Sema &S, CurInit.get()->getValueKind()); break; + case SK_ArrayLoopIndex: { + Expr *Cur = CurInit.get(); + Expr *BaseExpr = new (S.Context) + OpaqueValueExpr(Cur->getExprLoc(), Cur->getType(), + Cur->getValueKind(), Cur->getObjectKind(), Cur); + Expr *IndexExpr = + new (S.Context) ArrayInitIndexExpr(S.Context.getSizeType()); + CurInit = S.CreateBuiltinArraySubscriptExpr( + BaseExpr, Kind.getLocation(), IndexExpr, Kind.getLocation()); + ArrayLoopCommonExprs.push_back(BaseExpr); + break; + } + + case SK_ArrayLoopInit: { + assert(!ArrayLoopCommonExprs.empty() && + "mismatched SK_ArrayLoopIndex and SK_ArrayLoopInit"); + Expr *Common = ArrayLoopCommonExprs.pop_back_val(); + CurInit = new (S.Context) ArrayInitLoopExpr(Step->Type, Common, + CurInit.get()); + break; + } + case SK_ArrayInit: // Okay: we checked everything before creating this step. Note that // this is a GNU extension. @@ -7851,6 +7963,14 @@ void InitializationSequence::dump(raw_ostream &OS) const { OS << "Objective-C object conversion"; break; + case SK_ArrayLoopIndex: + OS << "indexing for array initialization loop"; + break; + + case SK_ArrayLoopInit: + OS << "array initialization loop"; + break; + case SK_ArrayInit: OS << "array initialization"; break; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 1a07e71..af60a56 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3221,6 +3221,9 @@ ExprResult TreeTransform::TransformInitializer(Expr *Init, if (ExprWithCleanups *ExprTemp = dyn_cast(Init)) Init = ExprTemp->getSubExpr(); + if (auto *AIL = dyn_cast(Init)) + Init = AIL->getCommonExpr(); + if (MaterializeTemporaryExpr *MTE = dyn_cast(Init)) Init = MTE->GetTemporaryExpr(); @@ -9047,6 +9050,20 @@ TreeTransform::TransformNoInitExpr( template ExprResult +TreeTransform::TransformArrayInitLoopExpr(ArrayInitLoopExpr *E) { + llvm_unreachable("Unexpected ArrayInitLoopExpr outside of initializer"); + return ExprError(); +} + +template +ExprResult +TreeTransform::TransformArrayInitIndexExpr(ArrayInitIndexExpr *E) { + llvm_unreachable("Unexpected ArrayInitIndexExpr outside of initializer"); + return ExprError(); +} + +template +ExprResult TreeTransform::TransformImplicitValueInitExpr( ImplicitValueInitExpr *E) { TemporaryBase Rebase(*this, E->getLocStart(), DeclarationName()); diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 032ceef..461bb9f8 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -845,6 +845,16 @@ void ASTStmtReader::VisitNoInitExpr(NoInitExpr *E) { VisitExpr(E); } +void ASTStmtReader::VisitArrayInitLoopExpr(ArrayInitLoopExpr *E) { + VisitExpr(E); + E->SubExprs[0] = Reader.ReadSubExpr(); + E->SubExprs[1] = Reader.ReadSubExpr(); +} + +void ASTStmtReader::VisitArrayInitIndexExpr(ArrayInitIndexExpr *E) { + VisitExpr(E); +} + void ASTStmtReader::VisitImplicitValueInitExpr(ImplicitValueInitExpr *E) { VisitExpr(E); } @@ -3231,6 +3241,14 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) NoInitExpr(Empty); break; + case EXPR_ARRAY_INIT_LOOP: + S = new (Context) ArrayInitLoopExpr(Empty); + break; + + case EXPR_ARRAY_INIT_INDEX: + S = new (Context) ArrayInitIndexExpr(Empty); + break; + case EXPR_VA_ARG: S = new (Context) VAArgExpr(Empty); break; diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 46acfad..fbe1d84 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -792,6 +792,18 @@ void ASTStmtWriter::VisitNoInitExpr(NoInitExpr *E) { Code = serialization::EXPR_NO_INIT; } +void ASTStmtWriter::VisitArrayInitLoopExpr(ArrayInitLoopExpr *E) { + VisitExpr(E); + Record.AddStmt(E->SubExprs[0]); + Record.AddStmt(E->SubExprs[1]); + Code = serialization::EXPR_ARRAY_INIT_LOOP; +} + +void ASTStmtWriter::VisitArrayInitIndexExpr(ArrayInitIndexExpr *E) { + VisitExpr(E); + Code = serialization::EXPR_ARRAY_INIT_INDEX; +} + void ASTStmtWriter::VisitImplicitValueInitExpr(ImplicitValueInitExpr *E) { VisitExpr(E); Code = serialization::EXPR_IMPLICIT_VALUE_INIT; diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 91946b8..93f6939c 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -905,6 +905,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // Cases not handled yet; but will handle some day. case Stmt::DesignatedInitExprClass: case Stmt::DesignatedInitUpdateExprClass: + case Stmt::ArrayInitLoopExprClass: + case Stmt::ArrayInitIndexExprClass: case Stmt::ExtVectorElementExprClass: case Stmt::ImaginaryLiteralClass: case Stmt::ObjCAtCatchStmtClass: diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index e68d793..7f0a297 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -243,6 +243,8 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::ChooseExprClass: case Stmt::DesignatedInitExprClass: case Stmt::DesignatedInitUpdateExprClass: + case Stmt::ArrayInitLoopExprClass: + case Stmt::ArrayInitIndexExprClass: case Stmt::ExprWithCleanupsClass: case Stmt::ExpressionTraitExprClass: case Stmt::ExtVectorElementExprClass: -- 2.7.4