summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2012-11-07 01:14:25 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2012-11-07 01:14:25 +0000
commitce2661f9ccb85de1aacaa7c3ea414a757f5986f6 (patch)
tree1db7a542b581bc600544d5df6d16bb1dc8fd8244
parent43f4f1e92bb07a04c30c9781be4d0bb5cc30acdd (diff)
downloadclang-ce2661f9ccb85de1aacaa7c3ea414a757f5986f6.tar.gz
clang-ce2661f9ccb85de1aacaa7c3ea414a757f5986f6.tar.bz2
clang-ce2661f9ccb85de1aacaa7c3ea414a757f5986f6.tar.xz
PR11851 (and duplicates): Whenever a constexpr function is referenced,
instantiate it if it can be instantiated and implicitly define it if it can be implicitly defined. This matches g++'s approach. Remove some cases from SemaOverload which were marking functions as referenced when just planning how overload resolution would proceed; such cases are not actually references. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@167514 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/Sema/SemaExpr.cpp47
-rw-r--r--lib/Sema/SemaOverload.cpp9
-rw-r--r--test/SemaCXX/constant-expression-cxx11.cpp5
-rw-r--r--test/SemaCXX/implicit-exception-spec.cpp11
-rw-r--r--test/SemaTemplate/constexpr-instantiate.cpp133
5 files changed, 178 insertions, 27 deletions
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 109386db6b..28a637180a 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -10340,15 +10340,44 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) {
Func->setReferenced();
- // Don't mark this function as used multiple times, unless it's a constexpr
- // function which we need to instantiate.
- if (Func->isUsed(false) &&
- !(Func->isConstexpr() && !Func->getBody() &&
- Func->isImplicitlyInstantiable()))
- return;
-
- if (!IsPotentiallyEvaluatedContext(*this))
- return;
+ // C++11 [basic.def.odr]p3:
+ // A function whose name appears as a potentially-evaluated expression is
+ // odr-used if it is the unique lookup result or the selected member of a
+ // set of overloaded functions [...].
+ //
+ // We (incorrectly) mark overload resolution as an unevaluated context, so we
+ // can just check that here. Skip the rest of this function if we've already
+ // marked the function as used.
+ if (Func->isUsed(false) || !IsPotentiallyEvaluatedContext(*this)) {
+ // C++11 [temp.inst]p3:
+ // Unless a function template specialization has been explicitly
+ // instantiated or explicitly specialized, the function template
+ // specialization is implicitly instantiated when the specialization is
+ // referenced in a context that requires a function definition to exist.
+ //
+ // We consider constexpr function templates to be referenced in a context
+ // that requires a definition to exist whenever they are referenced.
+ //
+ // FIXME: This instantiates constexpr functions too frequently. If this is
+ // really an unevaluated context (and we're not just in the definition of a
+ // function template or overload resolution or other cases which we
+ // incorrectly consider to be unevaluated contexts), and we're not in a
+ // subexpression which we actually need to evaluate (for instance, a
+ // template argument, array bound or an expression in a braced-init-list),
+ // we are not permitted to instantiate this constexpr function definition.
+ //
+ // FIXME: This also implicitly defines special members too frequently. They
+ // are only supposed to be implicitly defined if they are odr-used, but they
+ // are not odr-used from constant expressions in unevaluated contexts.
+ // However, they cannot be referenced if they are deleted, and they are
+ // deleted whenever the implicit definition of the special member would
+ // fail.
+ if (!Func->isConstexpr() || Func->getBody())
+ return;
+ CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Func);
+ if (!Func->isImplicitlyInstantiable() && (!MD || MD->isUserProvided()))
+ return;
+ }
// Note that this declaration has been used.
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(Func)) {
diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp
index a080031cdc..8e8580d701 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -2923,8 +2923,6 @@ IsInitializerListConstructorConversion(Sema &S, Expr *From, QualType ToType,
case OR_Success: {
// Record the standard conversion we used and the conversion function.
CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(Best->Function);
- S.MarkFunctionReferenced(From->getLocStart(), Constructor);
-
QualType ThisType = Constructor->getThisType(S.Context);
// Initializer lists don't have conversions as such.
User.Before.setAsIdentityConversion();
@@ -3105,8 +3103,6 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
// Record the standard conversion we used and the conversion function.
if (CXXConstructorDecl *Constructor
= dyn_cast<CXXConstructorDecl>(Best->Function)) {
- S.MarkFunctionReferenced(From->getLocStart(), Constructor);
-
// C++ [over.ics.user]p1:
// If the user-defined conversion is specified by a
// constructor (12.3.1), the initial standard conversion
@@ -3135,8 +3131,6 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
}
if (CXXConversionDecl *Conversion
= dyn_cast<CXXConversionDecl>(Best->Function)) {
- S.MarkFunctionReferenced(From->getLocStart(), Conversion);
-
// C++ [over.ics.user]p1:
//
// [...] If the user-defined conversion is specified by a
@@ -4049,8 +4043,6 @@ FindConversionForRefInit(Sema &S, ImplicitConversionSequence &ICS,
if (!Best->FinalConversion.DirectBinding)
return false;
- if (Best->Function)
- S.MarkFunctionReferenced(DeclLoc, Best->Function);
ICS.setUserDefined();
ICS.UserDefined.Before = Best->Conversions[0].Standard;
ICS.UserDefined.After = Best->FinalConversion;
@@ -9225,7 +9217,6 @@ Sema::ResolveAddressOfOverloadedFunction(Expr *AddressOfExpr,
Fn = Resolver.getMatchingFunctionDecl();
assert(Fn);
FoundResult = *Resolver.getMatchingFunctionAccessPair();
- MarkFunctionReferenced(AddressOfExpr->getLocStart(), Fn);
if (Complain)
CheckAddressOfMemberAccess(AddressOfExpr, FoundResult);
}
diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp
index 0dd7ffe5a9..f504eb621f 100644
--- a/test/SemaCXX/constant-expression-cxx11.cpp
+++ b/test/SemaCXX/constant-expression-cxx11.cpp
@@ -1451,6 +1451,7 @@ namespace PR14203 {
}
// FIXME: It's unclear whether this is valid. On the one hand, we're not
// allowed to generate a move constructor. On the other hand, if we did,
- // this would be a constant expression.
- int n = sizeof(short{duration(duration())}); // expected-error {{non-constant-expression cannot be narrowed}} expected-note {{override}}
+ // this would be a constant expression. For now, we generate a move
+ // constructor here.
+ int n = sizeof(short{duration(duration())});
}
diff --git a/test/SemaCXX/implicit-exception-spec.cpp b/test/SemaCXX/implicit-exception-spec.cpp
index b29cff5c5d..e26f985f0d 100644
--- a/test/SemaCXX/implicit-exception-spec.cpp
+++ b/test/SemaCXX/implicit-exception-spec.cpp
@@ -30,20 +30,17 @@ namespace InClassInitializers {
bool x = noexcept(TemplateArg());
// And within a nested class.
- // FIXME: The diagnostic location is terrible here.
- struct Nested {
+ struct Nested { // expected-error {{cannot be used by non-static data member initializer}}
struct Inner {
- int n = ExceptionIf<noexcept(Nested())>::f();
- } inner; // expected-error {{cannot be used by non-static data member initializer}}
+ int n = ExceptionIf<noexcept(Nested())>::f(); // expected-note {{implicit default constructor for 'InClassInitializers::Nested' first required here}}
+ } inner;
};
- bool y = noexcept(Nested());
- bool z = noexcept(Nested::Inner());
struct Nested2 {
struct Inner;
int n = Inner().n; // expected-error {{cannot be used by non-static data member initializer}}
struct Inner {
- int n = ExceptionIf<noexcept(Nested())>::f();
+ int n = ExceptionIf<noexcept(Nested2())>::f();
} inner;
};
}
diff --git a/test/SemaTemplate/constexpr-instantiate.cpp b/test/SemaTemplate/constexpr-instantiate.cpp
index 2f9fe0e485..80c4aaf856 100644
--- a/test/SemaTemplate/constexpr-instantiate.cpp
+++ b/test/SemaTemplate/constexpr-instantiate.cpp
@@ -75,3 +75,136 @@ namespace Reference {
constexpr int n = const_cast<int&>(S<int>::r);
static_assert(n == 5, "");
}
+
+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 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).
+ //
+ // FIXME: None of this is required by the C++ standard. The rules in this
+ // area are poorly specified, so this is subject to change.
+ namespace NotConstexpr {
+ template<typename T> struct S {
+ S() : n(0) {}
+ S(const S&) : n(T::error) {}
+ int n;
+ };
+ struct U : S<int> {};
+ decltype(U(U())) u; // ok, don't instantiate S<int>::S() because it wasn't declared constexpr
+ }
+ namespace Constexpr {
+ template<typename T> struct S {
+ constexpr S() : n(0) {}
+ constexpr S(const S&) : n(T::error) {} // expected-error {{has no members}}
+ int n;
+ };
+ struct U : S<int> {}; // expected-note {{instantiation}}
+ decltype(U(U())) u; // expected-note {{here}}
+ }
+
+ namespace PR11851_Comment0 {
+ template<int x> constexpr int f() { return x; }
+ template<int i> void ovf(int (&x)[f<i>()]);
+ void f() { int x[10]; ovf<10>(x); }
+ }
+
+ namespace PR11851_Comment1 {
+ template<typename T>
+ constexpr bool Integral() {
+ return true;
+ }
+ template<typename T, bool Int = Integral<T>()>
+ struct safe_make_unsigned {
+ typedef T type;
+ };
+ template<typename T>
+ using Make_unsigned = typename safe_make_unsigned<T>::type;
+ template <typename T>
+ struct get_distance_type {
+ using type = int;
+ };
+ template<typename R>
+ auto size(R) -> Make_unsigned<typename get_distance_type<R>::type>;
+ auto check() -> decltype(size(0));
+ }
+
+ namespace PR11851_Comment6 {
+ template<int> struct foo {};
+ template<class> constexpr int bar() { return 0; }
+ template<class T> foo<bar<T>()> foobar();
+ auto foobar_ = foobar<int>();
+ }
+
+ namespace PR11851_Comment9 {
+ struct S1 {
+ constexpr S1() {}
+ constexpr operator int() const { return 0; }
+ };
+ int k1 = sizeof(short{S1(S1())});
+
+ struct S2 {
+ constexpr S2() {}
+ constexpr operator int() const { return 123456; }
+ };
+ int k2 = sizeof(short{S2(S2())}); // expected-error {{cannot be narrowed}} expected-note {{override}}
+ }
+
+ namespace PR12288 {
+ template <typename> constexpr bool foo() { return true; }
+ template <bool> struct bar {};
+ template <typename T> bar<foo<T>()> baz() { return bar<foo<T>()>(); }
+ int main() { baz<int>(); }
+ }
+
+ namespace PR13423 {
+ template<bool, typename> struct enable_if {};
+ template<typename T> struct enable_if<true, T> { using type = T; };
+
+ template<typename T> struct F {
+ template<typename U>
+ static constexpr bool f() { return sizeof(T) < U::size; }
+
+ template<typename U>
+ static typename enable_if<f<U>(), void>::type g() {} // expected-note {{disabled by 'enable_if'}}
+ };
+
+ struct U { static constexpr int size = 2; };
+
+ void h() { F<char>::g<U>(); }
+ void i() { F<int>::g<U>(); } // expected-error {{no matching function}}
+ }
+
+ namespace PR14203 {
+ struct duration { constexpr duration() {} };
+
+ template <typename>
+ void sleep_for() {
+ constexpr duration max = duration();
+ }
+ }
+}
+
+namespace NoInstantiationWhenSelectingOverload {
+ // Check that we don't instantiate conversion functions when we're checking
+ // for the existence of an implicit conversion sequence, only when a function
+ // is actually chosen by overload resolution.
+ struct S {
+ template<typename T> constexpr S(T) : n(T::error) {} // expected-error {{no members}}
+ int n;
+ };
+
+ void f(S);
+ void f(int);
+
+ void g() { f(0); }
+ void h() { (void)sizeof(f(0)); }
+ void i() { (void)sizeof(f("oops")); } // expected-note {{instantiation of}}
+}