[cfe-commits] r167514 - in /cfe/trunk: lib/Sema/SemaExpr.cpp lib/Sema/SemaOverload.cpp test/SemaCXX/constant-expression-cxx11.cpp test/SemaCXX/implicit-exception-spec.cpp test/SemaTemplate/constexpr-instantiate.cpp

Richard Smith richard-llvm at metafoo.co.uk
Tue Nov 6 17:14:25 PST 2012


Author: rsmith
Date: Tue Nov  6 19:14:25 2012
New Revision: 167514

URL: http://llvm.org/viewvc/llvm-project?rev=167514&view=rev
Log:
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.

Modified:
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
    cfe/trunk/test/SemaCXX/implicit-exception-spec.cpp
    cfe/trunk/test/SemaTemplate/constexpr-instantiate.cpp

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=167514&r1=167513&r2=167514&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Tue Nov  6 19:14:25 2012
@@ -10340,15 +10340,44 @@
 
   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)) {

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=167514&r1=167513&r2=167514&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Tue Nov  6 19:14:25 2012
@@ -2923,8 +2923,6 @@
   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 @@
     // 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 @@
     }
     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 @@
     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 @@
     Fn = Resolver.getMatchingFunctionDecl();
     assert(Fn);
     FoundResult = *Resolver.getMatchingFunctionAccessPair();
-    MarkFunctionReferenced(AddressOfExpr->getLocStart(), Fn);
     if (Complain)
       CheckAddressOfMemberAccess(AddressOfExpr, FoundResult);
   }

Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp?rev=167514&r1=167513&r2=167514&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp Tue Nov  6 19:14:25 2012
@@ -1451,6 +1451,7 @@
   }
   // 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())});
 }

Modified: cfe/trunk/test/SemaCXX/implicit-exception-spec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/implicit-exception-spec.cpp?rev=167514&r1=167513&r2=167514&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/implicit-exception-spec.cpp (original)
+++ cfe/trunk/test/SemaCXX/implicit-exception-spec.cpp Tue Nov  6 19:14:25 2012
@@ -30,20 +30,17 @@
   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;
   };
 }

Modified: cfe/trunk/test/SemaTemplate/constexpr-instantiate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/constexpr-instantiate.cpp?rev=167514&r1=167513&r2=167514&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/constexpr-instantiate.cpp (original)
+++ cfe/trunk/test/SemaTemplate/constexpr-instantiate.cpp Tue Nov  6 19:14:25 2012
@@ -75,3 +75,136 @@
   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}}
+}





More information about the cfe-commits mailing list