Skip to content

Commit

Permalink
C++1y constexpr extensions, round 1: Allow most forms of declaration and
Browse files Browse the repository at this point in the history
statement in constexpr functions. Everything which doesn't require variable
mutation is also allowed as an extension in C++11. 'void' becomes a literal
type to support constexpr functions which return 'void'.

llvm-svn: 180022
  • Loading branch information
zygoloid committed Apr 22, 2013
1 parent 7dcc558 commit d9f663b
Show file tree
Hide file tree
Showing 17 changed files with 697 additions and 110 deletions.
4 changes: 2 additions & 2 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1443,8 +1443,8 @@ class Type : public ExtQualsTypeCommonBase {
}

/// isLiteralType - Return true if this is a literal type
/// (C++0x [basic.types]p10)
bool isLiteralType() const;
/// (C++11 [basic.types]p10)
bool isLiteralType(ASTContext &Ctx) const;

/// \brief Test if this type is a standard-layout type.
/// (C++0x [basic.type]p9)
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticASTKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ def note_constexpr_lshift_discards : Note<"signed left shift discards bits">;
def note_constexpr_invalid_function : Note<
"%select{non-constexpr|undefined}0 %select{function|constructor}1 %2 cannot "
"be used in a constant expression">;
def note_constexpr_no_return : Note<
"control reached end of constexpr function">;
def note_constexpr_virtual_call : Note<
"cannot evaluate virtual function call in a constant expression">;
def note_constexpr_virtual_base : Note<
Expand Down
46 changes: 40 additions & 6 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1593,20 +1593,54 @@ def err_constexpr_non_literal_param : Error<
"not a literal type">;
def err_constexpr_body_invalid_stmt : Error<
"statement not allowed in constexpr %select{function|constructor}0">;
def err_constexpr_type_definition : Error<
"types cannot be defined in a constexpr %select{function|constructor}0">;
def ext_constexpr_body_invalid_stmt : ExtWarn<
"use of this statement in a constexpr %select{function|constructor}0 "
"is a C++1y extension">, InGroup<CXX1y>;
def warn_cxx11_compat_constexpr_body_invalid_stmt : Warning<
"use of this statement in a constexpr %select{function|constructor}0 "
"is incompatible with C++ standards before C++1y">,
InGroup<CXXPre1yCompat>, DefaultIgnore;
def ext_constexpr_type_definition : ExtWarn<
"type definition in a constexpr %select{function|constructor}0 "
"is a C++1y extension">, InGroup<CXX1y>;
def warn_cxx11_compat_constexpr_type_definition : Warning<
"type definition in a constexpr %select{function|constructor}0 "
"is incompatible with C++ standards before C++1y">,
InGroup<CXXPre1yCompat>, DefaultIgnore;
def err_constexpr_vla : Error<
"variably-modified type %0 cannot be used in a constexpr "
"%select{function|constructor}1">;
def err_constexpr_var_declaration : Error<
"variables cannot be declared in a constexpr %select{function|constructor}0">;
def ext_constexpr_local_var : ExtWarn<
"variable declaration in a constexpr %select{function|constructor}0 "
"is a C++1y extension">, InGroup<CXX1y>;
def warn_cxx11_compat_constexpr_local_var : Warning<
"variable declaration in a constexpr %select{function|constructor}0 "
"is incompatible with C++ standards before C++1y">,
InGroup<CXXPre1yCompat>, DefaultIgnore;
def err_constexpr_local_var_static : Error<
"%select{static|thread_local}1 variable not permitted in a constexpr "
"%select{function|constructor}0">;
def err_constexpr_local_var_non_literal_type : Error<
"variable of non-literal type %1 cannot be defined in a constexpr "
"%select{function|constructor}0">;
def err_constexpr_local_var_no_init : Error<
"variables defined in a constexpr %select{function|constructor}0 must be "
"initialized">;
def ext_constexpr_function_never_constant_expr : ExtWarn<
"constexpr %select{function|constructor}0 never produces a "
"constant expression">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError;
def err_constexpr_body_no_return : Error<
"no return statement in constexpr function">;
def err_constexpr_body_multiple_return : Error<
"multiple return statements in constexpr function">;
def warn_cxx11_compat_constexpr_body_no_return : Warning<
"constexpr function with no return statements is incompatible with C++ "
"standards before C++1y">, InGroup<CXXPre1yCompat>, DefaultIgnore;
def ext_constexpr_body_multiple_return : ExtWarn<
"multiple return statements in constexpr function is a C++1y extension">,
InGroup<CXX1y>;
def warn_cxx11_compat_constexpr_body_multiple_return : Warning<
"multiple return statements in constexpr function "
"is incompatible with C++ standards before C++1y">,
InGroup<CXXPre1yCompat>, DefaultIgnore;
def note_constexpr_body_previous_return : Note<
"previous return statement is here">;
def err_constexpr_function_try_block : Error<
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
data().IsStandardLayout = false;

// Record if this base is the first non-literal field or base.
if (!hasNonLiteralTypeFieldsOrBases() && !BaseType->isLiteralType())
if (!hasNonLiteralTypeFieldsOrBases() && !BaseType->isLiteralType(C))
data().HasNonLiteralTypeFieldsOrBases = true;

// Now go through all virtual bases of this base and add them.
Expand Down Expand Up @@ -676,7 +676,7 @@ void CXXRecordDecl::addedMember(Decl *D) {
}

// Record if this field is the first non-literal or volatile field or base.
if (!T->isLiteralType() || T.isVolatileQualified())
if (!T->isLiteralType(Context) || T.isVolatileQualified())
data().HasNonLiteralTypeFieldsOrBases = true;

if (Field->hasInClassInitializer()) {
Expand Down Expand Up @@ -845,7 +845,7 @@ void CXXRecordDecl::addedMember(Decl *D) {
}
} else {
// Base element type of field is a non-class type.
if (!T->isLiteralType() ||
if (!T->isLiteralType(Context) ||
(!Field->hasInClassInitializer() && !isUnion()))
data().DefaultedDefaultConstructorIsConstexpr = false;

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ static void computeDeclRefDependence(ASTContext &Ctx, NamedDecl *D, QualType T,
// expression that is value-dependent [C++11]
if (VarDecl *Var = dyn_cast<VarDecl>(D)) {
if ((Ctx.getLangOpts().CPlusPlus11 ?
Var->getType()->isLiteralType() :
Var->getType()->isLiteralType(Ctx) :
Var->getType()->isIntegralOrEnumerationType()) &&
(Var->getType().isConstQualified() ||
Var->getType()->isReferenceType())) {
Expand Down
112 changes: 99 additions & 13 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ namespace {

// Note that we intentionally use std::map here so that references to
// values are stable.
typedef std::map<const Expr*, APValue> MapTy;
typedef std::map<const void*, APValue> MapTy;
typedef MapTy::const_iterator temp_iterator;
/// Temporaries - Temporary lvalues materialized within this stack frame.
MapTy Temporaries;
Expand Down Expand Up @@ -913,6 +913,14 @@ static bool EvaluateComplex(const Expr *E, ComplexValue &Res, EvalInfo &Info);
// Misc utilities
//===----------------------------------------------------------------------===//

/// Evaluate an expression to see if it had side-effects, and discard its
/// result.
static void EvaluateIgnoredValue(EvalInfo &Info, const Expr *E) {
APValue Scratch;
if (!Evaluate(Scratch, Info, E))
Info.EvalStatus.HasSideEffects = true;
}

/// Should this call expression be treated as a string literal?
static bool IsStringLiteralCall(const CallExpr *E) {
unsigned Builtin = E->isBuiltinCall();
Expand Down Expand Up @@ -1046,7 +1054,7 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
/// Check that this core constant expression is of literal type, and if not,
/// produce an appropriate diagnostic.
static bool CheckLiteralType(EvalInfo &Info, const Expr *E) {
if (!E->isRValue() || E->getType()->isLiteralType())
if (!E->isRValue() || E->getType()->isLiteralType(Info.Ctx))
return true;

// Prvalue constant expressions must be of literal types.
Expand Down Expand Up @@ -1461,6 +1469,15 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E,
return true;
}

// If this is a local variable, dig out its value.
if (VD->hasLocalStorage() && Frame && Frame->Index > 1) {
Result = Frame->Temporaries[VD];
// If we've carried on past an unevaluatable local variable initializer,
// we can't go any further. This can happen during potential constant
// expression checking.
return !Result.isUninit();
}

// Dig out the initializer, and use the declaration which it's attached to.
const Expr *Init = VD->getAnyInitializer(VD);
if (!Init || Init->isValueDependent()) {
Expand Down Expand Up @@ -1803,7 +1820,9 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
return false;
}

if (!isa<ParmVarDecl>(VD)) {
// Unless we're looking at a local variable or argument in a constexpr call,
// the variable we're reading must be const.
if (LVal.CallIndex <= 1 || !VD->hasLocalStorage()) {
if (VD->isConstexpr()) {
// OK, we can read this variable.
} else if (VT->isIntegralOrEnumerationType()) {
Expand Down Expand Up @@ -1911,7 +1930,7 @@ static bool EvaluateObjectArgument(EvalInfo &Info, const Expr *Object,
if (Object->isGLValue())
return EvaluateLValue(Object, This, Info);

if (Object->getType()->isLiteralType())
if (Object->getType()->isLiteralType(Info.Ctx))
return EvaluateTemporary(Object, This, Info);

return false;
Expand Down Expand Up @@ -2060,20 +2079,61 @@ enum EvalStmtResult {
};
}

static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
// We don't need to evaluate the initializer for a static local.
if (!VD->hasLocalStorage())
return true;

LValue Result;
Result.set(VD, Info.CurrentCall->Index);
APValue &Val = Info.CurrentCall->Temporaries[VD];

if (!EvaluateInPlace(Val, Info, Result, VD->getInit())) {
// Wipe out any partially-computed value, to allow tracking that this
// evaluation failed.
Val = APValue();
return false;
}
}

return true;
}

// Evaluate a statement.
static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info,
const Stmt *S) {
// FIXME: Mark all temporaries in the current frame as destroyed at
// the end of each full-expression.
switch (S->getStmtClass()) {
default:
if (const Expr *E = dyn_cast<Expr>(S)) {
EvaluateIgnoredValue(Info, E);
// Don't bother evaluating beyond an expression-statement which couldn't
// be evaluated.
if (Info.EvalStatus.HasSideEffects && !Info.keepEvaluatingAfterFailure())
return ESR_Failed;
return ESR_Succeeded;
}

Info.Diag(S->getLocStart());
return ESR_Failed;

case Stmt::NullStmtClass:
case Stmt::DeclStmtClass:
return ESR_Succeeded;

case Stmt::DeclStmtClass: {
const DeclStmt *DS = cast<DeclStmt>(S);
for (DeclStmt::const_decl_iterator DclIt = DS->decl_begin(),
DclEnd = DS->decl_end(); DclIt != DclEnd; ++DclIt)
if (!EvaluateDecl(Info, *DclIt) && !Info.keepEvaluatingAfterFailure())
return ESR_Failed;
return ESR_Succeeded;
}

case Stmt::ReturnStmtClass: {
const Expr *RetExpr = cast<ReturnStmt>(S)->getRetValue();
if (!Evaluate(Result, Info, RetExpr))
if (RetExpr && !Evaluate(Result, Info, RetExpr))
return ESR_Failed;
return ESR_Returned;
}
Expand All @@ -2088,6 +2148,28 @@ static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info,
}
return ESR_Succeeded;
}

case Stmt::IfStmtClass: {
const IfStmt *IS = cast<IfStmt>(S);

// Evaluate the condition, as either a var decl or as an expression.
bool Cond;
if (VarDecl *CondDecl = IS->getConditionVariable()) {
if (!EvaluateDecl(Info, CondDecl))
return ESR_Failed;
if (!HandleConversionToBool(Info.CurrentCall->Temporaries[CondDecl],
Cond))
return ESR_Failed;
} else if (!EvaluateAsBooleanCondition(IS->getCond(), Cond, Info))
return ESR_Failed;

if (const Stmt *SubStmt = Cond ? IS->getThen() : IS->getElse()) {
EvalStmtResult ESR = EvaluateStmt(Result, Info, SubStmt);
if (ESR != ESR_Succeeded)
return ESR;
}
return ESR_Succeeded;
}
}
}

Expand Down Expand Up @@ -2181,7 +2263,10 @@ static bool HandleFunctionCall(SourceLocation CallLoc,
return false;

CallStackFrame Frame(Info, CallLoc, Callee, This, ArgValues.data());
return EvaluateStmt(Result, Info, Body) == ESR_Returned;
EvalStmtResult ESR = EvaluateStmt(Result, Info, Body);
if (ESR == ESR_Succeeded)
Info.Diag(Callee->getLocEnd(), diag::note_constexpr_no_return);
return ESR == ESR_Returned;
}

/// Evaluate a constructor call.
Expand All @@ -2207,7 +2292,9 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This,
// If it's a delegating constructor, just delegate.
if (Definition->isDelegatingConstructor()) {
CXXConstructorDecl::init_const_iterator I = Definition->init_begin();
return EvaluateInPlace(Result, Info, This, (*I)->getInit());
if (!EvaluateInPlace(Result, Info, This, (*I)->getInit()))
return false;
return EvaluateStmt(Result, Info, Definition->getBody()) != ESR_Failed;
}

// For a trivial copy or move constructor, perform an APValue copy. This is
Expand Down Expand Up @@ -2307,7 +2394,8 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This,
}
}

return Success;
return Success &&
EvaluateStmt(Result, Info, Definition->getBody()) != ESR_Failed;
}

//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -2660,9 +2748,7 @@ class ExprEvaluatorBase

/// Visit a value which is evaluated, but whose value is ignored.
void VisitIgnoredValue(const Expr *E) {
APValue Scratch;
if (!Evaluate(Scratch, Info, E))
Info.EvalStatus.HasSideEffects = true;
EvaluateIgnoredValue(Info, E);
}
};

Expand Down Expand Up @@ -2868,7 +2954,7 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {

bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
if (!VD->getType()->isReferenceType()) {
if (isa<ParmVarDecl>(VD)) {
if (VD->hasLocalStorage() && Info.CurrentCall->Index > 1) {
Result.set(VD, Info.CurrentCall->Index);
return true;
}
Expand Down
16 changes: 10 additions & 6 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1142,16 +1142,20 @@ bool QualType::isTriviallyCopyableType(ASTContext &Context) const {



bool Type::isLiteralType() const {
bool Type::isLiteralType(ASTContext &Ctx) const {
if (isDependentType())
return false;

// C++0x [basic.types]p10:
// C++1y [basic.types]p10:
// A type is a literal type if it is:
// -- cv void; or
if (Ctx.getLangOpts().CPlusPlus1y && isVoidType())
return true;

// C++11 [basic.types]p10:
// A type is a literal type if it is:
// [...]
// -- an array of literal type.
// Extension: variable arrays cannot be literal types, since they're
// runtime-sized.
// -- an array of literal type other than an array of runtime bound; or
if (isVariableArrayType())
return false;
const Type *BaseTy = getBaseElementTypeUnsafe();
Expand All @@ -1162,7 +1166,7 @@ bool Type::isLiteralType() const {
if (BaseTy->isIncompleteType())
return false;

// C++0x [basic.types]p10:
// C++11 [basic.types]p10:
// A type is a literal type if it is:
// -- a scalar type; or
// As an extension, Clang treats vector types and complex types as
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7626,7 +7626,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
}

// Suggest adding 'constexpr' in C++11 for literal types.
} else if (getLangOpts().CPlusPlus11 && DclT->isLiteralType()) {
} else if (getLangOpts().CPlusPlus11 && DclT->isLiteralType(Context)) {
Diag(VDecl->getLocation(), diag::err_in_class_initializer_literal_type)
<< DclT << Init->getSourceRange()
<< FixItHint::CreateInsertion(VDecl->getLocStart(), "constexpr ");
Expand Down

0 comments on commit d9f663b

Please sign in to comment.