Hi,
The constexpr specifier implicitly marks non-static member function
declarations as const. We currently look at the storage class of the
function to determine whether this marking applies, but as PR12008 points
out, this is inadequate: for out-of-line definitions of constexpr member
functions, we don't know whether the function is static until we have
looked up the previous declaration (and performed some other semantic
checks).
The attached patch fixes this by delaying the addition of the const
qualifier to a constexpr non-static member function until the point where
we know it's not static. For efficiency, the patch modifies the type of the
TypeSourceInfo object in-place; this is safe because the reference to the
TypeSourceInfo object is unique, and the addition of a 'const' qualifier
does not change the TypeLoc representation.
Thanks!
Richard
Index: test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p8.cpp
===================================================================
--- test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p8.cpp (revision 150307)
+++ test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p8.cpp (working copy)
@@ -1,9 +1,14 @@
// 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) {
@@ -12,8 +17,18 @@
int (*f)() = &S::Sf;
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}}
Index: test/CXX/temp/temp.spec/temp.explicit/p1-0x.cpp
===================================================================
--- test/CXX/temp/temp.spec/temp.explicit/p1-0x.cpp (revision 150307)
+++ test/CXX/temp/temp.spec/temp.explicit/p1-0x.cpp (working copy)
@@ -6,10 +6,12 @@
};
template inline void X<int>::f(); // expected-error{{explicit instantiation cannot be 'inline'}}
+template void X<char>::f(); // ok
template<typename T>
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 int Y<char>::f() const; // ok
Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h (revision 150307)
+++ include/clang/AST/ASTContext.h (working copy)
@@ -1743,6 +1743,10 @@
TypeSourceInfo *getNullTypeSourceInfo() { return &NullTypeSourceInfo; }
+ /// \brief Add a 'const' qualifier to the provided function type. There
+ /// must be no other users of this TypeSourceInfo object.
+ void markFunctionTypeAsConstUnsafe(TypeSourceInfo *TSI) const;
+
/// \brief Add a deallocation callback that will be invoked when the
/// ASTContext is destroyed.
///
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp (revision 150307)
+++ lib/Sema/SemaDecl.cpp (working copy)
@@ -5046,15 +5046,36 @@
}
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
- // be either constructors or to return a literal type. Therefore,
- // destructors cannot be declared constexpr.
+ // 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.
+ CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewFD);
+ // Destructors can't be const, so can't be constexpr.
if (isa<CXXDestructorDecl>(NewFD))
Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor);
+ else if (MD && !isa<CXXConstructorDecl>(NewFD) &&
+ (MD->getTypeQualifiers() & Qualifiers::Const) == 0) {
+ // Check whether this is a out-of-line definition of a static
+ // member function.
+ CXXMethodDecl *PrevMD = 0;
+ if (!Previous.empty()) {
+ NamedDecl *Prev = *Previous.begin();
+ if (FunctionTemplateDecl *FTD =
+ dyn_cast_or_null<FunctionTemplateDecl>(Prev))
+ Prev = FTD->getTemplatedDecl();
+ PrevMD = dyn_cast_or_null<CXXMethodDecl>(Prev);
+ }
+ if (!MD->isStatic() && !(PrevMD && PrevMD->isStatic())) {
+ // Add a const qualifier to this function's type.
+ TypeSourceInfo *FI = NewFD->getTypeSourceInfo();
+ Context.markFunctionTypeAsConstUnsafe(FI);
+ NewFD->setType(FI->getType());
+ }
+ }
}
// If __module_private__ was specified, mark the function accordingly.
Index: lib/Sema/SemaType.cpp
===================================================================
--- lib/Sema/SemaType.cpp (revision 150308)
+++ lib/Sema/SemaType.cpp (working copy)
@@ -2462,21 +2462,6 @@
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.
- 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);
- }
-
// 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:
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp (revision 150307)
+++ lib/AST/ASTContext.cpp (working copy)
@@ -1354,6 +1354,16 @@
return DI;
}
+void ASTContext::markFunctionTypeAsConstUnsafe(TypeSourceInfo *FI) const {
+ const FunctionProtoType *FT = FI->getType()->castAs<FunctionProtoType>();
+ FunctionProtoType::ExtProtoInfo EPI = FT->getExtProtoInfo();
+ EPI.TypeQuals |= Qualifiers::Const;
+ // We rely on TypeLoc information for qualified and unqualified function
+ // types having the same representations.
+ FI->Ty = getFunctionType(FT->getResultType(), FT->arg_type_begin(),
+ FT->getNumArgs(), EPI);
+}
+
const ASTRecordLayout &
ASTContext::getASTObjCInterfaceLayout(const ObjCInterfaceDecl *D) const {
return getObjCLayout(D, 0);
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits