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@+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@+1{{in instantiation of member function}} + ConstantValueNoDiagDependentValue<double, 1>().f(theFloat4); DiagTrunc<double>().f(theFloat4); // expected-note@+1{{in instantiation of member function}} + DiagTruncDependentValue<double, 0>().f(theFloat4); + // expected-note@+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> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits