[clang] 7e7f38f - DR1413 and part of P1815R2: Minor improvements to Clang's determination

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Dec 15 14:53:48 PST 2020


Author: Richard Smith
Date: 2020-12-15T14:53:26-08:00
New Revision: 7e7f38f853fbf96c6ab2a0e5f9d7747ef8a76ffe

URL: https://github.com/llvm/llvm-project/commit/7e7f38f853fbf96c6ab2a0e5f9d7747ef8a76ffe
DIFF: https://github.com/llvm/llvm-project/commit/7e7f38f853fbf96c6ab2a0e5f9d7747ef8a76ffe.diff

LOG: DR1413 and part of P1815R2: Minor improvements to Clang's determination
of type- and value-dependency.

A static data member initialized to a constant inside a class template
is no longer considered value-dependent, per DR1413. A const but not
constexpr variable of literal type (other than integer or enumeration)
is no longer considered value-dependent, per P1815R2.

Added: 
    

Modified: 
    clang/include/clang/AST/Decl.h
    clang/lib/AST/ComputeDependence.cpp
    clang/test/CXX/drs/dr14xx.cpp
    clang/test/CXX/drs/dr21xx.cpp
    clang/test/CXX/drs/dr2xx.cpp
    clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2-0x.cpp
    clang/test/SemaCXX/typedef-redecl.cpp
    clang/test/SemaCXX/vector.cpp
    clang/www/cxx_dr_status.html

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 7f6f143aa866..ab24c8779df2 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1262,6 +1262,9 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
   /// constant expression, according to the relevant language standard.
   /// This only checks properties of the declaration, and does not check
   /// whether the initializer is in fact a constant expression.
+  ///
+  /// This corresponds to C++20 [expr.const]p3's notion of a
+  /// "potentially-constant" variable.
   bool mightBeUsableInConstantExpressions(const ASTContext &C) const;
 
   /// Determine whether this variable's value can be used in a

diff  --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 79e3b3b099fc..4026fdc76fd6 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -52,11 +52,12 @@ ExprDependence clang::computeDependence(UnaryOperator *E,
   //
   // What this amounts to is: constant-evaluate the operand and check whether it
   // refers to a templated entity other than a variable with local storage.
-  if (Ctx.getLangOpts().CPlusPlus11 && E->getOpcode() == UO_AddrOf &&
+  if (Ctx.getLangOpts().CPlusPlus && E->getOpcode() == UO_AddrOf &&
       !(Dep & ExprDependence::Value)) {
     Expr::EvalResult Result;
     SmallVector<PartialDiagnosticAt, 8> Diag;
     Result.Diag = &Diag;
+    // FIXME: This doesn't enforce the C++98 constant expression rules.
     if (E->getSubExpr()->EvaluateAsConstantExpr(Result, Ctx) && Diag.empty() &&
         Result.Val.isLValue()) {
       auto *VD = Result.Val.getLValueBase().dyn_cast<const ValueDecl *>();
@@ -452,22 +453,21 @@ ExprDependence clang::computeDependence(DeclRefExpr *E, const ASTContext &Ctx) {
     Deps |= ExprDependence::UnexpandedPack;
   Deps |= toExprDependence(Type->getDependence()) & ExprDependence::Error;
 
-  // (TD) C++ [temp.dep.expr]p3:
+  // C++ [temp.dep.expr]p3:
   //   An id-expression is type-dependent if it contains:
-  //
-  // and
-  //
-  // (VD) C++ [temp.dep.constexpr]p2:
-  //  An identifier is value-dependent if it is:
 
-  //  (TD)  - an identifier that was declared with dependent type
-  //  (VD)  - a name declared with a dependent type,
+  //    - an identifier associated by name lookup with one or more declarations
+  //      declared with a dependent type
+  //
+  // [The "or more" case is not modeled as a DeclRefExpr. There are a bunch
+  // more bullets here that we handle by treating the declaration as having a
+  // dependent type if they involve a placeholder type that can't be deduced.]
   if (Type->isDependentType())
     return Deps | ExprDependence::TypeValueInstantiation;
   else if (Type->isInstantiationDependentType())
     Deps |= ExprDependence::Instantiation;
 
-  //  (TD)  - a conversion-function-id that specifies a dependent type
+  //    - a conversion-function-id that specifies a dependent type
   if (Decl->getDeclName().getNameKind() ==
       DeclarationName::CXXConversionFunctionName) {
     QualType T = Decl->getDeclName().getCXXNameType();
@@ -478,23 +478,28 @@ ExprDependence clang::computeDependence(DeclRefExpr *E, const ASTContext &Ctx) {
       Deps |= ExprDependence::Instantiation;
   }
 
-  //  (VD)  - the name of a non-type template parameter,
+  //   - a template-id that is dependent,
+  //   - a nested-name-specifier or a qualified-id that names a member of an
+  //     unknown specialization
+  //   [These are not modeled as DeclRefExprs.]
+
+  //   or if it names a dependent member of the current instantiation that is a
+  //   static data member of type "array of unknown bound of T" for some T
+  //   [handled below].
+
+  // C++ [temp.dep.constexpr]p2:
+  //  An id-expression is value-dependent if:
+
+  //    - it is type-dependent [handled above]
+
+  //    - it is the name of a non-type template parameter,
   if (isa<NonTypeTemplateParmDecl>(Decl))
     return Deps | ExprDependence::ValueInstantiation;
 
-  //  (VD) - a constant with integral or enumeration type and is
-  //         initialized with an expression that is value-dependent.
-  //  (VD) - a constant with literal type and is initialized with an
-  //         expression that is value-dependent [C++11].
-  //  (VD) - FIXME: Missing from the standard:
-  //       -  an entity with reference type and is initialized with an
-  //          expression that is value-dependent [C++11]
-  if (VarDecl *Var = dyn_cast<VarDecl>(Decl)) {
-    if ((Ctx.getLangOpts().CPlusPlus11
-             ? Var->getType()->isLiteralType(Ctx)
-             : Var->getType()->isIntegralOrEnumerationType()) &&
-        (Var->getType().isConstQualified() ||
-         Var->getType()->isReferenceType())) {
+  //   - it names a potentially-constant variable that is initialized with an
+  //     expression that is value-dependent
+  if (const auto *Var = dyn_cast<VarDecl>(Decl)) {
+    if (Var->mightBeUsableInConstantExpressions(Ctx)) {
       if (const Expr *Init = Var->getAnyInitializer()) {
         if (Init->isValueDependent())
           Deps |= ExprDependence::ValueInstantiation;
@@ -503,25 +508,35 @@ ExprDependence clang::computeDependence(DeclRefExpr *E, const ASTContext &Ctx) {
       }
     }
 
-    // (VD) - FIXME: Missing from the standard:
-    //      -  a member function or a static data member of the current
-    //         instantiation
+    // - it names a static data member that is a dependent member of the
+    //   current instantiation and is not initialized in a member-declarator,
     if (Var->isStaticDataMember() &&
-        Var->getDeclContext()->isDependentContext()) {
-      Deps |= ExprDependence::ValueInstantiation;
-      TypeSourceInfo *TInfo = Var->getFirstDecl()->getTypeSourceInfo();
-      if (TInfo->getType()->isIncompleteArrayType())
-        Deps |= ExprDependence::Type;
+        Var->getDeclContext()->isDependentContext() &&
+        !Var->getFirstDecl()->hasInit()) {
+      const VarDecl *First = Var->getFirstDecl();
+      TypeSourceInfo *TInfo = First->getTypeSourceInfo();
+      if (TInfo->getType()->isIncompleteArrayType()) {
+        Deps |= ExprDependence::TypeValueInstantiation;
+      } else if (!First->hasInit()) {
+        Deps |= ExprDependence::ValueInstantiation;
+      }
     }
 
     return Deps;
   }
 
-  // (VD) - FIXME: Missing from the standard:
-  //      -  a member function or a static data member of the current
-  //         instantiation
-  if (isa<CXXMethodDecl>(Decl) && Decl->getDeclContext()->isDependentContext())
-    Deps |= ExprDependence::ValueInstantiation;
+  //   - it names a static member function that is a dependent member of the
+  //     current instantiation
+  //
+  // FIXME: It's unclear that the restriction to static members here has any
+  // effect: any use of a non-static member function name requires either
+  // forming a pointer-to-member or providing an object parameter, either of
+  // which makes the overall expression value-dependent.
+  if (auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {
+    if (MD->isStatic() && Decl->getDeclContext()->isDependentContext())
+      Deps |= ExprDependence::ValueInstantiation;
+  }
+
   return Deps;
 }
 

diff  --git a/clang/test/CXX/drs/dr14xx.cpp b/clang/test/CXX/drs/dr14xx.cpp
index c68e6faa76c0..866f2fe0bf85 100644
--- a/clang/test/CXX/drs/dr14xx.cpp
+++ b/clang/test/CXX/drs/dr14xx.cpp
@@ -4,6 +4,29 @@
 // RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
 
+namespace dr1413 { // dr1413: 12
+  template<int> struct Check {
+    typedef int type;
+  };
+  template<typename T> struct A : T {
+    static const int a = 1;
+    static const int b;
+    static void c();
+    void d();
+
+    void f() {
+      Check<true ? 0 : A::unknown_spec>::type *var1; // expected-error {{undeclared identifier 'var1'}}
+      Check<true ? 0 : a>::type *var2; // ok, variable declaration  expected-note 0+{{here}}
+      Check<true ? 0 : b>::type *var3; // expected-error {{undeclared identifier 'var3'}}
+      Check<true ? 0 : (c, 0)>::type *var4; // expected-error {{undeclared identifier 'var4'}}
+      // value-dependent because of the implied type-dependent 'this->', not because of 'd'
+      Check<true ? 0 : (d(), 0)>::type *var5; // expected-error {{undeclared identifier 'var5'}}
+      // value-dependent because of the value-dependent '&' operator, not because of 'A::d'
+      Check<true ? 0 : (&A::d(), 0)>::type *var5; // expected-error {{undeclared identifier 'var5'}}
+    }
+  };
+}
+
 namespace dr1423 { // dr1423: 11
 #if __cplusplus >= 201103L
   bool b1 = nullptr; // expected-error {{cannot initialize}}

diff  --git a/clang/test/CXX/drs/dr21xx.cpp b/clang/test/CXX/drs/dr21xx.cpp
index 98593e543e72..62c56703195b 100644
--- a/clang/test/CXX/drs/dr21xx.cpp
+++ b/clang/test/CXX/drs/dr21xx.cpp
@@ -31,6 +31,23 @@ namespace dr2100 { // dr2100: 12
 #endif
   };
   int q = A<int>().f() + A<int>().g();
+
+  // Corresponding constructs where the address is not taken are not
+  // value-dependent.
+  template<int N, bool = true> struct Y {};
+  template<typename T> struct B {
+    static const int n = 1;
+    int f() {
+      return Y<n>::declared_later; // expected-error {{no member named 'declared_later'}}
+    }
+    int g() {
+      static const int n = 2;
+      return Y<n>::declared_later; // expected-error {{no member named 'declared_later'}}
+    }
+  };
+  template<int N> struct Y<N> {
+    static const int declared_later = 0;
+  };
 }
 
 namespace dr2103 { // dr2103: yes

diff  --git a/clang/test/CXX/drs/dr2xx.cpp b/clang/test/CXX/drs/dr2xx.cpp
index c02b7a8f3634..ab70b8742d00 100644
--- a/clang/test/CXX/drs/dr2xx.cpp
+++ b/clang/test/CXX/drs/dr2xx.cpp
@@ -292,9 +292,9 @@ namespace dr224 { // dr224: no
     template <int, typename T> struct X { typedef T type; };
     template <class T> class A {
       static const int i = 5;
-      X<i, int>::type w; // FIXME: expected-error {{missing 'typename'}}
-      X<A::i, char>::type x; // FIXME: expected-error {{missing 'typename'}}
-      X<A<T>::i, double>::type y; // FIXME: expected-error {{missing 'typename'}}
+      X<i, int>::type w;
+      X<A::i, char>::type x;
+      X<A<T>::i, double>::type y;
       X<A<T*>::i, long>::type z; // expected-error {{missing 'typename'}}
       int f();
     };

diff  --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2-0x.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2-0x.cpp
index 8f2a599ab28a..11dd5194192b 100644
--- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2-0x.cpp
+++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2-0x.cpp
@@ -1,7 +1,11 @@
 // RUN: %clang_cc1 -std=c++11 -verify %s
-// expected-no-diagnostics
 
-template<int n> struct S;
+template<int n> struct S; // expected-note 3{{here}}
+
+struct LiteralType {
+  constexpr LiteralType(int n) : n(n) {}
+  int n;
+};
 
 template<int n> struct T {
   T() {
@@ -11,18 +15,28 @@ template<int n> struct T {
     S<s> check1; // ok, s is value-dependent
     //  - the name of a non-type template parameter
     typename S<n>::T check2; // ok, n is value-dependent
-    //  - a constant with literal type and is initialized with an expression
-    //  that is value-dependent.
+    //  - a potentially-constant variable that is initialized with an
+    //    expression that is value-dependent.
     const int k = n;
     typename S<k>::T check3a; // ok, u is value-dependent
 
     constexpr const int *p = &k;
     typename S<*p>::T check3b; // ok, p is value-dependent
 
-    // (missing from the standard)
-    //  - a reference and is initialized with an expression that is
-    //  value-dependent.
     const int &i = k;
     typename S<i>::T check4; // ok, i is value-dependent
+
+    static const int ki = 42;
+    const int &i2 = ki;
+    typename S<i2>::T check5; // expected-error {{undefined template}}
+
+    constexpr LiteralType x = n;
+    typename S<true ? 1 : x.n>::T check6; // ok, x is value-dependent
+
+    const LiteralType y = n;
+    typename S<true ? 2 : y.n>::T check7; // expected-error {{undefined template}}
+
+    constexpr LiteralType z = 42;
+    typename S<true ? 3 : z.n>::T check8; // expected-error {{undefined template}}
   }
 };

diff  --git a/clang/test/SemaCXX/typedef-redecl.cpp b/clang/test/SemaCXX/typedef-redecl.cpp
index b53bcd2b4580..06883ffd4b5e 100644
--- a/clang/test/SemaCXX/typedef-redecl.cpp
+++ b/clang/test/SemaCXX/typedef-redecl.cpp
@@ -86,7 +86,7 @@ namespace PR11630 {
 
   void f() {
     S<int> a;
-    a.f(); // expected-note{{in instantiation of member function 'PR11630::S<int>::f' requested here}}
+    a.f();
     S2<1> b;
     b.f();
     S2<2> b2;

diff  --git a/clang/test/SemaCXX/vector.cpp b/clang/test/SemaCXX/vector.cpp
index 724ccece0c42..4b2ebc99a183 100644
--- a/clang/test/SemaCXX/vector.cpp
+++ b/clang/test/SemaCXX/vector.cpp
@@ -426,6 +426,13 @@ struct ConstantValueNoDiag {
   }
   static constexpr double k = 1;
 };
+template <typename T, int N>
+struct ConstantValueNoDiagDependentValue {
+  float4 f(float4 x) {
+    return k * x;
+  }
+  static constexpr double k = N;
+};
 
 // The following two both diagnose because they cause a truncation.  Test both
 // the dependent type and non-dependent type versions.
@@ -437,6 +444,14 @@ struct DiagTrunc {
   }
   static constexpr double k = 1340282346638528859811704183484516925443.000000;
 };
+template <typename T, int N>
+struct DiagTruncDependentValue {
+  float4 f(float4 x) {
+    // expected-error at +1{{as implicit conversion would cause truncation}}
+    return k * x;
+  }
+  static constexpr double k = N + 1340282346638528859811704183484516925443.000000;
+};
 template <typename T>
 struct DiagTruncDependentType {
   float4 f(float4 x) {
@@ -467,9 +482,11 @@ void use() {
   NormalMember<double>().f(theFloat4);
 #if __cplusplus >= 201103L
   ConstantValueNoDiag<double>().f(theFloat4);
-  // expected-note at +1{{in instantiation of member function}}
+  ConstantValueNoDiagDependentValue<double, 1>().f(theFloat4);
   DiagTrunc<double>().f(theFloat4);
   // expected-note at +1{{in instantiation of member function}}
+  DiagTruncDependentValue<double, 0>().f(theFloat4);
+  // expected-note at +1{{in instantiation of member function}}
   DiagTruncDependentType<double>().f(theFloat4);
   PR45298Consumer<double>().f(theFloat4);
 #endif // __cplusplus >= 201103L

diff  --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 9f065c3ba6f5..6e0206ab9e63 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -8293,7 +8293,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://wg21.link/cwg1413">1413</a></td>
     <td>CD3</td>
     <td>Missing cases of value-dependency</td>
-    <td class="none" align="center">Unknown</td>
+    <td class="unreleased" align="center">Clang 12</td>
   </tr>
   <tr class="open" id="1414">
     <td><a href="https://wg21.link/cwg1414">1414</a></td>


        


More information about the cfe-commits mailing list