From b9fb121a62de69686dc193d32cba680a1a645277 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 6 May 2019 03:47:15 +0000 Subject: [PATCH] [c++20] Implement P1009R2: allow omitting the array bound in an array new expression. This was voted into C++20 as a defect report resolution, so we retroactively apply it to all prior language modes (though it can never actually be used before C++11 mode). llvm-svn: 360006 --- clang/include/clang/AST/ExprCXX.h | 20 +++--- clang/include/clang/AST/Stmt.h | 2 +- clang/include/clang/ASTMatchers/ASTMatchers.h | 4 +- clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 +- clang/include/clang/Sema/Sema.h | 2 +- clang/lib/AST/ASTImporter.cpp | 11 +++- clang/lib/AST/ExprCXX.cpp | 27 ++++---- clang/lib/AST/StmtPrinter.cpp | 5 +- clang/lib/Analysis/CFG.cpp | 4 +- clang/lib/CodeGen/CGExprCXX.cpp | 6 +- clang/lib/Parse/ParseExprCXX.cpp | 11 ++-- clang/lib/Sema/SemaExprCXX.cpp | 73 ++++++++++++++-------- clang/lib/Sema/TreeTransform.h | 21 ++++--- .../lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 4 +- clang/test/CodeGenCXX/new-array-init.cpp | 22 +++++++ clang/test/PCH/cxx-exprs.cpp | 6 ++ clang/test/SemaCXX/new-delete.cpp | 24 ++++++- clang/tools/libclang/CIndex.cpp | 2 +- clang/www/cxx_status.html | 2 +- 19 files changed, 170 insertions(+), 80 deletions(-) diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 2d5a5b4..05574c2 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -2058,7 +2058,7 @@ private: CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, FunctionDecl *OperatorDelete, bool ShouldPassAlignment, bool UsualArrayDeleteWantsSize, ArrayRef PlacementArgs, - SourceRange TypeIdParens, Expr *ArraySize, + SourceRange TypeIdParens, Optional ArraySize, InitializationStyle InitializationStyle, Expr *Initializer, QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range, SourceRange DirectInitRange); @@ -2073,7 +2073,7 @@ public: Create(const ASTContext &Ctx, bool IsGlobalNew, FunctionDecl *OperatorNew, FunctionDecl *OperatorDelete, bool ShouldPassAlignment, bool UsualArrayDeleteWantsSize, ArrayRef PlacementArgs, - SourceRange TypeIdParens, Expr *ArraySize, + SourceRange TypeIdParens, Optional ArraySize, InitializationStyle InitializationStyle, Expr *Initializer, QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range, SourceRange DirectInitRange); @@ -2116,15 +2116,15 @@ public: bool isArray() const { return CXXNewExprBits.IsArray; } - Expr *getArraySize() { - return isArray() - ? cast(getTrailingObjects()[arraySizeOffset()]) - : nullptr; + Optional getArraySize() { + if (!isArray()) + return None; + return cast_or_null(getTrailingObjects()[arraySizeOffset()]); } - const Expr *getArraySize() const { - return isArray() - ? cast(getTrailingObjects()[arraySizeOffset()]) - : nullptr; + Optional getArraySize() const { + if (!isArray()) + return None; + return cast_or_null(getTrailingObjects()[arraySizeOffset()]); } unsigned getNumPlacementArgs() const { diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 72222cb..e50d577 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -1008,7 +1008,7 @@ protected: CastIterator(StmtPtr *I) : Base(I) {} typename Base::value_type operator*() const { - return cast(*this->I); + return cast_or_null(*this->I); } }; diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index 57dff79..d973b48 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -6413,8 +6413,8 @@ AST_MATCHER(CXXNewExpr, isArray) { /// cxxNewExpr(hasArraySize(intgerLiteral(equals(10)))) /// matches the expression 'new MyClass[10]'. AST_MATCHER_P(CXXNewExpr, hasArraySize, internal::Matcher, InnerMatcher) { - return Node.isArray() && - InnerMatcher.matches(*Node.getArraySize(), Finder, Builder); + return Node.isArray() && *Node.getArraySize() && + InnerMatcher.matches(**Node.getArraySize(), Finder, Builder); } /// Matches a class declaration that is defined. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 84145c1..b94b5d3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6373,13 +6373,15 @@ def err_variably_modified_typeid : Error<"'typeid' of variably modified type %0" def err_static_illegal_in_new : Error< "the 'static' modifier for the array size is not legal in new expressions">; def err_array_new_needs_size : Error< - "array size must be specified in new expressions">; + "array size must be specified in new expression with no initializer">; def err_bad_new_type : Error< "cannot allocate %select{function|reference}1 type %0 with new">; def err_new_incomplete_type : Error< "allocation of incomplete type %0">; def err_new_array_nonconst : Error< "only the first dimension of an allocated array may have dynamic size">; +def err_new_array_size_unknown_from_init : Error< + "cannot determine allocated array size from initializer">; def err_new_array_init_args : Error< "array 'new' cannot have initialization arguments">; def ext_new_paren_array_nonconst : ExtWarn< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2a737fe..138f985 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5308,7 +5308,7 @@ public: SourceRange TypeIdParens, QualType AllocType, TypeSourceInfo *AllocTypeInfo, - Expr *ArraySize, + Optional ArraySize, SourceRange DirectInitRange, Expr *Initializer); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 004c466..1ab99be 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -175,6 +175,14 @@ namespace clang { return Importer.Import_New(From); } + // Import an Optional by importing the contained T, if any. + template + Expected> import(Optional From) { + if (!From) + return Optional(); + return import(*From); + } + template Expected> importSeq(const T &From) { @@ -6896,7 +6904,8 @@ ExpectedStmt ASTNodeImporter::VisitCXXNewExpr(CXXNewExpr *E) { FunctionDecl *ToOperatorNew, *ToOperatorDelete; SourceRange ToTypeIdParens, ToSourceRange, ToDirectInitRange; - Expr *ToArraySize, *ToInitializer; + Optional ToArraySize; + Expr *ToInitializer; QualType ToType; TypeSourceInfo *ToAllocatedTypeSourceInfo; std::tie( diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index b1ce167..bbf7e04 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -97,7 +97,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, FunctionDecl *OperatorDelete, bool ShouldPassAlignment, bool UsualArrayDeleteWantsSize, ArrayRef PlacementArgs, SourceRange TypeIdParens, - Expr *ArraySize, InitializationStyle InitializationStyle, + Optional ArraySize, + InitializationStyle InitializationStyle, Expr *Initializer, QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range, SourceRange DirectInitRange) @@ -112,7 +113,7 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, "Only NoInit can have no initializer!"); CXXNewExprBits.IsGlobalNew = IsGlobalNew; - CXXNewExprBits.IsArray = ArraySize != nullptr; + CXXNewExprBits.IsArray = ArraySize.hasValue(); CXXNewExprBits.ShouldPassAlignment = ShouldPassAlignment; CXXNewExprBits.UsualArrayDeleteWantsSize = UsualArrayDeleteWantsSize; CXXNewExprBits.StoredInitializationStyle = @@ -122,12 +123,14 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, CXXNewExprBits.NumPlacementArgs = PlacementArgs.size(); if (ArraySize) { - if (ArraySize->isInstantiationDependent()) - ExprBits.InstantiationDependent = true; - if (ArraySize->containsUnexpandedParameterPack()) - ExprBits.ContainsUnexpandedParameterPack = true; + if (Expr *SizeExpr = *ArraySize) { + if (SizeExpr->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; + if (SizeExpr->containsUnexpandedParameterPack()) + ExprBits.ContainsUnexpandedParameterPack = true; + } - getTrailingObjects()[arraySizeOffset()] = ArraySize; + getTrailingObjects()[arraySizeOffset()] = *ArraySize; } if (Initializer) { @@ -179,11 +182,11 @@ CXXNewExpr::Create(const ASTContext &Ctx, bool IsGlobalNew, FunctionDecl *OperatorNew, FunctionDecl *OperatorDelete, bool ShouldPassAlignment, bool UsualArrayDeleteWantsSize, ArrayRef PlacementArgs, SourceRange TypeIdParens, - Expr *ArraySize, InitializationStyle InitializationStyle, - Expr *Initializer, QualType Ty, - TypeSourceInfo *AllocatedTypeInfo, SourceRange Range, - SourceRange DirectInitRange) { - bool IsArray = ArraySize != nullptr; + Optional ArraySize, + InitializationStyle InitializationStyle, Expr *Initializer, + QualType Ty, TypeSourceInfo *AllocatedTypeInfo, + SourceRange Range, SourceRange DirectInitRange) { + bool IsArray = ArraySize.hasValue(); bool HasInit = Initializer != nullptr; unsigned NumPlacementArgs = PlacementArgs.size(); bool IsParenTypeId = TypeIdParens.isValid(); diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 38eb344..012bd4a 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1976,10 +1976,11 @@ void StmtPrinter::VisitCXXNewExpr(CXXNewExpr *E) { if (E->isParenTypeId()) OS << "("; std::string TypeS; - if (Expr *Size = E->getArraySize()) { + if (Optional Size = E->getArraySize()) { llvm::raw_string_ostream s(TypeS); s << '['; - Size->printPretty(s, Helper, Policy); + if (*Size) + (*Size)->printPretty(s, Helper, Policy); s << ']'; } E->getAllocatedType().print(OS, Policy, TypeS); diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index fdd845e..0928fa2 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -4339,8 +4339,8 @@ CFGBlock *CFGBuilder::VisitCXXNewExpr(CXXNewExpr *NE, if (BuildOpts.AddCXXNewAllocator) appendNewAllocator(Block, NE); - if (NE->isArray()) - Block = Visit(NE->getArraySize()); + if (NE->isArray() && *NE->getArraySize()) + Block = Visit(*NE->getArraySize()); for (CXXNewExpr::arg_iterator I = NE->placement_arg_begin(), E = NE->placement_arg_end(); I != E; ++I) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index b0078c2..25b0abb 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -681,9 +681,9 @@ static llvm::Value *EmitCXXNewAllocSize(CodeGenFunction &CGF, // We multiply the size of all dimensions for NumElements. // e.g for 'int[2][3]', ElemType is 'int' and NumElements is 6. numElements = - ConstantEmitter(CGF).tryEmitAbstract(e->getArraySize(), e->getType()); + ConstantEmitter(CGF).tryEmitAbstract(*e->getArraySize(), e->getType()); if (!numElements) - numElements = CGF.EmitScalarExpr(e->getArraySize()); + numElements = CGF.EmitScalarExpr(*e->getArraySize()); assert(isa(numElements->getType())); // The number of elements can be have an arbitrary integer type; @@ -693,7 +693,7 @@ static llvm::Value *EmitCXXNewAllocSize(CodeGenFunction &CGF, // important way: if the count is negative, it's an error even if // the cookie size would bring the total size >= 0. bool isSigned - = e->getArraySize()->getType()->isSignedIntegerOrEnumerationType(); + = (*e->getArraySize())->getType()->isSignedIntegerOrEnumerationType(); llvm::IntegerType *numElementsType = cast(numElements->getType()); unsigned numElementsWidth = numElementsType->getBitWidth(); diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 87a6b4d..fbafb43 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -2929,12 +2929,12 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) { /// passed to ParseDeclaratorInternal. /// /// direct-new-declarator: -/// '[' expression ']' +/// '[' expression[opt] ']' /// direct-new-declarator '[' constant-expression ']' /// void Parser::ParseDirectNewDeclarator(Declarator &D) { // Parse the array dimensions. - bool first = true; + bool First = true; while (Tok.is(tok::l_square)) { // An array-size expression can't start with a lambda. if (CheckProhibitedCXX11Attribute()) @@ -2943,14 +2943,15 @@ void Parser::ParseDirectNewDeclarator(Declarator &D) { BalancedDelimiterTracker T(*this, tok::l_square); T.consumeOpen(); - ExprResult Size(first ? ParseExpression() - : ParseConstantExpression()); + ExprResult Size = + First ? (Tok.is(tok::r_square) ? ExprResult() : ParseExpression()) + : ParseConstantExpression(); if (Size.isInvalid()) { // Recover SkipUntil(tok::r_square, StopAtSemi); return; } - first = false; + First = false; T.consumeClose(); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 36e82d3..881e0f6 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1663,7 +1663,7 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, SourceLocation PlacementLParen, MultiExprArg PlacementArgs, SourceLocation PlacementRParen, SourceRange TypeIdParens, Declarator &D, Expr *Initializer) { - Expr *ArraySize = nullptr; + Optional ArraySize; // If the specified type is an array, unwrap it and save the expression. if (D.getNumTypeObjects() > 0 && D.getTypeObject(0).Kind == DeclaratorChunk::Array) { @@ -1674,7 +1674,7 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, if (Chunk.Arr.hasStatic) return ExprError(Diag(Chunk.Loc, diag::err_static_illegal_in_new) << D.getSourceRange()); - if (!Chunk.Arr.NumElts) + if (!Chunk.Arr.NumElts && !Initializer) return ExprError(Diag(Chunk.Loc, diag::err_array_new_needs_size) << D.getSourceRange()); @@ -1787,7 +1787,7 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, SourceRange TypeIdParens, QualType AllocType, TypeSourceInfo *AllocTypeInfo, - Expr *ArraySize, + Optional ArraySize, SourceRange DirectInitRange, Expr *Initializer) { SourceRange TypeRange = AllocTypeInfo->getTypeLoc().getSourceRange(); @@ -1838,9 +1838,11 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, auto *Deduced = AllocType->getContainedDeducedType(); if (Deduced && isa(Deduced)) { if (ArraySize) - return ExprError(Diag(ArraySize->getExprLoc(), - diag::err_deduced_class_template_compound_type) - << /*array*/ 2 << ArraySize->getSourceRange()); + return ExprError( + Diag(ArraySize ? (*ArraySize)->getExprLoc() : TypeRange.getBegin(), + diag::err_deduced_class_template_compound_type) + << /*array*/ 2 + << (ArraySize ? (*ArraySize)->getSourceRange() : TypeRange)); InitializedEntity Entity = InitializedEntity::InitializeNew(StartLoc, AllocType); @@ -1906,8 +1908,9 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, QualType ResultType = Context.getPointerType(AllocType); - if (ArraySize && ArraySize->getType()->isNonOverloadPlaceholderType()) { - ExprResult result = CheckPlaceholderExpr(ArraySize); + if (ArraySize && *ArraySize && + (*ArraySize)->getType()->isNonOverloadPlaceholderType()) { + ExprResult result = CheckPlaceholderExpr(*ArraySize); if (result.isInvalid()) return ExprError(); ArraySize = result.get(); } @@ -1919,19 +1922,19 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, // C++1y [expr.new]p6: The expression [...] is implicitly converted to // std::size_t. llvm::Optional KnownArraySize; - if (ArraySize && !ArraySize->isTypeDependent()) { + if (ArraySize && *ArraySize && !(*ArraySize)->isTypeDependent()) { ExprResult ConvertedSize; if (getLangOpts().CPlusPlus14) { assert(Context.getTargetInfo().getIntWidth() && "Builtin type of size 0?"); - ConvertedSize = PerformImplicitConversion(ArraySize, Context.getSizeType(), + ConvertedSize = PerformImplicitConversion(*ArraySize, Context.getSizeType(), AA_Converting); if (!ConvertedSize.isInvalid() && - ArraySize->getType()->getAs()) + (*ArraySize)->getType()->getAs()) // Diagnose the compatibility of this conversion. Diag(StartLoc, diag::warn_cxx98_compat_array_size_conversion) - << ArraySize->getType() << 0 << "'size_t'"; + << (*ArraySize)->getType() << 0 << "'size_t'"; } else { class SizeConvertDiagnoser : public ICEConvertDiagnoser { protected: @@ -1985,16 +1988,16 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, : diag::ext_array_size_conversion) << T << ConvTy->isEnumeralType() << ConvTy; } - } SizeDiagnoser(ArraySize); + } SizeDiagnoser(*ArraySize); - ConvertedSize = PerformContextualImplicitConversion(StartLoc, ArraySize, + ConvertedSize = PerformContextualImplicitConversion(StartLoc, *ArraySize, SizeDiagnoser); } if (ConvertedSize.isInvalid()) return ExprError(); ArraySize = ConvertedSize.get(); - QualType SizeType = ArraySize->getType(); + QualType SizeType = (*ArraySize)->getType(); if (!SizeType->isIntegralOrUnscopedEnumerationType()) return ExprError(); @@ -2006,18 +2009,18 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, // Let's see if this is a constant < 0. If so, we reject it out of hand, // per CWG1464. Otherwise, if it's not a constant, we must have an // unparenthesized array type. - if (!ArraySize->isValueDependent()) { + if (!(*ArraySize)->isValueDependent()) { llvm::APSInt Value; // We've already performed any required implicit conversion to integer or // unscoped enumeration type. // FIXME: Per CWG1464, we are required to check the value prior to // converting to size_t. This will never find a negative array size in // C++14 onwards, because Value is always unsigned here! - if (ArraySize->isIntegerConstantExpr(Value, Context)) { + if ((*ArraySize)->isIntegerConstantExpr(Value, Context)) { if (Value.isSigned() && Value.isNegative()) { - return ExprError(Diag(ArraySize->getBeginLoc(), + return ExprError(Diag((*ArraySize)->getBeginLoc(), diag::err_typecheck_negative_array_size) - << ArraySize->getSourceRange()); + << (*ArraySize)->getSourceRange()); } if (!AllocType->isDependentType()) { @@ -2025,15 +2028,15 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, ConstantArrayType::getNumAddressingBits(Context, AllocType, Value); if (ActiveSizeBits > ConstantArrayType::getMaxSizeBits(Context)) return ExprError( - Diag(ArraySize->getBeginLoc(), diag::err_array_too_large) - << Value.toString(10) << ArraySize->getSourceRange()); + Diag((*ArraySize)->getBeginLoc(), diag::err_array_too_large) + << Value.toString(10) << (*ArraySize)->getSourceRange()); } KnownArraySize = Value.getZExtValue(); } else if (TypeIdParens.isValid()) { // Can't have dynamic array size when the type-id is in parentheses. - Diag(ArraySize->getBeginLoc(), diag::ext_new_paren_array_nonconst) - << ArraySize->getSourceRange() + Diag((*ArraySize)->getBeginLoc(), diag::ext_new_paren_array_nonconst) + << (*ArraySize)->getSourceRange() << FixItHint::CreateRemoval(TypeIdParens.getBegin()) << FixItHint::CreateRemoval(TypeIdParens.getEnd()); @@ -2056,10 +2059,10 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, AllocationFunctionScope Scope = UseGlobal ? AFS_Global : AFS_Both; if (!AllocType->isDependentType() && !Expr::hasAnyTypeDependentArguments(PlacementArgs) && - FindAllocationFunctions(StartLoc, - SourceRange(PlacementLParen, PlacementRParen), - Scope, Scope, AllocType, ArraySize, PassAlignment, - PlacementArgs, OperatorNew, OperatorDelete)) + FindAllocationFunctions( + StartLoc, SourceRange(PlacementLParen, PlacementRParen), Scope, Scope, + AllocType, ArraySize.hasValue(), PassAlignment, PlacementArgs, + OperatorNew, OperatorDelete)) return ExprError(); // If this is an array allocation, compute whether the usual array @@ -2152,6 +2155,22 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, FullInit = Binder->getSubExpr(); Initializer = FullInit.get(); + + // FIXME: If we have a KnownArraySize, check that the array bound of the + // initializer is no greater than that constant value. + + if (ArraySize && !*ArraySize) { + auto *CAT = Context.getAsConstantArrayType(Initializer->getType()); + if (CAT) { + // FIXME: Track that the array size was inferred rather than explicitly + // specified. + ArraySize = IntegerLiteral::Create( + Context, CAT->getSize(), Context.getSizeType(), TypeRange.getEnd()); + } else { + Diag(TypeRange.getEnd(), diag::err_new_array_size_unknown_from_init) + << Initializer->getSourceRange(); + } + } } // Mark the new and delete operators as referenced. diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index a8ea6b0..b1b2a91 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -2745,7 +2745,7 @@ public: SourceRange TypeIdParens, QualType AllocatedType, TypeSourceInfo *AllocatedTypeInfo, - Expr *ArraySize, + Optional ArraySize, SourceRange DirectInitRange, Expr *Initializer) { return getSema().BuildCXXNew(StartLoc, UseGlobal, @@ -10378,9 +10378,16 @@ TreeTransform::TransformCXXNewExpr(CXXNewExpr *E) { return ExprError(); // Transform the size of the array we're allocating (if any). - ExprResult ArraySize = getDerived().TransformExpr(E->getArraySize()); - if (ArraySize.isInvalid()) - return ExprError(); + Optional ArraySize; + if (Optional OldArraySize = E->getArraySize()) { + ExprResult NewArraySize; + if (*OldArraySize) { + NewArraySize = getDerived().TransformExpr(*OldArraySize); + if (NewArraySize.isInvalid()) + return ExprError(); + } + ArraySize = NewArraySize.get(); + } // Transform the placement arguments (if any). bool ArgumentChanged = false; @@ -10417,7 +10424,7 @@ TreeTransform::TransformCXXNewExpr(CXXNewExpr *E) { if (!getDerived().AlwaysRebuild() && AllocTypeInfo == E->getAllocatedTypeSourceInfo() && - ArraySize.get() == E->getArraySize() && + ArraySize == E->getArraySize() && NewInit.get() == OldInit && OperatorNew == E->getOperatorNew() && OperatorDelete == E->getOperatorDelete() && @@ -10444,7 +10451,7 @@ TreeTransform::TransformCXXNewExpr(CXXNewExpr *E) { } QualType AllocType = AllocTypeInfo->getType(); - if (!ArraySize.get()) { + if (!ArraySize) { // If no array size was specified, but the new expression was // instantiated with an array type (e.g., "new T" where T is // instantiated with "int[4]"), extract the outer bound from the @@ -10472,7 +10479,7 @@ TreeTransform::TransformCXXNewExpr(CXXNewExpr *E) { E->getBeginLoc(), E->isGlobalNew(), /*FIXME:*/ E->getBeginLoc(), PlacementArgs, /*FIXME:*/ E->getBeginLoc(), E->getTypeIdParens(), AllocType, - AllocTypeInfo, ArraySize.get(), E->getDirectInitRange(), NewInit.get()); + AllocTypeInfo, ArraySize, E->getDirectInitRange(), NewInit.get()); } template diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index f7ad8fac..50d2342 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -984,7 +984,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation( } else if (const CXXNewExpr *NE = dyn_cast(E)) { if (NE->isArray()) - Arg = NE->getArraySize(); + Arg = *NE->getArraySize(); else return State; } @@ -1116,7 +1116,7 @@ ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C, SVal ElementCount; const SubRegion *Region; if (NE->isArray()) { - const Expr *SizeExpr = NE->getArraySize(); + const Expr *SizeExpr = *NE->getArraySize(); ElementCount = C.getSVal(SizeExpr); // Store the extent size for the (symbolic)region // containing the elements. diff --git a/clang/test/CodeGenCXX/new-array-init.cpp b/clang/test/CodeGenCXX/new-array-init.cpp index 90cd600..b504d5e 100644 --- a/clang/test/CodeGenCXX/new-array-init.cpp +++ b/clang/test/CodeGenCXX/new-array-init.cpp @@ -123,3 +123,25 @@ void constexpr_test() { // SIO: call i8* @_Zna{{.}}(i32 4) new int[0+1]{0}; } + +// CHECK-LABEL: define void @_Z13unknown_boundv +void unknown_bound() { + struct Aggr { int x, y, z; }; + new Aggr[]{1, 2, 3, 4}; + // CHECK: call {{.*}}_Znaj(i32 24) + // CHECK: store i32 1 + // CHECK: store i32 2 + // CHECK: store i32 3 + // CHECK: store i32 4 + // CHECK: store i32 0 + // CHECK: store i32 0 + // CHECK-NOT: store + // CHECK: } +} + +// CHECK-LABEL: define void @_Z20unknown_bound_stringv +void unknown_bound_string() { + new char[]{"hello"}; + // CHECK: call {{.*}}_Znaj(i32 6) + // CHECK: memcpy{{.*}} i32 6, +} diff --git a/clang/test/PCH/cxx-exprs.cpp b/clang/test/PCH/cxx-exprs.cpp index b7707e0..e02bb0a 100644 --- a/clang/test/PCH/cxx-exprs.cpp +++ b/clang/test/PCH/cxx-exprs.cpp @@ -20,10 +20,16 @@ public: } }; +template int *arr_new(T ...v) { + return new int[]{v...}; +} + #else New *clone_new(New *n) { return n->clone(); } +int *use_arr_new = arr_new(1, 2, 3); + #endif diff --git a/clang/test/SemaCXX/new-delete.cpp b/clang/test/SemaCXX/new-delete.cpp index 870a592..4db1206 100644 --- a/clang/test/SemaCXX/new-delete.cpp +++ b/clang/test/SemaCXX/new-delete.cpp @@ -65,6 +65,12 @@ void good_news() typedef foo x[2]; typedef foo y[2][2]; x* f3 = new y; + +#if __cplusplus >= 201103L + (void)new int[]{}; + (void)new int[]{1, 2, 3}; + (void)new char[]{"hello"}; +#endif } struct abstract { @@ -126,9 +132,14 @@ void bad_news(int *ip) (void)::new ((S*)0) U; // expected-error {{no matching function for call to 'operator new'}} // This must fail, because any member version hides all global versions. (void)new U; // expected-error {{no matching function for call to 'operator new'}} - (void)new (int[]); // expected-error {{array size must be specified in new expressions}} + (void)new (int[]); // expected-error {{array size must be specified in new expression with no initializer}} (void)new int&; // expected-error {{cannot allocate reference type 'int &' with new}} - // Some lacking cases due to lack of sema support. + (void)new int[]; // expected-error {{array size must be specified in new expression with no initializer}} + (void)new int[](); // expected-error {{cannot determine allocated array size from initializer}} + // FIXME: This is a terrible diagnostic. +#if __cplusplus < 201103L + (void)new int[]{}; // expected-error {{array size must be specified in new expression with no initializer}} +#endif } void good_deletes() @@ -601,3 +612,12 @@ struct A { void g() { this->::delete; } // expected-error {{expected unqualified-id}} }; } + +#if __cplusplus >= 201103L +template int *dependent_array_size(T ...v) { + return new int[]{v...}; // expected-error {{cannot initialize}} +} +int *p0 = dependent_array_size(); +int *p3 = dependent_array_size(1, 2, 3); +int *fail = dependent_array_size("hello"); // expected-note {{instantiation of}} +#endif diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index a5a8998..d7eee5e 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -2479,7 +2479,7 @@ void EnqueueVisitor::VisitCXXNewExpr(const CXXNewExpr *E) { // Enqueue the initializer , if any. AddStmt(E->getInitializer()); // Enqueue the array size, if any. - AddStmt(E->getArraySize()); + AddStmt(E->getArraySize().getValueOr(nullptr)); // Enqueue the allocated type. AddTypeLoc(E->getAllocatedTypeSourceInfo()); // Enqueue the placement arguments. diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 2a39009..ad7f8ab 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -110,7 +110,7 @@ with libc++ or with gcc's libstdc++. P1009R2 (DR) - No + SVN Static assertions -- 2.7.4