73 changes: 46 additions & 27 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Expr *> ArraySize;
// If the specified type is an array, unwrap it and save the expression.
if (D.getNumTypeObjects() > 0 &&
D.getTypeObject(0).Kind == DeclaratorChunk::Array) {
Expand All @@ -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());

Expand Down Expand Up @@ -1787,7 +1787,7 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
SourceRange TypeIdParens,
QualType AllocType,
TypeSourceInfo *AllocTypeInfo,
Expr *ArraySize,
Optional<Expr *> ArraySize,
SourceRange DirectInitRange,
Expr *Initializer) {
SourceRange TypeRange = AllocTypeInfo->getTypeLoc().getSourceRange();
Expand Down Expand Up @@ -1838,9 +1838,11 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
auto *Deduced = AllocType->getContainedDeducedType();
if (Deduced && isa<DeducedTemplateSpecializationType>(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);
Expand Down Expand Up @@ -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();
}
Expand All @@ -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<uint64_t> 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<RecordType>())
(*ArraySize)->getType()->getAs<RecordType>())
// 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:
Expand Down Expand Up @@ -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();
Expand All @@ -2006,34 +2009,34 @@ 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()) {
unsigned ActiveSizeBits =
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());

Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down
21 changes: 14 additions & 7 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -2745,7 +2745,7 @@ class TreeTransform {
SourceRange TypeIdParens,
QualType AllocatedType,
TypeSourceInfo *AllocatedTypeInfo,
Expr *ArraySize,
Optional<Expr *> ArraySize,
SourceRange DirectInitRange,
Expr *Initializer) {
return getSema().BuildCXXNew(StartLoc, UseGlobal,
Expand Down Expand Up @@ -10378,9 +10378,16 @@ TreeTransform<Derived>::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<Expr *> ArraySize;
if (Optional<Expr *> 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;
Expand Down Expand Up @@ -10417,7 +10424,7 @@ TreeTransform<Derived>::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() &&
Expand All @@ -10444,7 +10451,7 @@ TreeTransform<Derived>::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
Expand Down Expand Up @@ -10472,7 +10479,7 @@ TreeTransform<Derived>::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<typename Derived>
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -984,7 +984,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation(
}
else if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
if (NE->isArray())
Arg = NE->getArraySize();
Arg = *NE->getArraySize();
else
return State;
}
Expand Down Expand Up @@ -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.
Expand Down
22 changes: 22 additions & 0 deletions clang/test/CodeGenCXX/new-array-init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
6 changes: 6 additions & 0 deletions clang/test/PCH/cxx-exprs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,16 @@ class New {
}
};

template<typename ...T> int *arr_new(T ...v) {
return new int[]{v...};
}

#else

New<int> *clone_new(New<int> *n) {
return n->clone();
}

int *use_arr_new = arr_new(1, 2, 3);

#endif
24 changes: 22 additions & 2 deletions clang/test/SemaCXX/new-delete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -601,3 +612,12 @@ struct A {
void g() { this->::delete; } // expected-error {{expected unqualified-id}}
};
}

#if __cplusplus >= 201103L
template<typename ...T> 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
2 changes: 1 addition & 1 deletion clang/tools/libclang/CIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion clang/www/cxx_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ <h2 id="cxx11">C++11 implementation status</h2>
</tr>
<tr> <!-- from Kona 2019-->
<td><a href="http://wg21.link/p1009r2">P1009R2</a> (<a href="#dr">DR</a>)</td>
<td class="none" align="center">No</td>
<td class="svn" align="center">SVN</td>
</tr>
<tr>
<td>Static assertions</td>
Expand Down