forked from OSchip/llvm-project
constexpr: semantic checking for constexpr variables.
We had an extension which allowed const static class members of floating-point type to have in-class initializers, 'as a C++0x extension'. However, C++0x does not allow this. The extension has been kept, and extended to all literal types in C++0x mode (with a fixit to add the 'constexpr' specifier). llvm-svn: 140801
This commit is contained in:
parent
d71061298c
commit
2316cd8b79
|
@ -303,9 +303,7 @@ def NonGCC : DiagGroup<"non-gcc",
|
|||
|
||||
// A warning group for warnings about using C++0x features as extensions in
|
||||
// earlier C++ versions.
|
||||
def CXX0xStaticNonIntegralInitializer :
|
||||
DiagGroup<"c++0x-static-nonintegral-init">;
|
||||
def CXX0x : DiagGroup<"c++0x-extensions", [CXX0xStaticNonIntegralInitializer]>;
|
||||
def CXX0x : DiagGroup<"c++0x-extensions">;
|
||||
def DelegatingCtorCycles :
|
||||
DiagGroup<"delegating-ctor-cycles">;
|
||||
|
||||
|
|
|
@ -1186,7 +1186,15 @@ def err_constexpr_tag : Error<
|
|||
def err_constexpr_dtor : Error<"destructor cannot be marked constexpr">;
|
||||
def err_constexpr_no_declarators : Error<
|
||||
"constexpr can only be used in variable and function declarations">;
|
||||
|
||||
def err_invalid_constexpr_var_decl : Error<
|
||||
"constexpr variable declaration must be a definition">;
|
||||
def err_constexpr_var_requires_init : Error<
|
||||
"declaration of constexpr variable %0 requires an initializer">;
|
||||
def err_constexpr_initialized_static_member : Error<
|
||||
"definition of initialized static data member %0 cannot be marked constexpr">;
|
||||
def err_constexpr_var_requires_const_init : Error<
|
||||
"constexpr variable %0 must be initialized by a constant expression">;
|
||||
|
||||
// Objective-C++
|
||||
def err_objc_decls_may_only_appear_in_global_scope : Error<
|
||||
"Objective-C declarations may only appear in global scope">;
|
||||
|
@ -4072,8 +4080,12 @@ def err_in_class_initializer_non_const : Error<
|
|||
def err_in_class_initializer_bad_type : Error<
|
||||
"static data member of type %0 must be initialized out of line">;
|
||||
def ext_in_class_initializer_float_type : ExtWarn<
|
||||
"in-class initializer for static data member of type %0 "
|
||||
"is a C++0x extension">, InGroup<CXX0xStaticNonIntegralInitializer>;
|
||||
"in-class initializer for static data member of type %0 not allowed, "
|
||||
"accepted as an extension">, InGroup<DiagGroup<"static-member-init">>;
|
||||
def ext_in_class_initializer_literal_type : ExtWarn<
|
||||
"in-class initializer for static data member of type %0 requires "
|
||||
"'constexpr' specifier, accepted as an extension">,
|
||||
InGroup<DiagGroup<"static-member-init">>;
|
||||
def err_in_class_initializer_non_constant : Error<
|
||||
"in-class initializer is not a constant expression">;
|
||||
|
||||
|
|
|
@ -3833,8 +3833,36 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
|
|||
}
|
||||
|
||||
if (D.getDeclSpec().isConstexprSpecified()) {
|
||||
// FIXME: check this is a valid use of constexpr.
|
||||
NewVD->setConstexpr(true);
|
||||
// FIXME: once we know whether there's an initializer, apply this to
|
||||
// static data members too.
|
||||
if (!NewVD->isStaticDataMember() &&
|
||||
!NewVD->isThisDeclarationADefinition()) {
|
||||
// 'constexpr' is redundant and ill-formed on a non-defining declaration
|
||||
// of a variable. Suggest replacing it with 'const' if appropriate.
|
||||
SourceLocation ConstexprLoc = D.getDeclSpec().getConstexprSpecLoc();
|
||||
SourceRange ConstexprRange(ConstexprLoc, ConstexprLoc);
|
||||
// If the declarator is complex, we need to move the keyword to the
|
||||
// innermost chunk as we switch it from 'constexpr' to 'const'.
|
||||
int Kind = DeclaratorChunk::Paren;
|
||||
for (unsigned I = 0, E = D.getNumTypeObjects(); I != E; ++I) {
|
||||
Kind = D.getTypeObject(I).Kind;
|
||||
if (Kind != DeclaratorChunk::Paren)
|
||||
break;
|
||||
}
|
||||
if ((D.getDeclSpec().getTypeQualifiers() & DeclSpec::TQ_const) ||
|
||||
Kind == DeclaratorChunk::Reference)
|
||||
Diag(ConstexprLoc, diag::err_invalid_constexpr_var_decl)
|
||||
<< FixItHint::CreateRemoval(ConstexprRange);
|
||||
else if (Kind == DeclaratorChunk::Paren)
|
||||
Diag(ConstexprLoc, diag::err_invalid_constexpr_var_decl)
|
||||
<< FixItHint::CreateReplacement(ConstexprRange, "const");
|
||||
else
|
||||
Diag(ConstexprLoc, diag::err_invalid_constexpr_var_decl)
|
||||
<< FixItHint::CreateRemoval(ConstexprRange)
|
||||
<< FixItHint::CreateInsertion(D.getIdentifierLoc(), "const ");
|
||||
} else {
|
||||
NewVD->setConstexpr(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5796,11 +5824,26 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
|
|||
// A member-declarator can contain a constant-initializer only
|
||||
// if it declares a static member (9.4) of const integral or
|
||||
// const enumeration type, see 9.4.2.
|
||||
//
|
||||
// C++0x [class.static.data]p3:
|
||||
// If a non-volatile const static data member is of integral or
|
||||
// enumeration type, its declaration in the class definition can
|
||||
// specify a brace-or-equal-initializer in which every initalizer-clause
|
||||
// that is an assignment-expression is a constant expression. A static
|
||||
// data member of literal type can be declared in the class definition
|
||||
// with the constexpr specifier; if so, its declaration shall specify a
|
||||
// brace-or-equal-initializer in which every initializer-clause that is
|
||||
// an assignment-expression is a constant expression.
|
||||
QualType T = VDecl->getType();
|
||||
|
||||
// Do nothing on dependent types.
|
||||
if (T->isDependentType()) {
|
||||
|
||||
// Allow any 'static constexpr' members, whether or not they are of literal
|
||||
// type. We separately check that the initializer is a constant expression,
|
||||
// which implicitly requires the member to be of literal type.
|
||||
} else if (VDecl->isConstexpr()) {
|
||||
|
||||
// Require constness.
|
||||
} else if (!T.isConstQualified()) {
|
||||
Diag(VDecl->getLocation(), diag::err_in_class_initializer_non_const)
|
||||
|
@ -5809,6 +5852,9 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
|
|||
|
||||
// We allow integer constant expressions in all cases.
|
||||
} else if (T->isIntegralOrEnumerationType()) {
|
||||
// FIXME: In C++0x, a non-constexpr const static data member with an
|
||||
// in-class initializer cannot be volatile.
|
||||
|
||||
// Check whether the expression is a constant expression.
|
||||
SourceLocation Loc;
|
||||
if (Init->isValueDependent())
|
||||
|
@ -5828,31 +5874,28 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
|
|||
VDecl->setInvalidDecl();
|
||||
}
|
||||
|
||||
// We allow floating-point constants as an extension in C++03, and
|
||||
// C++0x has far more complicated rules that we don't really
|
||||
// implement fully.
|
||||
} else {
|
||||
bool Allowed = false;
|
||||
if (getLangOptions().CPlusPlus0x) {
|
||||
Allowed = T->isLiteralType();
|
||||
} else if (T->isFloatingType()) { // also permits complex, which is ok
|
||||
Diag(VDecl->getLocation(), diag::ext_in_class_initializer_float_type)
|
||||
<< T << Init->getSourceRange();
|
||||
Allowed = true;
|
||||
}
|
||||
// Suggest adding 'constexpr' in C++0x for literal types.
|
||||
} else if (getLangOptions().CPlusPlus0x && T->isLiteralType()) {
|
||||
Diag(VDecl->getLocation(), diag::ext_in_class_initializer_literal_type)
|
||||
<< T << Init->getSourceRange()
|
||||
<< FixItHint::CreateInsertion(VDecl->getLocStart(), "constexpr ");
|
||||
VDecl->setConstexpr(true);
|
||||
|
||||
if (!Allowed) {
|
||||
Diag(VDecl->getLocation(), diag::err_in_class_initializer_bad_type)
|
||||
<< T << Init->getSourceRange();
|
||||
VDecl->setInvalidDecl();
|
||||
// We allow floating-point constants as an extension.
|
||||
} else if (T->isFloatingType()) { // also permits complex, which is ok
|
||||
Diag(VDecl->getLocation(), diag::ext_in_class_initializer_float_type)
|
||||
<< T << Init->getSourceRange();
|
||||
|
||||
// TODO: there are probably expressions that pass here that shouldn't.
|
||||
} else if (!Init->isValueDependent() &&
|
||||
!Init->isConstantInitializer(Context, false)) {
|
||||
if (!Init->isValueDependent() &&
|
||||
!Init->isConstantInitializer(Context, false)) {
|
||||
Diag(Init->getExprLoc(), diag::err_in_class_initializer_non_constant)
|
||||
<< Init->getSourceRange();
|
||||
VDecl->setInvalidDecl();
|
||||
}
|
||||
} else {
|
||||
Diag(VDecl->getLocation(), diag::err_in_class_initializer_bad_type)
|
||||
<< T << Init->getSourceRange();
|
||||
VDecl->setInvalidDecl();
|
||||
}
|
||||
} else if (VDecl->isFileVarDecl()) {
|
||||
if (VDecl->getStorageClassAsWritten() == SC_Extern &&
|
||||
|
@ -5893,6 +5936,17 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
|
|||
|
||||
if (!VDecl->isInvalidDecl())
|
||||
checkUnsafeAssigns(VDecl->getLocation(), VDecl->getType(), Init);
|
||||
|
||||
if (VDecl->isConstexpr() && !VDecl->isInvalidDecl() &&
|
||||
!VDecl->getType()->isDependentType() &&
|
||||
!Init->isTypeDependent() && !Init->isValueDependent() &&
|
||||
!Init->isConstantInitializer(Context,
|
||||
VDecl->getType()->isReferenceType())) {
|
||||
// FIXME: Improve this diagnostic to explain why the initializer is not
|
||||
// a constant expression.
|
||||
Diag(VDecl->getLocation(), diag::err_constexpr_var_requires_const_init)
|
||||
<< VDecl << Init->getSourceRange();
|
||||
}
|
||||
|
||||
Init = MaybeCreateExprWithCleanups(Init);
|
||||
// Attach the initializer to the decl.
|
||||
|
@ -5958,6 +6012,24 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl,
|
|||
return;
|
||||
}
|
||||
|
||||
// C++0x [dcl.constexpr]p9: An object or reference declared constexpr must
|
||||
// have an initializer.
|
||||
// C++0x [class.static.data]p3: A static data member can be declared with
|
||||
// the constexpr specifier; if so, its declaration shall specify
|
||||
// a brace-or-equal-initializer.
|
||||
if (Var->isConstexpr()) {
|
||||
// FIXME: Provide fix-its to convert the constexpr to const.
|
||||
if (Var->isStaticDataMember() && Var->getAnyInitializer()) {
|
||||
Diag(Var->getLocation(), diag::err_constexpr_initialized_static_member)
|
||||
<< Var->getDeclName();
|
||||
} else {
|
||||
Diag(Var->getLocation(), diag::err_constexpr_var_requires_init)
|
||||
<< Var->getDeclName();
|
||||
}
|
||||
Var->setInvalidDecl();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (Var->isThisDeclarationADefinition()) {
|
||||
case VarDecl::Definition:
|
||||
if (!Var->isStaticDataMember() || !Var->getAnyInitializer())
|
||||
|
@ -6151,9 +6223,8 @@ void Sema::ActOnCXXForRangeDecl(Decl *D) {
|
|||
case SC_OpenCLWorkGroupLocal:
|
||||
llvm_unreachable("Unexpected storage class");
|
||||
}
|
||||
// FIXME: constexpr isn't allowed here.
|
||||
//if (DS.isConstexprSpecified())
|
||||
// Error = 5;
|
||||
if (VD->isConstexpr())
|
||||
Error = 5;
|
||||
if (Error != -1) {
|
||||
Diag(VD->getOuterLocStart(), diag::err_for_range_storage_class)
|
||||
<< VD->getDeclName() << Error;
|
||||
|
|
|
@ -1239,14 +1239,8 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
|
|||
if (Init)
|
||||
AddInitializerToDecl(Member, Init, false,
|
||||
DS.getTypeSpecType() == DeclSpec::TST_auto);
|
||||
else if (DS.getTypeSpecType() == DeclSpec::TST_auto &&
|
||||
DS.getStorageClassSpec() == DeclSpec::SCS_static) {
|
||||
// C++0x [dcl.spec.auto]p4: 'auto' can only be used in the type of a static
|
||||
// data member if a brace-or-equal-initializer is provided.
|
||||
Diag(Loc, diag::err_auto_var_requires_init)
|
||||
<< Name << cast<ValueDecl>(Member)->getType();
|
||||
Member->setInvalidDecl();
|
||||
}
|
||||
else if (DS.getStorageClassSpec() == DeclSpec::SCS_static)
|
||||
ActOnUninitializedDecl(Member, DS.getTypeSpecType() == DeclSpec::TST_auto);
|
||||
|
||||
FinalizeDeclaration(Member);
|
||||
|
||||
|
@ -8727,10 +8721,21 @@ void Sema::AddCXXDirectInitializerToDecl(Decl *RealDecl,
|
|||
return;
|
||||
}
|
||||
|
||||
CheckImplicitConversions(Result.get(), LParenLoc);
|
||||
Expr *Init = Result.get();
|
||||
CheckImplicitConversions(Init, LParenLoc);
|
||||
|
||||
Result = MaybeCreateExprWithCleanups(Result);
|
||||
VDecl->setInit(Result.takeAs<Expr>());
|
||||
if (VDecl->isConstexpr() && !VDecl->isInvalidDecl() &&
|
||||
!Init->isValueDependent() &&
|
||||
!Init->isConstantInitializer(Context,
|
||||
VDecl->getType()->isReferenceType())) {
|
||||
// FIXME: Improve this diagnostic to explain why the initializer is not
|
||||
// a constant expression.
|
||||
Diag(VDecl->getLocation(), diag::err_constexpr_var_requires_const_init)
|
||||
<< VDecl << Init->getSourceRange();
|
||||
}
|
||||
|
||||
Init = MaybeCreateExprWithCleanups(Init);
|
||||
VDecl->setInit(Init);
|
||||
VDecl->setCXXDirectInitializer(true);
|
||||
|
||||
CheckCompleteVariableDeclaration(VDecl);
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
|
||||
|
||||
struct NonLit {
|
||||
NonLit();
|
||||
};
|
||||
|
||||
struct S {
|
||||
static constexpr int a = 0;
|
||||
static constexpr int b; // expected-error {{declaration of constexpr variable 'b' requires an initializer}}
|
||||
|
||||
static constexpr int c = 0;
|
||||
static const int d;
|
||||
|
||||
static constexpr double e = 0.0; // ok
|
||||
static const double f = 0.0; // expected-warning {{accepted as an extension}}
|
||||
static char *const g = 0; // expected-warning {{accepted as an extension}}
|
||||
static const NonLit h = NonLit(); // expected-error {{must be initialized out of line}}
|
||||
};
|
||||
|
||||
constexpr int S::a; // expected-error {{definition of initialized static data member 'a' cannot be marked constexpr}}
|
||||
constexpr int S::b = 0;
|
||||
|
||||
const int S::c;
|
||||
constexpr int S::d = 0;
|
|
@ -11,19 +11,22 @@ struct notlit2 {
|
|||
constexpr int i1 = 0;
|
||||
constexpr int f1() { return 0; }
|
||||
struct s1 {
|
||||
constexpr static int mi = 0;
|
||||
constexpr static int mi1 = 0;
|
||||
const static int mi2;
|
||||
};
|
||||
constexpr int s1::mi2 = 0;
|
||||
|
||||
// invalid declarations
|
||||
// not a definition of an object
|
||||
constexpr extern int i2; // x
|
||||
constexpr extern int i2; // expected-error {{constexpr variable declaration must be a definition}}
|
||||
// not a literal type
|
||||
constexpr notlit nl1; // x
|
||||
constexpr notlit nl1; // expected-error {{declaration of constexpr variable 'nl1' requires an initializer}}
|
||||
// function parameters
|
||||
void f2(constexpr int i) {} // expected-error {{function parameter cannot be constexpr}}
|
||||
// non-static member
|
||||
struct s2 {
|
||||
constexpr int mi; // expected-error {{non-static data member cannot be constexpr}}
|
||||
constexpr int mi1; // expected-error {{non-static data member cannot be constexpr}}
|
||||
static constexpr int mi2; // expected-error {{requires an initializer}}
|
||||
};
|
||||
// typedef
|
||||
typedef constexpr int CI; // expected-error {{typedef cannot be constexpr}}
|
||||
|
@ -63,7 +66,8 @@ constexpr T ft(T t) { return t; }
|
|||
template <>
|
||||
notlit ft(notlit nl) { return nl; }
|
||||
|
||||
constexpr int i3 = ft(1);
|
||||
// FIXME: The initializer is a constant expression.
|
||||
constexpr int i3 = ft(1); // unexpected-error {{must be initialized by a constant expression}}
|
||||
|
||||
void test() {
|
||||
// ignore constexpr when instantiating with non-literal
|
||||
|
@ -85,17 +89,17 @@ constexpr pixel::pixel(int a)
|
|||
: x(square(a)), y(square(a))
|
||||
{ }
|
||||
|
||||
constexpr pixel small(2); // x (no definition of square(int) yet, so can't
|
||||
// constexpr-eval pixel(int))
|
||||
constexpr pixel small(2); // expected-error {{must be initialized by a constant expression}}
|
||||
|
||||
constexpr int square(int x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
constexpr pixel large(4); // now valid
|
||||
// FIXME: The initializer is a constant expression.
|
||||
constexpr pixel large(4); // unexpected-error {{must be initialized by a constant expression}}
|
||||
|
||||
int next(constexpr int x) { // expected-error {{function parameter cannot be constexpr}}
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
extern constexpr int memsz; // x
|
||||
extern constexpr int memsz; // expected-error {{constexpr variable declaration must be a definition}}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
|
||||
|
||||
// A constexpr specifier used in an object declaration declares the object as
|
||||
// const.
|
||||
constexpr int a = 0;
|
||||
extern const int a;
|
||||
|
||||
int i;
|
||||
constexpr int *b = &i;
|
||||
extern int *const b;
|
||||
|
||||
constexpr int &c = i;
|
||||
extern int &c;
|
||||
|
||||
constexpr int (*d)(int) = 0;
|
||||
extern int (*const d)(int);
|
||||
|
||||
// A variable declaration which uses the constexpr specifier shall have an
|
||||
// initializer and shall be initialized by a constant expression.
|
||||
constexpr int ni1; // expected-error {{declaration of constexpr variable 'ni1' requires an initializer}}
|
||||
constexpr struct C { C(); } ni2; // expected-error {{declaration of constexpr variable 'ni2' requires an initializer}}
|
||||
constexpr double &ni3; // expected-error {{declaration of constexpr variable 'ni3' requires an initializer}}
|
||||
|
||||
constexpr int nc1 = i; // expected-error {{constexpr variable 'nc1' must be initialized by a constant expression}}
|
||||
constexpr C nc2 = C(); // expected-error {{constexpr variable 'nc2' must be initialized by a constant expression}}
|
||||
int &f();
|
||||
constexpr int &nc3 = f(); // expected-error {{constexpr variable 'nc3' must be initialized by a constant expression}}
|
||||
constexpr int nc4(i); // expected-error {{constexpr variable 'nc4' must be initialized by a constant expression}}
|
||||
constexpr C nc5((C())); // expected-error {{constexpr variable 'nc5' must be initialized by a constant expression}}
|
||||
int &f();
|
||||
constexpr int &nc6(f()); // expected-error {{constexpr variable 'nc6' must be initialized by a constant expression}}
|
||||
|
||||
struct pixel {
|
||||
int x, y;
|
||||
};
|
||||
constexpr pixel ur = { 1294, 1024 }; // ok
|
||||
constexpr pixel origin; // expected-error {{requires an initializer}}
|
|
@ -100,8 +100,7 @@ void g() {
|
|||
for (extern int a : A()) {} // expected-error {{loop variable 'a' may not be declared 'extern'}}
|
||||
for (static int a : A()) {} // expected-error {{loop variable 'a' may not be declared 'static'}}
|
||||
for (register int a : A()) {} // expected-error {{loop variable 'a' may not be declared 'register'}}
|
||||
// FIXME: when clang supports constexpr, this should be rejected.
|
||||
for (constexpr int a : A()) {} // desired-error {{loop variable 'a' may not be declared 'constexpr'}}
|
||||
for (constexpr int a : A()) {} // expected-error {{loop variable 'a' may not be declared 'constexpr'}}
|
||||
|
||||
struct NoBeginADL {
|
||||
null_t alt_end();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// RUN: %clang_cc1 -verify -std=c++0x %s
|
||||
// RUN: cp %s %t
|
||||
// RUN: not %clang_cc1 -x c++ -std=c++0x -fixit %t
|
||||
// RUN: not %clang_cc1 -x c++ -std=c++0x -Werror -fixit %t
|
||||
// RUN: %clang_cc1 -Wall -pedantic -x c++ -std=c++0x %t
|
||||
|
||||
/* This is a test of the various code modification hints that only
|
||||
|
@ -17,3 +17,45 @@ void x() {
|
|||
using ::T = void; // expected-error {{name defined in alias declaration must be an identifier}}
|
||||
using typename U = void; // expected-error {{name defined in alias declaration must be an identifier}}
|
||||
using typename ::V = void; // expected-error {{name defined in alias declaration must be an identifier}}
|
||||
|
||||
namespace Constexpr {
|
||||
extern constexpr int a; // expected-error {{must be a definition}}
|
||||
// -> extern const int a;
|
||||
|
||||
extern constexpr int *b; // expected-error {{must be a definition}}
|
||||
// -> extern int *const b;
|
||||
|
||||
extern constexpr int &c; // expected-error {{must be a definition}}
|
||||
// -> extern int &b;
|
||||
|
||||
extern constexpr const int d; // expected-error {{must be a definition}}
|
||||
// -> extern const int d;
|
||||
|
||||
int z;
|
||||
constexpr int a = 0;
|
||||
constexpr int *b = &z;
|
||||
constexpr int &c = z;
|
||||
constexpr int d = a;
|
||||
|
||||
// FIXME: Provide FixIts for static data members too.
|
||||
#if 0
|
||||
struct S {
|
||||
static constexpr int a = 0;
|
||||
|
||||
static constexpr int b; // xpected-error {{requires an initializer}}
|
||||
// -> const int b;
|
||||
};
|
||||
|
||||
constexpr int S::a; // xpected-error {{requires an initializer}}
|
||||
// -> const int S::a;
|
||||
|
||||
constexpr int S::b = 0;
|
||||
#endif
|
||||
|
||||
struct S {
|
||||
static const double d = 0.0; // expected-warning {{accepted as an extension}}
|
||||
// -> constexpr static const double d = 0.0;
|
||||
static char *const p = 0; // expected-warning {{accepted as an extension}}
|
||||
// -> constexpr static char *const p = 0;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -172,8 +172,8 @@ namespace rdar8367341 {
|
|||
float foo();
|
||||
|
||||
struct A {
|
||||
static const float x = 5.0f; // expected-warning {{in-class initializer for static data member of type 'const float' is a C++0x extension}}
|
||||
static const float y = foo(); // expected-warning {{in-class initializer for static data member of type 'const float' is a C++0x extension}} expected-error {{in-class initializer is not a constant expression}}
|
||||
static const float x = 5.0f; // expected-warning {{in-class initializer for static data member of type 'const float' not allowed}}
|
||||
static const float y = foo(); // expected-warning {{in-class initializer for static data member of type 'const float' not allowed}} expected-error {{in-class initializer is not a constant expression}}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ X<int, 0> xi0; // expected-note{{in instantiation of template class 'X<int, 0>'
|
|||
|
||||
template<typename T>
|
||||
class Y {
|
||||
static const T value = 0; // expected-warning{{in-class initializer for static data member of type 'const float' is a C++0x extension}}
|
||||
static const T value = 0; // expected-warning{{in-class initializer for static data member of type 'const float' not allowed, accepted as an extension}}
|
||||
};
|
||||
|
||||
Y<float> fy; // expected-note{{in instantiation of template class 'Y<float>' requested here}}
|
||||
|
|
Loading…
Reference in New Issue