From: Richard Smith Date: Mon, 14 Jan 2013 05:37:29 +0000 (+0000) Subject: PR12008: defer adding the implicit 'const' to a constexpr member function until X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=21c8fa87a3517d835072193a59a955ec7f6bf408;p=clang PR12008: defer adding the implicit 'const' to a constexpr member function until we know whether it is static. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@172376 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 668b8f8103..7dc413e499 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -5598,11 +5598,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, } if (isConstexpr) { - // C++0x [dcl.constexpr]p2: constexpr functions and constexpr constructors + // C++11 [dcl.constexpr]p2: constexpr functions and constexpr constructors // are implicitly inline. NewFD->setImplicitlyInline(); - // C++0x [dcl.constexpr]p3: functions declared constexpr are required to + // C++11 [dcl.constexpr]p3: functions declared constexpr are required to // be either constructors or to return a literal type. Therefore, // destructors cannot be declared constexpr. if (isa(NewFD)) @@ -6162,6 +6162,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, filterNonConflictingPreviousDecls(Context, NewFD, Previous); bool Redeclaration = false; + NamedDecl *OldDecl = 0; // Merge or overload the declaration with an existing declaration of // the same name, if appropriate. @@ -6170,8 +6171,6 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, // a declaration that requires merging. If it's an overload, // there's no more work to do here; we'll just add the new // function to the scope. - - NamedDecl *OldDecl = 0; if (!AllowOverloadingOfFunction(Previous, Context)) { Redeclaration = true; OldDecl = Previous.getFoundDecl(); @@ -6208,43 +6207,68 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, Context)); } } + } - if (Redeclaration) { - // NewFD and OldDecl represent declarations that need to be - // merged. - if (MergeFunctionDecl(NewFD, OldDecl, S)) { - NewFD->setInvalidDecl(); - return Redeclaration; - } + // C++11 [dcl.constexpr]p8: + // A constexpr specifier for a non-static member function that is not + // a constructor declares that member function to be const. + // + // This needs to be delayed until we know whether this is an out-of-line + // definition of a static member function. + CXXMethodDecl *MD = dyn_cast(NewFD); + if (MD && MD->isConstexpr() && !MD->isStatic() && + !isa(MD) && + (MD->getTypeQualifiers() & Qualifiers::Const) == 0) { + CXXMethodDecl *OldMD = dyn_cast_or_null(OldDecl); + if (FunctionTemplateDecl *OldTD = + dyn_cast_or_null(OldDecl)) + OldMD = dyn_cast(OldTD->getTemplatedDecl()); + if (!OldMD || !OldMD->isStatic()) { + const FunctionProtoType *FPT = + MD->getType()->castAs(); + FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); + EPI.TypeQuals |= Qualifiers::Const; + MD->setType(Context.getFunctionType(FPT->getResultType(), + FPT->arg_type_begin(), + FPT->getNumArgs(), EPI)); + } + } - Previous.clear(); - Previous.addDecl(OldDecl); - - if (FunctionTemplateDecl *OldTemplateDecl - = dyn_cast(OldDecl)) { - NewFD->setPreviousDeclaration(OldTemplateDecl->getTemplatedDecl()); - FunctionTemplateDecl *NewTemplateDecl - = NewFD->getDescribedFunctionTemplate(); - assert(NewTemplateDecl && "Template/non-template mismatch"); - if (CXXMethodDecl *Method - = dyn_cast(NewTemplateDecl->getTemplatedDecl())) { - Method->setAccess(OldTemplateDecl->getAccess()); - NewTemplateDecl->setAccess(OldTemplateDecl->getAccess()); - } - - // If this is an explicit specialization of a member that is a function - // template, mark it as a member specialization. - if (IsExplicitSpecialization && - NewTemplateDecl->getInstantiatedFromMemberTemplate()) { - NewTemplateDecl->setMemberSpecialization(); - assert(OldTemplateDecl->isMemberSpecialization()); - } - - } else { - if (isa(NewFD)) // Set access for out-of-line definitions - NewFD->setAccess(OldDecl->getAccess()); - NewFD->setPreviousDeclaration(cast(OldDecl)); + if (Redeclaration) { + // NewFD and OldDecl represent declarations that need to be + // merged. + if (MergeFunctionDecl(NewFD, OldDecl, S)) { + NewFD->setInvalidDecl(); + return Redeclaration; + } + + Previous.clear(); + Previous.addDecl(OldDecl); + + if (FunctionTemplateDecl *OldTemplateDecl + = dyn_cast(OldDecl)) { + NewFD->setPreviousDeclaration(OldTemplateDecl->getTemplatedDecl()); + FunctionTemplateDecl *NewTemplateDecl + = NewFD->getDescribedFunctionTemplate(); + assert(NewTemplateDecl && "Template/non-template mismatch"); + if (CXXMethodDecl *Method + = dyn_cast(NewTemplateDecl->getTemplatedDecl())) { + Method->setAccess(OldTemplateDecl->getAccess()); + NewTemplateDecl->setAccess(OldTemplateDecl->getAccess()); + } + + // If this is an explicit specialization of a member that is a function + // template, mark it as a member specialization. + if (IsExplicitSpecialization && + NewTemplateDecl->getInstantiatedFromMemberTemplate()) { + NewTemplateDecl->setMemberSpecialization(); + assert(OldTemplateDecl->isMemberSpecialization()); } + + } else { + if (isa(NewFD)) // Set access for out-of-line definitions + NewFD->setAccess(OldDecl->getAccess()); + NewFD->setPreviousDeclaration(cast(OldDecl)); } } diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index cfabccf5fe..cc9f4df447 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -1012,28 +1012,37 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old, // 13.1p2). While not part of the definition of the signature, // this check is important to determine whether these functions // can be overloaded. - CXXMethodDecl* OldMethod = dyn_cast(Old); - CXXMethodDecl* NewMethod = dyn_cast(New); + CXXMethodDecl *OldMethod = dyn_cast(Old); + CXXMethodDecl *NewMethod = dyn_cast(New); if (OldMethod && NewMethod && - !OldMethod->isStatic() && !NewMethod->isStatic() && - (OldMethod->getTypeQualifiers() != NewMethod->getTypeQualifiers() || - OldMethod->getRefQualifier() != NewMethod->getRefQualifier())) { - if (!UseUsingDeclRules && - OldMethod->getRefQualifier() != NewMethod->getRefQualifier() && - (OldMethod->getRefQualifier() == RQ_None || - NewMethod->getRefQualifier() == RQ_None)) { - // C++0x [over.load]p2: - // - Member function declarations with the same name and the same - // parameter-type-list as well as member function template - // declarations with the same name, the same parameter-type-list, and - // the same template parameter lists cannot be overloaded if any of - // them, but not all, have a ref-qualifier (8.3.5). - Diag(NewMethod->getLocation(), diag::err_ref_qualifier_overload) - << NewMethod->getRefQualifier() << OldMethod->getRefQualifier(); - Diag(OldMethod->getLocation(), diag::note_previous_declaration); + !OldMethod->isStatic() && !NewMethod->isStatic()) { + if (OldMethod->getRefQualifier() != NewMethod->getRefQualifier()) { + if (!UseUsingDeclRules && + (OldMethod->getRefQualifier() == RQ_None || + NewMethod->getRefQualifier() == RQ_None)) { + // C++0x [over.load]p2: + // - Member function declarations with the same name and the same + // parameter-type-list as well as member function template + // declarations with the same name, the same parameter-type-list, and + // the same template parameter lists cannot be overloaded if any of + // them, but not all, have a ref-qualifier (8.3.5). + Diag(NewMethod->getLocation(), diag::err_ref_qualifier_overload) + << NewMethod->getRefQualifier() << OldMethod->getRefQualifier(); + Diag(OldMethod->getLocation(), diag::note_previous_declaration); + } + return true; } - return true; + // We may not have applied the implicit const for a constexpr member + // function yet (because we haven't yet resolved whether this is a static + // or non-static member function). Add it now, on the assumption that this + // is a redeclaration of OldMethod. + unsigned NewQuals = NewMethod->getTypeQualifiers(); + if ((OldMethod->isConstexpr() || NewMethod->isConstexpr()) && + !isa(NewMethod)) + NewQuals |= Qualifiers::Const; + if (OldMethod->getTypeQualifiers() != NewQuals) + return true; } // The signatures match; this is not an overload. diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index df1521f9c7..67c9ae5e1f 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -5918,6 +5918,25 @@ Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD, Ovl->getDeclContext()->getRedeclContext())) continue; + // When matching a constexpr member function template specialization + // against the primary template, we don't yet know whether the + // specialization has an implicit 'const' (because we don't know whether + // it will be a static member function until we know which template it + // specializes), so adjust it now assuming it specializes this template. + QualType FT = FD->getType(); + if (FD->isConstexpr()) { + CXXMethodDecl *OldMD = + dyn_cast(FunTmpl->getTemplatedDecl()); + if (OldMD && OldMD->isConst()) { + const FunctionProtoType *FPT = FT->castAs(); + FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); + EPI.TypeQuals |= Qualifiers::Const; + FT = Context.getFunctionType(FPT->getResultType(), + FPT->arg_type_begin(), + FPT->getNumArgs(), EPI); + } + } + // C++ [temp.expl.spec]p11: // A trailing template-argument can be left unspecified in the // template-id naming an explicit function template specialization @@ -5928,10 +5947,8 @@ Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD, TemplateDeductionInfo Info(FD->getLocation()); FunctionDecl *Specialization = 0; if (TemplateDeductionResult TDK - = DeduceTemplateArguments(FunTmpl, ExplicitTemplateArgs, - FD->getType(), - Specialization, - Info)) { + = DeduceTemplateArguments(FunTmpl, ExplicitTemplateArgs, FT, + Specialization, Info)) { // FIXME: Template argument deduction failed; record why it failed, so // that we can provide nifty diagnostics. (void)TDK; diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index f44603b1ef..930d98d44a 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -2690,30 +2690,6 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, FreeFunction = (DC && !DC->isRecord()); } - // C++0x [dcl.constexpr]p8: A constexpr specifier for a non-static member - // function that is not a constructor declares that function to be const. - // FIXME: This should be deferred until we know whether this is a static - // member function (for an out-of-class definition, we don't know - // this until we perform redeclaration lookup). - if (D.getDeclSpec().isConstexprSpecified() && !FreeFunction && - D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static && - D.getName().getKind() != UnqualifiedId::IK_ConstructorName && - D.getName().getKind() != UnqualifiedId::IK_ConstructorTemplateId && - !(FnTy->getTypeQuals() & DeclSpec::TQ_const)) { - // Rebuild function type adding a 'const' qualifier. - FunctionProtoType::ExtProtoInfo EPI = FnTy->getExtProtoInfo(); - EPI.TypeQuals |= DeclSpec::TQ_const; - T = Context.getFunctionType(FnTy->getResultType(), - FnTy->arg_type_begin(), - FnTy->getNumArgs(), EPI); - // Rebuild any parens around the identifier in the function type. - for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i) { - if (D.getTypeObject(i).Kind != DeclaratorChunk::Paren) - break; - T = S.BuildParenType(T); - } - } - // C++11 [dcl.fct]p6 (w/DR1417): // An attempt to specify a function type with a cv-qualifier-seq or a // ref-qualifier (including by typedef-name) is ill-formed unless it is: diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p8.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p8.cpp index c4935b34a0..559838b056 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p8.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p8.cpp @@ -1,19 +1,35 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +using size_t = decltype(sizeof(int)); + struct S { constexpr int f(); constexpr int g() const; static constexpr int Sf(); + /*static*/ constexpr void *operator new(size_t) noexcept; + template constexpr T tm(); + template static constexpr T ts(); }; void f(const S &s) { s.f(); s.g(); - int (*f)() = &S::Sf; + int (*Sf)() = &S::Sf; + int (S::*f)() const = &S::f; int (S::*g)() const = &S::g; + void *(*opNew)(size_t) = &S::operator new; + int (S::*tm)() const = &S::tm; + int (*ts)() = &S::ts; } +constexpr int S::f() const { return 0; } +constexpr int S::g() { return 1; } +constexpr int S::Sf() { return 2; } +constexpr void *S::operator new(size_t) noexcept { return 0; } +template constexpr T S::tm() { return T(); } +template constexpr T S::ts() { return T(); } + namespace std_example { class debug_flag { // expected-note {{not an aggregate and has no constexpr constructors}} diff --git a/test/CXX/temp/temp.spec/temp.explicit/p1-0x.cpp b/test/CXX/temp/temp.spec/temp.explicit/p1-0x.cpp index 80f0598cb1..e0c7b35a79 100644 --- a/test/CXX/temp/temp.spec/temp.explicit/p1-0x.cpp +++ b/test/CXX/temp/temp.spec/temp.explicit/p1-0x.cpp @@ -12,7 +12,7 @@ struct Y { constexpr int f() { return 0; } }; -template constexpr int Y::f(); // expected-error{{explicit instantiation cannot be 'constexpr'}} +template constexpr int Y::f() const; // expected-error{{explicit instantiation cannot be 'constexpr'}} template struct Z {