diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2017-01-07 00:48:55 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2017-01-07 00:48:55 +0000 |
commit | 8ea4b56436cd21f29fc7c5725903e8ab7c8c9e10 (patch) | |
tree | 50f971e8b638d69b41c8a3089ec9938cb72dc20b /test/SemaTemplate | |
parent | 337babf4c2e29435df2ef6784177cc19137c07ac (diff) |
PR23135: Don't instantiate constexpr functions referenced in unevaluated operands where possible.
This implements something like the current direction of DR1581: we use a narrow
syntactic check to determine the set of places where a constant expression
could be evaluated, and only instantiate a constexpr function or variable if
it's referenced in one of those contexts, or is odr-used.
It's not yet clear whether this is the right set of syntactic locations; we
currently consider all contexts within templates that would result in odr-uses
after instantiation, and contexts within list-initialization (narrowing
conversions take another victim...), as requiring instantiation. We could in
principle restrict the former cases more (only const integral / reference
variable initializers, and contexts in which a constant expression is required,
perhaps). However, this is sufficient to allow us to accept libstdc++ code,
which relies on GCC's behavior (which appears to be somewhat similar to this
approach).
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@291318 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test/SemaTemplate')
-rw-r--r-- | test/SemaTemplate/constexpr-instantiate.cpp | 84 | ||||
-rw-r--r-- | test/SemaTemplate/default-arguments-cxx0x.cpp | 2 | ||||
-rw-r--r-- | test/SemaTemplate/instantiate-init.cpp | 3 |
3 files changed, 67 insertions, 22 deletions
diff --git a/test/SemaTemplate/constexpr-instantiate.cpp b/test/SemaTemplate/constexpr-instantiate.cpp index e8e3e7dd5a..b8cfbe1e0e 100644 --- a/test/SemaTemplate/constexpr-instantiate.cpp +++ b/test/SemaTemplate/constexpr-instantiate.cpp @@ -77,20 +77,19 @@ namespace Reference { } namespace Unevaluated { - // We follow g++ in treating any reference to a constexpr function template - // specialization as requiring an instantiation, even if it occurs in an - // unevaluated context. + // We follow the current proposed resolution of core issue 1581: a constexpr + // function template specialization requires a definition if: + // * it is odr-used, or would be odr-used except that it appears within the + // definition of a template, or + // * it is used within a braced-init-list, where it may be necessary for + // detecting narrowing conversions. // - // We go slightly further than g++, and also trigger the implicit definition - // of a defaulted special member in the same circumstances. This seems scary, - // since a lot of classes have constexpr special members in C++11, but the - // only observable impact should be the implicit instantiation of constexpr - // special member templates (defaulted special members should only be - // generated if they are well-formed, and non-constexpr special members in a - // base or member cause the class's special member to not be constexpr). + // We apply this both for instantiating constexpr function template + // specializations and for implicitly defining defaulted constexpr special + // member functions. // - // FIXME: None of this is required by the C++ standard. The rules in this - // area are poorly specified, so this is subject to change. + // FIXME: None of this is required by the C++ standard yet. The rules in this + // area are subject to change. namespace NotConstexpr { template<typename T> struct S { S() : n(0) {} @@ -98,16 +97,35 @@ namespace Unevaluated { int n; }; struct U : S<int> {}; - decltype(U(U())) u; // ok, don't instantiate S<int>::S() because it wasn't declared constexpr + decltype(U(U())) u; } namespace Constexpr { template<typename T> struct S { constexpr S() : n(0) {} - constexpr S(const S&) : n(T::error) {} // expected-error {{has no members}} + constexpr S(const S&) : n(T::error) {} int n; }; - struct U : S<int> {}; // expected-note {{instantiation}} - decltype(U(U())) u; // expected-note {{here}} + struct U : S<int> {}; + decltype(U(U())) u; + } + namespace ConstexprList { + template<int N> struct S { + constexpr S() : n(0) { + static_assert(N >= 0, ""); + } + constexpr operator int() const { return 0; } + int n; + }; + struct U : S<0> {}; + // ok, trigger instantiation within a list + decltype(char{U()}) t0; + decltype(new char{S<1>()}) t1; // expected-warning {{side effects}} + decltype((char){S<2>()}) t2; + decltype(+(char[1]){{S<3>()}}) t3; + // do not trigger instantiation outside a list + decltype(char(S<-1>())) u1; + decltype(new char(S<-2>())) u2; // expected-warning {{side effects}} + decltype((char)(S<-3>())) u3; } namespace PR11851_Comment0 { @@ -190,6 +208,32 @@ namespace Unevaluated { constexpr duration max = duration(); } } + + // For variables, we instantiate when they are used in a context in which + // evaluation could be required (odr-used, used in a template whose + // instantiations would odr-use, or used in list initialization), if they + // can be used as a constant (const integral or constexpr). + namespace Variables { + template<int N> struct A { + static const int k; + static int n; + }; + template<const int *N> struct B {}; + template<int N> constexpr int A<N>::k = *(int[N]){N}; // expected-error 1+{{negative}} + template<int N> int A<N>::n = *(int[N]){0}; + + template <typename> void f() { + (void)A<-1>::n; // ok + (void)A<-1>::k; // expected-note {{instantiation of }} + B<&A<-2>::n> b1; // ok + B<&A<-2>::k> b2; // expected-note {{instantiation of }} + }; + + decltype(A<-3>::k) d1 = 0; // ok + decltype(char{A<-4>::k}) d2 = 0; // expected-note {{instantiation of }} expected-error {{narrow}} expected-note {{cast}} + decltype(char{A<1>::k}) d3 = 0; // ok + decltype(char{A<1 + (unsigned char)-1>::k}) d4 = 0; // expected-error {{narrow}} expected-note {{cast}} + } } namespace NoInstantiationWhenSelectingOverload { @@ -201,10 +245,10 @@ namespace NoInstantiationWhenSelectingOverload { int n; }; - int f(S); - int f(int); + constexpr int f(S) { return 0; } + constexpr int f(int) { return 0; } void g() { f(0); } - void h() { (void)sizeof(f(0)); } - void i() { (void)sizeof(f("oops")); } // expected-note {{instantiation of}} + void h() { (void)sizeof(char{f(0)}); } + void i() { (void)sizeof(char{f("oops")}); } // expected-note {{instantiation of}} } diff --git a/test/SemaTemplate/default-arguments-cxx0x.cpp b/test/SemaTemplate/default-arguments-cxx0x.cpp index c52899a8e6..d9fa2b4a82 100644 --- a/test/SemaTemplate/default-arguments-cxx0x.cpp +++ b/test/SemaTemplate/default-arguments-cxx0x.cpp @@ -50,6 +50,8 @@ namespace PR16975 { bar(T); }; + bar<> foo{0}; + struct baz : public bar<> { using bar::bar; }; diff --git a/test/SemaTemplate/instantiate-init.cpp b/test/SemaTemplate/instantiate-init.cpp index e9be60d16c..244e94f6d6 100644 --- a/test/SemaTemplate/instantiate-init.cpp +++ b/test/SemaTemplate/instantiate-init.cpp @@ -115,9 +115,8 @@ namespace PR13064 { struct A { explicit A(int); }; // expected-note{{here}} template<typename T> struct B { T a { 0 }; }; B<A> b; - // expected-note@+1 {{in instantiation of default member initializer}} template<typename T> struct C { T a = { 0 }; }; // expected-error{{explicit}} - C<A> c; // expected-note{{here}} + C<A> c; // expected-note {{in instantiation of default member initializer}} } namespace PR16903 { |