From 1ef9dbfbac798d6e09edda41da9ce95537dfdd4e Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 10 Feb 2017 02:19:05 +0000 Subject: [PATCH] [c++1z] P0512R0: support for 'explicit' specifier on deduction-guides. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@294693 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 3 +++ lib/Sema/SemaDecl.cpp | 11 ++++---- lib/Sema/SemaDeclCXX.cpp | 10 ++++++++ lib/Sema/SemaInit.cpp | 11 ++++---- lib/Sema/SemaTemplateInstantiateDecl.cpp | 4 +++ .../over.match.class.deduct/p2.cpp | 25 +++++++++++++++++++ .../over.match.class.deduct/p3.cpp | 4 +-- ...xx1z-class-template-argument-deduction.cpp | 17 ++++++++----- 8 files changed, 67 insertions(+), 18 deletions(-) create mode 100644 test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index a27c055a48..e03bda9c36 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1991,6 +1991,9 @@ def err_deduction_guide_name_not_class_template : Error< "template template parameter|dependent template name}0 %1">; def err_deduction_guide_defines_function : Error< "deduction guide cannot have a function definition">; +def err_deduction_guide_explicit_mismatch : Error< + "deduction guide is %select{not |}0declared 'explicit' but " + "previous declaration was%select{ not|}0">; def err_deduction_guide_specialized : Error<"deduction guide cannot be " "%select{explicitly instantiated|explicitly specialized}0">; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 7749bef4e7..abb9f51062 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -7659,11 +7659,12 @@ static FunctionDecl* CreateNewFunctionDecl(Sema &SemaRef, Declarator &D, // We don't need to store much extra information for a deduction guide, so // just model it as a plain FunctionDecl. - // FIXME: Store IsExplicit! - return FunctionDecl::Create(SemaRef.Context, DC, - D.getLocStart(), - NameInfo, R, TInfo, SC, isInline, - true/*HasPrototype*/, isConstexpr); + auto *FD = FunctionDecl::Create(SemaRef.Context, DC, D.getLocStart(), + NameInfo, R, TInfo, SC, isInline, + true /*HasPrototype*/, isConstexpr); + if (isExplicit) + FD->setExplicitSpecified(); + return FD; } else if (DC->isRecord()) { // If the name of the function is the same as the name of the record, // then this must be an invalid constructor that has a return type. diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 9ba2091bb2..0acf91d3d0 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -647,6 +647,16 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old, Invalid = true; } + // FIXME: It's not clear what should happen if multiple declarations of a + // deduction guide have different explicitness. For now at least we simply + // reject any case where the explicitness changes. + if (New->isDeductionGuide() && + New->isExplicitSpecified() != Old->isExplicitSpecified()) { + Diag(New->getLocation(), diag::err_deduction_guide_explicit_mismatch) + << New->isExplicitSpecified(); + Diag(Old->getLocation(), diag::note_previous_declaration); + } + // C++11 [dcl.fct.default]p4: If a friend declaration specifies a default // argument expression, that declaration shall be a definition and shall be // the only declaration of the function or function template in the diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 23ea650dd3..32e7ae0d8d 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -8324,10 +8324,13 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // C++ [over.match.copy]p1: (non-list copy-initialization from class) // The converting constructors of T are candidate functions. if (Kind.isCopyInit() && !ListInit) { - // FIXME: if (FD->isExplicit()) continue; + // Only consider converting constructors. + if (FD->isExplicit()) + continue; // When looking for a converting constructor, deduction guides that - // could never be called with one argument are not interesting. + // could never be called with one argument are not interesting to + // check or note. if (FD->getMinRequiredArguments() > 1 || (FD->getNumParams() == 0 && !FD->isVariadic())) continue; @@ -8353,7 +8356,6 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // ever have a parameter of the right type. bool SuppressUserConversions = Kind.isCopyInit(); - // FIXME: These are definitely wrong in the non-deduction-guide case. if (TD) AddTemplateOverloadCandidate(TD, Pair, /*ExplicitArgs*/ nullptr, Inits, Candidates, SuppressUserConversions); @@ -8410,8 +8412,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // C++ [over.match.list]p1: // In copy-list-initialization, if an explicit constructor is chosen, the // initialization is ill-formed. - if (Kind.isCopyInit() && ListInit && - false /*FIXME: Best->Function->isExplicit()*/) { + if (Kind.isCopyInit() && ListInit && Best->Function->isExplicit()) { bool IsDeductionGuide = !Best->Function->isImplicit(); Diag(Kind.getLocation(), diag::err_deduced_class_template_explicit) << TemplateName << IsDeductionGuide; diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 1c6920852c..a69d54c5dc 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1610,6 +1610,10 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, if (D->isInlined()) Function->setImplicitlyInline(); + // A deduction-guide could be explicit. + if (D->isExplicitSpecified()) + Function->setExplicitSpecified(); + if (QualifierLoc) Function->setQualifierInfo(QualifierLoc); diff --git a/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp b/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp new file mode 100644 index 0000000000..fe1b6a63d7 --- /dev/null +++ b/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -verify -std=c++1z %s + +namespace Explicit { + // Each notional constructor is explicit if the function or function template + // was generated from a constructor or deduction-guide that was declared explicit. + template struct A { + A(T); + A(T*); + }; + template A(T) -> A; + template explicit A(T*) -> A; // expected-note {{explicit}} + + int *p; + A a(p); + A b = p; + A c{p}; + A d = {p}; // expected-error {{selected an explicit deduction guide}} + + using X = A; + using Y = A; + + using X = decltype(a); + using Y = decltype(b); + using X = decltype(c); +} diff --git a/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p3.cpp b/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p3.cpp index d3d98870ae..0fc51fb54e 100644 --- a/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p3.cpp +++ b/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p3.cpp @@ -15,9 +15,9 @@ namespace std_example { A a4 = {0, i}; // expected-error {{no viable constructor or deduction guide}} template A(const T &, const T &) -> A; - template explicit A(T &&, T &&) -> A; + template explicit A(T &&, T &&) -> A; // expected-note {{explicit deduction guide declared here}} - A a5 = {0, 1}; // FIXME: Should be invalid, explicit deduction guide selected in copy-list-init + A a5 = {0, 1}; // expected-error {{class template argument deduction for 'A' selected an explicit deduction guide}} A a6{0, 1}; A a7 = {0, i}; // expected-note {{in instantiation of}} A a8{0, i}; // expected-error {{no matching constructor}} diff --git a/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp b/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp index 55aa6c6b14..33f2c72045 100644 --- a/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp +++ b/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp @@ -35,9 +35,14 @@ static_assert(has_type>(v2)); vector v3(5, 5); static_assert(has_type>(v3)); +vector v4 = {it, end}; +static_assert(has_type>(v4)); + +vector v5{it, end}; +static_assert(has_type>(v5)); template struct tuple { tuple(T...); }; -template explicit tuple(T ...t) -> tuple; +template explicit tuple(T ...t) -> tuple; // expected-note {{declared}} // FIXME: Remove template tuple(tuple) -> tuple; @@ -46,14 +51,14 @@ tuple ta = tuple{1, 'a', "foo", n}; static_assert(has_type>(ta)); tuple tb{ta}; -static_assert(has_type>(ta)); +static_assert(has_type>(tb)); -// FIXME: This should be tuple>; +// FIXME: This should be tuple>; when the above guide is removed. tuple tc = {ta}; -static_assert(has_type>(ta)); +static_assert(has_type>(tc)); -tuple td = {1, 2, 3}; -static_assert(has_type>(ta)); +tuple td = {1, 2, 3}; // expected-error {{selected an explicit deduction guide}} +static_assert(has_type>(td)); // FIXME: This is a GCC extension for now; if CWG don't allow this, at least // add a warning for it. -- 2.40.0