PR12008: defer adding the implicit 'const' to a constexpr member function until

we know whether it is static.

llvm-svn: 172376
This commit is contained in:
Richard Smith 2013-01-14 05:37:29 +00:00
parent 01141a95a5
commit 574f4f6a1d
6 changed files with 129 additions and 87 deletions

View File

@ -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<CXXDestructorDecl>(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<CXXMethodDecl>(NewFD);
if (MD && MD->isConstexpr() && !MD->isStatic() &&
!isa<CXXConstructorDecl>(MD) &&
(MD->getTypeQualifiers() & Qualifiers::Const) == 0) {
CXXMethodDecl *OldMD = dyn_cast_or_null<CXXMethodDecl>(OldDecl);
if (FunctionTemplateDecl *OldTD =
dyn_cast_or_null<FunctionTemplateDecl>(OldDecl))
OldMD = dyn_cast<CXXMethodDecl>(OldTD->getTemplatedDecl());
if (!OldMD || !OldMD->isStatic()) {
const FunctionProtoType *FPT =
MD->getType()->castAs<FunctionProtoType>();
FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
EPI.TypeQuals |= Qualifiers::Const;
MD->setType(Context.getFunctionType(FPT->getResultType(),
FPT->arg_type_begin(),
FPT->getNumArgs(), EPI));
}
}
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<FunctionTemplateDecl>(OldDecl)) {
NewFD->setPreviousDeclaration(OldTemplateDecl->getTemplatedDecl());
FunctionTemplateDecl *NewTemplateDecl
= NewFD->getDescribedFunctionTemplate();
assert(NewTemplateDecl && "Template/non-template mismatch");
if (CXXMethodDecl *Method
= dyn_cast<CXXMethodDecl>(NewTemplateDecl->getTemplatedDecl())) {
Method->setAccess(OldTemplateDecl->getAccess());
NewTemplateDecl->setAccess(OldTemplateDecl->getAccess());
}
Previous.clear();
Previous.addDecl(OldDecl);
if (FunctionTemplateDecl *OldTemplateDecl
= dyn_cast<FunctionTemplateDecl>(OldDecl)) {
NewFD->setPreviousDeclaration(OldTemplateDecl->getTemplatedDecl());
FunctionTemplateDecl *NewTemplateDecl
= NewFD->getDescribedFunctionTemplate();
assert(NewTemplateDecl && "Template/non-template mismatch");
if (CXXMethodDecl *Method
= dyn_cast<CXXMethodDecl>(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<CXXMethodDecl>(NewFD)) // Set access for out-of-line definitions
NewFD->setAccess(OldDecl->getAccess());
NewFD->setPreviousDeclaration(cast<FunctionDecl>(OldDecl));
// 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<CXXMethodDecl>(NewFD)) // Set access for out-of-line definitions
NewFD->setAccess(OldDecl->getAccess());
NewFD->setPreviousDeclaration(cast<FunctionDecl>(OldDecl));
}
}

View File

@ -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<CXXMethodDecl>(Old);
CXXMethodDecl* NewMethod = dyn_cast<CXXMethodDecl>(New);
CXXMethodDecl *OldMethod = dyn_cast<CXXMethodDecl>(Old);
CXXMethodDecl *NewMethod = dyn_cast<CXXMethodDecl>(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<CXXConstructorDecl>(NewMethod))
NewQuals |= Qualifiers::Const;
if (OldMethod->getTypeQualifiers() != NewQuals)
return true;
}
// The signatures match; this is not an overload.

View File

@ -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<CXXMethodDecl>(FunTmpl->getTemplatedDecl());
if (OldMD && OldMD->isConst()) {
const FunctionProtoType *FPT = FT->castAs<FunctionProtoType>();
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;

View File

@ -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:

View File

@ -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<typename T> constexpr T tm();
template<typename T> 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<typename T> constexpr T S::tm() { return T(); }
template<typename T> constexpr T S::ts() { return T(); }
namespace std_example {
class debug_flag { // expected-note {{not an aggregate and has no constexpr constructors}}

View File

@ -12,7 +12,7 @@ struct Y {
constexpr int f() { return 0; }
};
template constexpr int Y<int>::f(); // expected-error{{explicit instantiation cannot be 'constexpr'}}
template constexpr int Y<int>::f() const; // expected-error{{explicit instantiation cannot be 'constexpr'}}
template<typename T>
struct Z {