From: John McCall Date: Fri, 10 Sep 2010 23:21:22 +0000 (+0000) Subject: Support in-class initialization of static const floating-point data members. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4e6356426fcfef84e2484820814a8eaaaf547eda;p=clang Support in-class initialization of static const floating-point data members. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@113663 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 849c5c477c..da5ebc2a25 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -619,7 +619,7 @@ def err_not_integral_type_bitfield : Error< def err_not_integral_type_anon_bitfield : Error< "anonymous bit-field has non-integral type %0">; def err_member_initialization : Error< - "%0 can only be initialized if it is a static const integral data member">; + "fields can only be initialized in constructors">; def err_member_function_initialization : Error< "initializer on function does not look like a pure-specifier">; def err_non_virtual_pure : Error< @@ -2826,10 +2826,15 @@ def err_base_init_direct_and_virtual : Error< def err_not_direct_base_or_virtual : Error< "type %0 is not a direct or virtual base of %1">; -def err_in_class_initializer_non_integral_type : Error< - "in-class initializer has non-integral, non-enumeration type %0">; +def err_in_class_initializer_non_const : Error< + "non-const static data member must be initialized out of line">; +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">; def err_in_class_initializer_non_constant : Error< - "in-class initializer is not an integral constant expression">; + "in-class initializer is not a constant expression">; // C++ anonymous unions and GNU anonymous structs/unions def ext_anonymous_union : Extension< diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index dbc758f1f2..e4dca2ba98 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -4115,8 +4115,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { if (getLangOptions().CPlusPlus && RealDecl->getLexicalDeclContext()->isRecord() && isa(RealDecl)) - Diag(RealDecl->getLocation(), diag::err_member_initialization) - << cast(RealDecl)->getDeclName(); + Diag(RealDecl->getLocation(), diag::err_member_initialization); else Diag(RealDecl->getLocation(), diag::err_illegal_initializer); RealDecl->setInvalidDecl(); @@ -4220,34 +4219,38 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { // static const int value = 17; // }; - // Attach the initializer - VDecl->setInit(Init); + // Try to perform the initialization regardless. + if (!VDecl->isInvalidDecl()) { + InitializationSequence InitSeq(*this, Entity, Kind, &Init, 1); + ExprResult Result = InitSeq.Perform(*this, Entity, Kind, + MultiExprArg(*this, &Init, 1), + &DclT); + if (Result.isInvalid()) { + VDecl->setInvalidDecl(); + return; + } + + Init = Result.takeAs(); + } // C++ [class.mem]p4: // 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. QualType T = VDecl->getType(); - if (!T->isDependentType() && - (!Context.getCanonicalType(T).isConstQualified() || - !T->isIntegralOrEnumerationType())) { - Diag(VDecl->getLocation(), diag::err_member_initialization) - << VDecl->getDeclName() << Init->getSourceRange(); + + // Do nothing on dependent types. + if (T->isDependentType()) { + + // Require constness. + } else if (!T.isConstQualified()) { + Diag(VDecl->getLocation(), diag::err_in_class_initializer_non_const) + << Init->getSourceRange(); VDecl->setInvalidDecl(); - } else { - // C++ [class.static.data]p4: - // If a static data member is of const integral or const - // enumeration type, its declaration in the class definition - // can specify a constant-initializer which shall be an - // integral constant expression (5.19). - if (!Init->isTypeDependent() && - !Init->getType()->isIntegralOrEnumerationType()) { - // We have a non-dependent, non-integral or enumeration type. - Diag(Init->getSourceRange().getBegin(), - diag::err_in_class_initializer_non_integral_type) - << Init->getType() << Init->getSourceRange(); - VDecl->setInvalidDecl(); - } else if (!Init->isTypeDependent() && !Init->isValueDependent()) { + + // We allow integer constant expressions in all cases. + } else if (T->isIntegralOrEnumerationType()) { + if (!Init->isValueDependent()) { // Check whether the expression is a constant expression. llvm::APSInt Value; SourceLocation Loc; @@ -4255,8 +4258,33 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { Diag(Loc, diag::err_in_class_initializer_non_constant) << Init->getSourceRange(); VDecl->setInvalidDecl(); - } else if (!VDecl->getType()->isDependentType()) - ImpCastExprToType(Init, VDecl->getType(), CK_IntegralCast); + } + } + + // 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; + } + + if (!Allowed) { + Diag(VDecl->getLocation(), diag::err_in_class_initializer_bad_type) + << T << Init->getSourceRange(); + VDecl->setInvalidDecl(); + + // TODO: there are probably expressions that pass here that shouldn't. + } else if (!Init->isValueDependent() && + !Init->isConstantInitializer(Context, false)) { + Diag(Init->getExprLoc(), diag::err_in_class_initializer_non_constant) + << Init->getSourceRange(); + VDecl->setInvalidDecl(); } } } else if (VDecl->isFileVarDecl()) { diff --git a/test/SemaCXX/class.cpp b/test/SemaCXX/class.cpp index e51d0fdff9..ad0792c9c4 100644 --- a/test/SemaCXX/class.cpp +++ b/test/SemaCXX/class.cpp @@ -12,16 +12,18 @@ public: } class NestedC { + public: + NestedC(int); void m() { sx = 0; - x = 0; // expected-error {{error: invalid use of nonstatic data member 'x'}} + x = 0; // expected-error {{invalid use of nonstatic data member 'x'}} } }; int b : 1, w : 2; int : 1, : 2; typedef int E : 1; // expected-error {{typedef member 'E' cannot be a bit-field}} - static int sb : 1; // expected-error {{error: static member 'sb' cannot be a bit-field}} + static int sb : 1; // expected-error {{static member 'sb' cannot be a bit-field}} static int vs; typedef int func(); @@ -32,10 +34,10 @@ public: enum E1 { en1, en2 }; - int i = 0; // expected-error {{error: 'i' can only be initialized if it is a static const integral data member}} - static int si = 0; // expected-error {{error: 'si' can only be initialized if it is a static const integral data member}} - static const NestedC ci = 0; // expected-error {{error: 'ci' can only be initialized if it is a static const integral data member}} - static const int nci = vs; // expected-error {{in-class initializer is not an integral constant expression}} + int i = 0; // expected-error {{fields can only be initialized in constructors}} + static int si = 0; // expected-error {{non-const static data member must be initialized out of line}} + static const NestedC ci = 0; // expected-error {{static data member of type 'const C::NestedC' must be initialized out of line}} + static const int nci = vs; // expected-error {{in-class initializer is not a constant expression}} static const int vi = 0; static const E evi = 0; @@ -165,3 +167,12 @@ namespace rdar8066414 { C() {} } // expected-error{{expected ';' after class}} } + +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}} + }; +} diff --git a/test/SemaTemplate/instantiate-static-var.cpp b/test/SemaTemplate/instantiate-static-var.cpp index e90ac5223d..cd25ccb3d3 100644 --- a/test/SemaTemplate/instantiate-static-var.cpp +++ b/test/SemaTemplate/instantiate-static-var.cpp @@ -2,7 +2,7 @@ template class X { public: - static const T value = 10 / Divisor; // expected-error{{in-class initializer is not an integral constant expression}} + static const T value = 10 / Divisor; // expected-error{{in-class initializer is not a constant expression}} }; int array1[X::value == 5? 1 : -1]; @@ -11,7 +11,7 @@ X xi0; // expected-note{{in instantiation of template class 'X' template class Y { - static const T value = 0; // expected-error{{'value' can only be initialized if it is a static const integral data member}} + static const T value = 0; // expected-warning{{in-class initializer for static data member of type 'float const' is a C++0x extension}} }; Y fy; // expected-note{{in instantiation of template class 'Y' requested here}}