From: Richard Smith Date: Tue, 20 Dec 2011 02:08:33 +0000 (+0000) Subject: Unlike in C++03, a constant-expression is not an unevaluated operand in C++11. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f6702a3927147655206ae729a84339c4fda4c651;p=clang Unlike in C++03, a constant-expression is not an unevaluated operand in C++11. Split out a new ExpressionEvaluationContext flag for this case, and don't treat it as unevaluated in C++11. This fixes some crash-on-invalids where we would allow references to class members in potentially-evaluated constant expressions in static member functions, and also fixes half of PR10177. The fix to PR10177 exposed a case where template instantiation failed to provide a source location for a diagnostic, so TreeTransform has been tweaked to supply source locations when transforming a type. The source location is still not very good, but MarkDeclarationsReferencedInType would need to operate on a TypeLoc to improve it further. Also fix MarkDeclarationReferenced in C++98 mode to trigger instantiation for static data members of class templates which are used in constant expressions. This fixes a link-time problem, but we still incorrectly treat the member as non-constant. The rest of the fix for that issue is blocked on PCH support for early-instantiated static data members, which will be added in a subsequent patch. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@146955 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index bd542da6a6..fd87d6b2de 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -512,12 +512,17 @@ public: /// evaluated at run-time, if at all. enum ExpressionEvaluationContext { /// \brief The current expression and its subexpressions occur within an - /// unevaluated operand (C++0x [expr]p8), such as a constant expression - /// or the subexpression of \c sizeof, where the type or the value of the - /// expression may be significant but no code will be generated to evaluate - /// the value of the expression at run time. + /// unevaluated operand (C++11 [expr]p7), such as the subexpression of + /// \c sizeof, where the type of the expression may be significant but + /// no code will be generated to evaluate the value of the expression at + /// run time. Unevaluated, + /// \brief The current expression and its subexpressions occur within a + /// constant expression. Such a context is not potentially-evaluated in + /// C++98, but is potentially-evaluated in C++11. + ConstantEvaluated, + /// \brief The current expression is potentially evaluated at run time, /// which means that code may be generated to evaluate the value of the /// expression at run time. diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 6fc2dec62f..3619017a80 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -247,11 +247,12 @@ Parser::ParseAssignmentExprWithObjCMessageExprStart(SourceLocation LBracLoc, ExprResult Parser::ParseConstantExpression() { - // C++ [basic.def.odr]p2: + // C++03 [basic.def.odr]p2: // An expression is potentially evaluated unless it appears where an // integral constant expression is required (see 5.19) [...]. + // C++98 and C++11 have no such rule, but this is only a defect in C++98. EnterExpressionEvaluationContext Unevaluated(Actions, - Sema::Unevaluated); + Sema::ConstantEvaluated); ExprResult LHS(ParseCastExpression(false)); return ParseRHSOfBinaryExpression(LHS, prec::Conditional); @@ -1041,7 +1042,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, if (T.expectAndConsume(diag::err_expected_lparen_after, "noexcept")) return ExprError(); - // C++ [expr.unary.noexcept]p1: + // C++11 [expr.unary.noexcept]p1: // The noexcept operator determines whether the evaluation of its operand, // which is an unevaluated operand, can throw an exception. EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated); @@ -1439,8 +1440,8 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, // [...] The operand is either an expression, which is an unevaluated // operand (Clause 5) [...] // - // The GNU typeof and alignof extensions also behave as unevaluated - // operands. + // The GNU typeof and GNU/C++11 alignof extensions also behave as + // unevaluated operands. EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated); Operand = ParseCastExpression(true/*isUnaryExpression*/); @@ -1456,8 +1457,8 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, // [...] The operand is either an expression, which is an unevaluated // operand (Clause 5) [...] // - // The GNU typeof and alignof extensions also behave as unevaluated - // operands. + // The GNU typeof and GNU/C++11 alignof extensions also behave as + // unevaluated operands. EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated); Operand = ParseParenExpression(ExprType, true/*stopIfCastExpr*/, diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 2176afa92d..f8c02ba741 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -10515,7 +10515,8 @@ void Sema::MarkVTableUsed(SourceLocation Loc, CXXRecordDecl *Class, // not have a vtable. if (!Class->isDynamicClass() || Class->isDependentContext() || CurContext->isDependentContext() || - ExprEvalContexts.back().Context == Unevaluated) + ExprEvalContexts.back().Context == Unevaluated || + ExprEvalContexts.back().Context == ConstantEvaluated) return; // Try to insert this class into the map. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index bc1e0a27b4..62ee9f554d 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1199,7 +1199,8 @@ diagnoseUncapturableValueReference(Sema &S, SourceLocation loc, VarDecl *var, DeclContext *DC) { switch (S.ExprEvalContexts.back().Context) { case Sema::Unevaluated: - // The argument will never be evaluated, so don't complain. + case Sema::ConstantEvaluated: + // The argument will never be evaluated at runtime, so don't complain. return CR_NoCapture; case Sema::PotentiallyEvaluated: @@ -9330,7 +9331,7 @@ void Sema::PopExpressionEvaluationContext() { // temporaries that we may have created as part of the evaluation of // the expression in that context: they aren't relevant because they // will never be constructed. - if (Rec.Context == Unevaluated) { + if (Rec.Context == Unevaluated || Rec.Context == ConstantEvaluated) { ExprCleanupObjects.erase(ExprCleanupObjects.begin() + Rec.NumCleanupObjects, ExprCleanupObjects.end()); ExprNeedsCleanups = Rec.ParentNeedsCleanups; @@ -9392,6 +9393,16 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) { // We are in an expression that is not potentially evaluated; do nothing. return; + case ConstantEvaluated: + // We are in an expression that will be evaluated during translation; in + // C++11, we need to define any functions which are used in case they're + // constexpr, whereas in C++98, we only need to define static data members + // of class templates. + if (!getLangOptions().CPlusPlus || + (!getLangOptions().CPlusPlus0x && !isa(D))) + return; + break; + case PotentiallyEvaluated: // We are in a potentially-evaluated expression, so this declaration is // "used"; handle this below. @@ -9663,6 +9674,10 @@ bool Sema::DiagRuntimeBehavior(SourceLocation Loc, const Stmt *Statement, // The argument will never be evaluated, so don't complain. break; + case ConstantEvaluated: + // Relevant diagnostics should be produced by constant evaluation. + break; + case PotentiallyEvaluated: case PotentiallyEvaluatedIfUsed: if (Statement && getCurFunctionOrMethodDecl()) { diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp index 03b1731d24..65e444915c 100644 --- a/lib/Sema/SemaExprMember.cpp +++ b/lib/Sema/SemaExprMember.cpp @@ -140,7 +140,7 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, return IMA_Mixed_StaticContext; if (SemaRef.getLangOptions().CPlusPlus0x && hasField) { - // C++0x [expr.prim.general]p10: + // C++11 [expr.prim.general]p12: // An id-expression that denotes a non-static data member or non-static // member function of a class can only be used: // (...) diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 123548e656..b2208b63c8 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -66,17 +66,16 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, // FIXME: This should be generalized to more than just the AlignedAttr. if (const AlignedAttr *Aligned = dyn_cast(TmplAttr)) { if (Aligned->isAlignmentDependent()) { - // The alignment expression is not potentially evaluated. - EnterExpressionEvaluationContext Unevaluated(*this, - Sema::Unevaluated); - if (Aligned->isAlignmentExpr()) { + // The alignment expression is a constant expression. + EnterExpressionEvaluationContext Unevaluated(*this, + Sema::ConstantEvaluated); + ExprResult Result = SubstExpr(Aligned->getAlignmentExpr(), TemplateArgs); if (!Result.isInvalid()) AddAlignedAttr(Aligned->getLocation(), New, Result.takeAs()); - } - else { + } else { TypeSourceInfo *Result = SubstType(Aligned->getAlignmentType(), TemplateArgs, Aligned->getLocation(), @@ -380,7 +379,7 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) { // We already have an initializer in the class. } else if (D->getInit()) { if (Var->isStaticDataMember() && !D->isOutOfLine()) - SemaRef.PushExpressionEvaluationContext(Sema::Unevaluated); + SemaRef.PushExpressionEvaluationContext(Sema::ConstantEvaluated); else SemaRef.PushExpressionEvaluationContext(Sema::PotentiallyEvaluated); @@ -462,8 +461,9 @@ Decl *TemplateDeclInstantiator::VisitFieldDecl(FieldDecl *D) { if (Invalid) BitWidth = 0; else if (BitWidth) { - // The bit-width expression is not potentially evaluated. - EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated); + // The bit-width expression is a constant expression. + EnterExpressionEvaluationContext Unevaluated(SemaRef, + Sema::ConstantEvaluated); ExprResult InstantiatedBitWidth = SemaRef.SubstExpr(BitWidth, TemplateArgs); @@ -591,8 +591,9 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) { Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) { Expr *AssertExpr = D->getAssertExpr(); - // The expression in a static assertion is not potentially evaluated. - EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated); + // The expression in a static assertion is a constant expression. + EnterExpressionEvaluationContext Unevaluated(SemaRef, + Sema::ConstantEvaluated); ExprResult InstantiatedAssertExpr = SemaRef.SubstExpr(AssertExpr, TemplateArgs); @@ -653,9 +654,9 @@ Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { // The specified value for the enumerator. ExprResult Value = SemaRef.Owned((Expr *)0); if (Expr *UninstValue = EC->getInitExpr()) { - // The enumerator's value expression is not potentially evaluated. + // The enumerator's value expression is a constant expression. EnterExpressionEvaluationContext Unevaluated(SemaRef, - Sema::Unevaluated); + Sema::ConstantEvaluated); Value = SemaRef.SubstExpr(UninstValue, TemplateArgs); } @@ -2321,11 +2322,12 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, } Expr *NoexceptExpr = 0; if (Expr *OldNoexceptExpr = Proto->getNoexceptExpr()) { - EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated); + EnterExpressionEvaluationContext Unevaluated(SemaRef, + Sema::ConstantEvaluated); ExprResult E = SemaRef.SubstExpr(OldNoexceptExpr, TemplateArgs); if (E.isUsable()) E = SemaRef.CheckBooleanCondition(E.get(), E.get()->getLocStart()); - + if (E.isUsable()) { SourceLocation ErrLoc; llvm::APSInt NoexceptVal; diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 8e8f9d5d45..53a541ab46 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -1055,7 +1055,8 @@ static QualType inferARCLifetimeForPointee(Sema &S, QualType type, // If we are in an unevaluated context, like sizeof, assume ExplicitNone and // don't give error. - } else if (S.ExprEvalContexts.back().Context == Sema::Unevaluated) { + } else if (S.ExprEvalContexts.back().Context == Sema::Unevaluated || + S.ExprEvalContexts.back().Context == Sema::ConstantEvaluated) { implicitLifetime = Qualifiers::OCL_ExplicitNone; // If that failed, give an error and recover using __autoreleasing. diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index a30a0d036a..7995b0ba77 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -2898,7 +2898,7 @@ bool TreeTransform::TransformTemplateArgument( Expr *SourceExpr = Input.getSourceDeclExpression(); if (SourceExpr) { EnterExpressionEvaluationContext Unevaluated(getSema(), - Sema::Unevaluated); + Sema::ConstantEvaluated); ExprResult E = getDerived().TransformExpr(SourceExpr); SourceExpr = (E.isInvalid() ? 0 : E.take()); } @@ -2932,9 +2932,9 @@ bool TreeTransform::TransformTemplateArgument( llvm_unreachable("Caller should expand pack expansions"); case TemplateArgument::Expression: { - // Template argument expressions are not potentially evaluated. + // Template argument expressions are constant expressions. EnterExpressionEvaluationContext Unevaluated(getSema(), - Sema::Unevaluated); + Sema::ConstantEvaluated); Expr *InputExpr = Input.getSourceExpression(); if (!InputExpr) InputExpr = Input.getArgument().getAsExpr(); @@ -3182,6 +3182,9 @@ QualType TreeTransform::TransformType(QualType T) { template TypeSourceInfo *TreeTransform::TransformType(TypeSourceInfo *DI) { + // Refine the base location to the type's location. + TemporaryBase Rebase(*this, DI->getTypeLoc().getBeginLoc(), + getDerived().getBaseEntity()); if (getDerived().AlreadyTransformed(DI->getType())) return DI; @@ -3595,7 +3598,8 @@ TreeTransform::TransformConstantArrayType(TypeLocBuilder &TLB, Expr *Size = TL.getSizeExpr(); if (Size) { - EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated); + EnterExpressionEvaluationContext Unevaluated(SemaRef, + Sema::ConstantEvaluated); Size = getDerived().TransformExpr(Size).template takeAs(); } NewTL.setSizeExpr(Size); @@ -3640,9 +3644,6 @@ TreeTransform::TransformVariableArrayType(TypeLocBuilder &TLB, if (ElementType.isNull()) return QualType(); - // Array bounds are not potentially evaluated contexts - EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated); - ExprResult SizeResult = getDerived().TransformExpr(T->getSizeExpr()); if (SizeResult.isInvalid()) @@ -3680,8 +3681,9 @@ TreeTransform::TransformDependentSizedArrayType(TypeLocBuilder &TLB, if (ElementType.isNull()) return QualType(); - // Array bounds are not potentially evaluated contexts - EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated); + // Array bounds are constant expressions. + EnterExpressionEvaluationContext Unevaluated(SemaRef, + Sema::ConstantEvaluated); // Prefer the expression from the TypeLoc; the other may have been uniqued. Expr *origSize = TL.getSizeExpr(); @@ -3728,8 +3730,9 @@ QualType TreeTransform::TransformDependentSizedExtVectorType( if (ElementType.isNull()) return QualType(); - // Vector sizes are not potentially evaluated contexts - EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated); + // Vector sizes are constant expressions. + EnterExpressionEvaluationContext Unevaluated(SemaRef, + Sema::ConstantEvaluated); ExprResult Size = getDerived().TransformExpr(T->getSizeExpr()); if (Size.isInvalid()) @@ -6913,9 +6916,6 @@ TreeTransform::TransformCXXUuidofExpr(CXXUuidofExpr *E) { E->getLocEnd()); } - // We don't know whether the expression is potentially evaluated until - // after we perform semantic analysis, so the expression is potentially - // potentially evaluated. EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated); ExprResult SubExpr = getDerived().TransformExpr(E->getExprOperand()); diff --git a/test/CXX/expr/expr.prim/expr.prim.general/p12-0x.cpp b/test/CXX/expr/expr.prim/expr.prim.general/p12-0x.cpp index aec62ddd97..4a2dccff90 100644 --- a/test/CXX/expr/expr.prim/expr.prim.general/p12-0x.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.general/p12-0x.cpp @@ -10,3 +10,12 @@ struct S { int i = sizeof(S::m); // ok int j = sizeof(S::m + 42); // ok + + +struct T { + int n; + static void f() { + int a[n]; // expected-error {{invalid use of member 'n' in static member function}} + int b[sizeof n]; // ok + } +}; diff --git a/test/CodeGenCXX/vla.cpp b/test/CodeGenCXX/vla.cpp index 58cdf795ee..b523c769d5 100644 --- a/test/CodeGenCXX/vla.cpp +++ b/test/CodeGenCXX/vla.cpp @@ -1,5 +1,19 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - | FileCheck %s +template +struct S { + static int n; +}; +template int S::n = 5; + +int f() { + // Make sure that the reference here is enough to trigger the instantiation of + // the static data member. + // CHECK: @_ZN1SIiE1nE = weak_odr global i32 5 + int a[S::n]; + return sizeof a; +} + // rdar://problem/9506377 void test0(void *array, int n) { // CHECK: define void @_Z5test0Pvi( @@ -40,4 +54,3 @@ void test0(void *array, int n) { // CHECK-NEXT: ret void } - diff --git a/test/SemaCXX/PR10177.cpp b/test/SemaCXX/PR10177.cpp new file mode 100644 index 0000000000..8d745de0a6 --- /dev/null +++ b/test/SemaCXX/PR10177.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s + +template using alias_ref = T; +template void func_ref() {} +template struct class_ref {}; + +template +struct U { + static int a; +}; + +template struct S; // expected-note 2{{here}} + +template +int U::a = S::kError; // expected-error 2{{undefined}} + +template +void f() { + // FIXME: The standard suggests that U<0>::a is odr-used by this expression, + // but it's not entirely clear that's the right behaviour. + (void)alias_ref::a>(); + (void)func_ref::a>(); // expected-note {{here}} + (void)class_ref::a>(); // expected-note {{here}} +}; + +int main() { + f(); // expected-note 2{{here}} +} + +namespace N { + template struct S { static int n; }; + template int S::n = 5; + void g(int*); + template int f() { + int k[S::n]; + g(k); + return k[3]; + } + int j = f(); +}