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. llvm-svn: 167514
This commit is contained in:
parent
e030a63868
commit
e10d304d20
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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())});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue