summaryrefslogtreecommitdiffstats
path: root/test/SemaTemplate
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2017-01-07 00:48:55 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2017-01-07 00:48:55 +0000
commit8ea4b56436cd21f29fc7c5725903e8ab7c8c9e10 (patch)
tree50f971e8b638d69b41c8a3089ec9938cb72dc20b /test/SemaTemplate
parent337babf4c2e29435df2ef6784177cc19137c07ac (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.cpp84
-rw-r--r--test/SemaTemplate/default-arguments-cxx0x.cpp2
-rw-r--r--test/SemaTemplate/instantiate-init.cpp3
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 {