From: Douglas Gregor Date: Sun, 1 Nov 2009 20:32:48 +0000 (+0000) Subject: When determining whether a reference to a static data member is an X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cf3293eaeb3853d12cff47e648bbe835004e929f;p=clang When determining whether a reference to a static data member is an integral constant expression, make sure to find where the initializer was provided---inside or outside the class definition---since that can affect whether we have an integral constant expression (and, we need to see the initializer itself). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@85741 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 50a875c3eb..265823e4eb 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -22,6 +22,7 @@ #include "clang/AST/StmtVisitor.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include using namespace clang; @@ -1538,16 +1539,35 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { // type initialized by an ICE can be used in ICEs. if (const VarDecl *Dcl = dyn_cast(cast(E)->getDecl())) { - if (Dcl->isInitKnownICE()) { - // We have already checked whether this subexpression is an - // integral constant expression. - if (Dcl->isInitICE()) - return NoDiag(); - else - return ICEDiag(2, E->getLocStart()); - } + Qualifiers Quals = Ctx.getCanonicalType(Dcl->getType()).getQualifiers(); + if (Quals.hasVolatile() || !Quals.hasConst()) + return ICEDiag(2, cast(E)->getLocation()); + + // Look for the definition of this variable, which will actually have + // an initializer. + const VarDecl *Def = 0; + const Expr *Init = Dcl->getDefinition(Def); + if (Init) { + if (Def->isInitKnownICE()) { + // We have already checked whether this subexpression is an + // integral constant expression. + if (Def->isInitICE()) + return NoDiag(); + else + return ICEDiag(2, cast(E)->getLocation()); + } - if (const Expr *Init = Dcl->getInit()) { + // C++ [class.static.data]p4: + // If a static data member is of const integral or const + // enumeration type, its declaration in the class definition can + // specify a constant-initializer which shall be an integral + // constant expression (5.19). In that case, the member can appear + // in integral constant expressions. + if (Def->isOutOfLine()) { + Dcl->setInitKnownICE(Ctx, false); + return ICEDiag(2, cast(E)->getLocation()); + } + ICEDiag Result = CheckICE(Init, Ctx); // Cache the result of the ICE test. Dcl->setInitKnownICE(Ctx, Result.Val == 0); @@ -1750,7 +1770,7 @@ bool Expr::isIntegerConstantExpr(llvm::APSInt &Result, ASTContext &Ctx, } EvalResult EvalResult; if (!Evaluate(EvalResult, Ctx)) - assert(0 && "ICE cannot be evaluated!"); + llvm::llvm_unreachable("ICE cannot be evaluated!"); assert(!EvalResult.HasSideEffects && "ICE with side effects!"); assert(EvalResult.Val.isInt() && "ICE that isn't integer!"); Result = EvalResult.Val.getInt(); diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 6ddeba99a7..804684cc2b 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -270,8 +270,9 @@ APValue LValueExprEvaluator::VisitDeclRefExpr(DeclRefExpr *E) { if (!VD->getType()->isReferenceType()) return APValue(E, 0); // FIXME: Check whether VD might be overridden! - if (VD->getInit()) - return Visit(VD->getInit()); + const VarDecl *Def = 0; + if (const Expr *Init = VD->getDefinition(Def)) + return Visit(const_cast(Init)); } return APValue(); @@ -855,11 +856,14 @@ bool IntExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { // In C++, const, non-volatile integers initialized with ICEs are ICEs. // In C, they can also be folded, although they are not ICEs. - if (E->getType().getCVRQualifiers() == Qualifiers::Const) { + if (Info.Ctx.getCanonicalType(E->getType()).getCVRQualifiers() + == Qualifiers::Const) { if (const VarDecl *D = dyn_cast(E->getDecl())) { - if (APValue *V = D->getEvaluatedValue()) - return Success(V->getInt(), E); - if (const Expr *Init = D->getInit()) { + const VarDecl *Def = 0; + if (const Expr *Init = D->getDefinition(Def)) { + if (APValue *V = D->getEvaluatedValue()) + return Success(V->getInt(), E); + if (Visit(const_cast(Init))) { // Cache the evaluated value in the variable declaration. D->setEvaluatedValue(Info.Ctx, Result); diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 27275230f7..7288ae29a0 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -165,7 +165,7 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) { // which they were instantiated. if (Var->isStaticDataMember()) SemaRef.Context.setInstantiatedFromStaticDataMember(Var, D, - TSK_ImplicitInstantiation); + TSK_ImplicitInstantiation); if (D->getInit()) { OwningExprResult Init diff --git a/test/SemaTemplate/instantiate-declref-ice.cpp b/test/SemaTemplate/instantiate-declref-ice.cpp index 21ee872027..ab12b90f6c 100644 --- a/test/SemaTemplate/instantiate-declref-ice.cpp +++ b/test/SemaTemplate/instantiate-declref-ice.cpp @@ -5,3 +5,33 @@ template struct x { x* y; }; +template +const int x::j; + +int array0[x<2>::j]; + + +template +struct X0 { + static const unsigned value = sizeof(T); +}; + +template +const unsigned X0::value; + +int array1[X0::value == sizeof(int)? 1 : -1]; + +const unsigned& testX0() { return X0::value; } + +int array2[X0::value == sizeof(int)? 1 : -1]; + +template +struct X1 { + static const unsigned value; +}; + +template +const unsigned X1::value = sizeof(T); + +int array3[X1::value == sizeof(int)? 1 : -1]; // expected-error{{variable length arrays are not permitted in C++}} \ +// expected-error{{variable length array declaration not allowed at file scope}}