From: Richard Smith Date: Wed, 21 Dec 2011 02:55:12 +0000 (+0000) Subject: C++11 half of r147023: In C++11, additionally eagerly instantiate: X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1d238ea926bbdd04356ce475934fcd4cac654c4b;p=clang C++11 half of r147023: In C++11, additionally eagerly instantiate: - constexpr function template instantiations - variables of reference type - constexpr variables git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@147031 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 6b97a5775a..52e09e6643 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -1025,6 +1025,12 @@ public: /// \endcode bool extendsLifetimeOfTemporary() const; + /// \brief Determine whether this variable's value can be used in a + /// constant expression, according to the relevant language standard. + /// This only checks properties of the declaration, and does not check + /// whether the initializer is in fact a constant expression. + bool isUsableInConstantExpressions() const; + EvaluatedStmt *ensureEvaluatedStmt() const; /// \brief Attempt to evaluate the value of the initializer attached to this diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 9028a09bc3..84a2a59ee2 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1339,6 +1339,26 @@ void VarDecl::setInit(Expr *I) { Init = I; } +bool VarDecl::isUsableInConstantExpressions() const { + const LangOptions &Lang = getASTContext().getLangOptions(); + + // Only const variables can be used in constant expressions in C++. C++98 does + // not require the variable to be non-volatile, but we consider this to be a + // defect. + if (!Lang.CPlusPlus || + !getType().isConstQualified() || getType().isVolatileQualified()) + return false; + + // In C++, const, non-volatile variables of integral or enumeration types + // can be used in constant expressions. + if (getType()->isIntegralOrEnumerationType()) + return true; + + // Additionally, in C++11, non-volatile constexpr variables and references can + // be used in constant expressions. + return Lang.CPlusPlus0x && (isConstexpr() || getType()->isReferenceType()); +} + /// Convert the initializer for this declaration to the elaborated EvaluatedStmt /// form, which contains extra information on the evaluated value of the /// initializer. diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index ebcdcf58d0..9ae39f1af3 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -6544,8 +6544,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { for (unsigned I = 0, N = Notes.size(); I != N; ++I) Diag(Notes[I].first, Notes[I].second); } - } else if (getLangOptions().CPlusPlus && !Type.isVolatileQualified() && - Type.isConstQualified() && Type->isIntegralOrEnumerationType()) { + } else if (var->isUsableInConstantExpressions()) { // Check whether the initializer of a const variable of integral or // enumeration type is an ICE now, since we can't tell whether it was // initialized by a constant expression if we check later. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 3a61fe5066..9d2298a704 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -9491,6 +9491,11 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) { cast(Function->getDeclContext())->isLocalClass()) PendingLocalImplicitInstantiations.push_back(std::make_pair(Function, Loc)); + else if (Function->getTemplateInstantiationPattern()->isConstexpr()) + // Do not defer instantiations of constexpr functions, to avoid the + // expression evaluator needing to call back into Sema if it sees a + // call to such a function. + InstantiateFunctionDefinition(Loc, Function); else PendingInstantiations.push_back(std::make_pair(Function, Loc)); } @@ -9526,9 +9531,9 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) { // This is a modification of an existing AST node. Notify listeners. if (ASTMutationListener *L = getASTMutationListener()) L->StaticDataMemberInstantiated(Var); - QualType T = Var->getType(); - if (T.isConstQualified() && !T.isVolatileQualified() && - T->isIntegralOrEnumerationType()) + if (Var->isUsableInConstantExpressions()) + // Do not defer instantiations of variables which could be used in a + // constant expression. InstantiateStaticDataMemberDefinition(Loc, Var); else PendingInstantiations.push_back(std::make_pair(Var, Loc)); diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp index 0fb6f7def0..809a0cb176 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp @@ -68,7 +68,7 @@ struct ConstexprDtor { }; // template stuff -template constexpr T ft(T t) { return t; } // unexpected-note {{here}} +template constexpr T ft(T t) { return t; } template T gt(T t) { return t; } struct S { template constexpr T f(); @@ -89,8 +89,7 @@ template <> int S::g() const; // desired-error {{non-constexpr declaration of 'g template <> char S::g() { return 0; } // expected-error {{no function template matches}} template <> double S::g() const { return 0; } // ok -// FIXME: The initializer is a constant expression. -constexpr int i3 = ft(1); // unexpected-error {{must be initialized by a constant expression}} unexpected-note {{undefined function 'ft'}} +constexpr int i3 = ft(1); void test() { // ignore constexpr when instantiating with non-literal diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index 5798eb5948..b470c80619 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -8,16 +8,17 @@ static_assert(false, "test"); // expected-error {{test}} } -template constexpr T id(const T &t) { return t; } // expected-note {{here}} -// FIXME: support templates here. -//template constexpr T min(const T &a, const T &b) { -// return a < b ? a : b; -//} -//template constexpr T max(const T &a, const T &b) { -// return a < b ? b : a; -//} -constexpr int min(const int &a, const int &b) { return a < b ? a : b; } -constexpr int max(const int &a, const int &b) { return a < b ? b : a; } +typedef decltype(sizeof(char)) size_t; + +template constexpr T id(const T &t) { return t; } +template constexpr T min(const T &a, const T &b) { + return a < b ? a : b; +} +template constexpr T max(const T &a, const T &b) { + return a < b ? b : a; +} +template constexpr T *begin(T (&xs)[N]) { return xs; } +template constexpr T *end(T (&xs)[N]) { return xs + N; } struct MemberZero { constexpr int zero() { return 0; } @@ -84,8 +85,7 @@ namespace TemplateArgumentConversion { template struct IntParam {}; using IntParam0 = IntParam<0>; - // FIXME: This should be accepted once we implement the new ICE rules. - using IntParam0 = IntParam; // expected-error {{not an integral constant expression}} + using IntParam0 = IntParam; using IntParam0 = IntParam; // expected-error {{did you mean to call it with no arguments?}} expected-error {{not an integral constant expression}} } @@ -94,8 +94,7 @@ namespace CaseStatements { switch (n) { // FIXME: Produce the 'add ()' fixit for this. case MemberZero().zero: // desired-error {{did you mean to call it with no arguments?}} expected-error {{not an integer constant expression}} expected-note {{non-literal type ''}} - // FIXME: This should be accepted once we implement the new ICE rules. - case id(1): // expected-error {{not an integer constant expression}} expected-note {{undefined function}} + case id(1): return; } } @@ -352,14 +351,8 @@ constexpr int strcmp_ce(const char *p, const char *q) { namespace StringLiteral { -// FIXME: Refactor this once we support constexpr templates. -constexpr int MangleChars(const char *p) { - return *p + 3 * (*p ? MangleChars(p+1) : 0); -} -constexpr int MangleChars(const char16_t *p) { - return *p + 3 * (*p ? MangleChars(p+1) : 0); -} -constexpr int MangleChars(const char32_t *p) { +template +constexpr int MangleChars(const Char *p) { return *p + 3 * (*p ? MangleChars(p+1) : 0); } @@ -383,9 +376,6 @@ constexpr const char *max_element(const char *a, const char *b) { return (a+1 >= b) ? a : max_iter(a, max_element(a+1, b)); } -constexpr const char *begin(const char (&arr)[45]) { return arr; } -constexpr const char *end(const char (&arr)[45]) { return arr + 45; } - constexpr char str[] = "the quick brown fox jumped over the lazy dog"; constexpr const char *max = max_element(begin(str), end(str)); static_assert(*max == 'z', ""); @@ -400,12 +390,10 @@ static_assert(strcmp_ce("", " ") < 0, ""); namespace Array { -// FIXME: Use templates for these once we support constexpr templates. -constexpr int Sum(const int *begin, const int *end) { +template +constexpr auto Sum(Iter begin, Iter end) -> decltype(+*begin) { return begin == end ? 0 : *begin + Sum(begin+1, end); } -constexpr const int *begin(const int (&xs)[5]) { return xs; } -constexpr const int *end(const int (&xs)[5]) { return xs + 5; } constexpr int xs[] = { 1, 2, 3, 4, 5 }; constexpr int ys[] = { 5, 4, 3, 2, 1 };