From 1274ccd90aec0b205fc838c3d504821ccfb55482 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 8 Oct 2010 23:50:27 +0000 Subject: [PATCH] Implement C++0x scoped enumerations, from Daniel Wallin! (and tweaked a bit by me). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@116122 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LanguageExtensions.html | 6 + include/clang/AST/Decl.h | 69 ++++++++- include/clang/AST/Type.h | 3 + include/clang/Basic/DiagnosticParseKinds.td | 3 + include/clang/Basic/DiagnosticSemaKinds.td | 14 ++ include/clang/Sema/Sema.h | 3 +- lib/AST/ASTContext.cpp | 4 +- lib/AST/ASTImporter.cpp | 2 +- lib/AST/Decl.cpp | 11 +- lib/AST/DeclBase.cpp | 2 +- lib/AST/Type.cpp | 57 ++++--- lib/CodeGen/CGExpr.cpp | 8 +- lib/CodeGen/CodeGenTypes.cpp | 10 +- lib/Lex/PPMacroExpansion.cpp | 1 + lib/Parse/ParseDecl.cpp | 42 +++++- lib/Parse/ParseDeclCXX.cpp | 3 +- lib/Sema/SemaCXXCast.cpp | 9 ++ lib/Sema/SemaDecl.cpp | 155 +++++++++++++++++--- lib/Sema/SemaExpr.cpp | 16 +- lib/Sema/SemaExprCXX.cpp | 2 +- lib/Sema/SemaOverload.cpp | 13 +- lib/Sema/SemaTemplate.cpp | 3 +- lib/Sema/SemaTemplateInstantiateDecl.cpp | 24 ++- lib/Sema/SemaType.cpp | 2 +- lib/Serialization/ASTReaderDecl.cpp | 7 +- lib/Serialization/ASTWriterDecl.cpp | 6 +- test/SemaCXX/enum-scoped.cpp | 98 +++++++++++++ test/SemaTemplate/enum-forward.cpp | 8 + 28 files changed, 512 insertions(+), 69 deletions(-) create mode 100644 test/SemaCXX/enum-scoped.cpp create mode 100644 test/SemaTemplate/enum-forward.cpp diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index 8faa555f5c..27d90c5cd3 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -42,6 +42,7 @@ td {
  • C++0x type inference
  • C++0x variadic templates
  • C++0x inline namespaces
  • +
  • C++0x strongly-typed enumerations
  • C++0x trailing return type
  • Blocks
  • @@ -368,6 +369,11 @@ inline namespaces is enabled.

    Use __has_feature(cxx_trailing_return) to determine if support for the alternate function declaration syntax with trailing return type is enabled.

    +

    C++0x strongly typed enumerations

    + +

    Use __has_feature(cxx_strong_enums) to determine if support for +strongly typed, scoped enumerations is enabled.

    +

    Blocks

    diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index d4c6600292..1e7b0cd0d4 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -1825,6 +1825,14 @@ protected: unsigned NumPositiveBits : 8; unsigned NumNegativeBits : 8; + /// IsScoped - True if this is tag declaration is a scoped enumeration. Only + /// possible in C++0x mode. + bool IsScoped : 1; + + /// IsFixed - True if this is an enumeration with fixed underlying type. Only + /// possible in C++0x mode. + bool IsFixed : 1; + private: SourceLocation TagKeywordLoc; SourceLocation RBraceLoc; @@ -2008,7 +2016,19 @@ class EnumDecl : public TagDecl { /// IntegerType - This represent the integer type that the enum corresponds /// to for code generation purposes. Note that the enumerator constants may /// have a different type than this does. - QualType IntegerType; + /// + /// If the underlying integer type was explicitly stated in the source + /// code, this is a TypeSourceInfo* for that type. Otherwise this type + /// was automatically deduced somehow, and this is a Type*. + /// + /// Normally if IsFixed(), this would contain a TypeSourceInfo*, but in + /// some cases it won't. + /// + /// The underlying type of an enumeration never has any qualifiers, so + /// we can get away with just storing a raw Type*, and thus save an + /// extra pointer when TypeSourceInfo is needed. + + llvm::PointerUnion IntegerType; /// PromotionType - The integer type that values of this type should /// promote to. In C, enumerators are generally of an integer type @@ -2029,11 +2049,14 @@ class EnumDecl : public TagDecl { }; EnumDecl(DeclContext *DC, SourceLocation L, - IdentifierInfo *Id, EnumDecl *PrevDecl, SourceLocation TKL) + IdentifierInfo *Id, EnumDecl *PrevDecl, SourceLocation TKL, + bool Scoped, bool Fixed) : TagDecl(Enum, TTK_Enum, DC, L, Id, PrevDecl, TKL), InstantiatedFrom(0) { - IntegerType = QualType(); + IntegerType = (const Type*)0; NumNegativeBits = 0; NumPositiveBits = 0; + IsScoped = Scoped; + IsFixed = Fixed; } public: EnumDecl *getCanonicalDecl() { @@ -2052,7 +2075,8 @@ public: static EnumDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L, IdentifierInfo *Id, - SourceLocation TKL, EnumDecl *PrevDecl); + SourceLocation TKL, EnumDecl *PrevDecl, + bool IsScoped, bool IsFixed); static EnumDecl *Create(ASTContext &C, EmptyShell Empty); /// completeDefinition - When created, the EnumDecl corresponds to a @@ -2092,10 +2116,25 @@ public: /// getIntegerType - Return the integer type this enum decl corresponds to. /// This returns a null qualtype for an enum forward definition. - QualType getIntegerType() const { return IntegerType; } + QualType getIntegerType() const { + if (!IntegerType) + return QualType(); + if (const Type* T = IntegerType.dyn_cast()) + return QualType(T, 0); + return IntegerType.get()->getType(); + } /// \brief Set the underlying integer type. - void setIntegerType(QualType T) { IntegerType = T; } + void setIntegerType(QualType T) { IntegerType = T.getTypePtr(); } + + /// \brief Set the underlying integer type source info. + void setIntegerTypeSourceInfo(TypeSourceInfo* TInfo) { IntegerType = TInfo; } + + /// \brief Return the type source info for the underlying integer type, + /// if no type source info exists, return 0. + TypeSourceInfo* getIntegerTypeSourceInfo() const { + return IntegerType.dyn_cast(); + } /// \brief Returns the width in bits requred to store all the /// non-negative enumerators of this enum. @@ -2123,6 +2162,22 @@ public: NumNegativeBits = Num; } + /// \brief Returns true if this is a C++0x scoped enumeration. + bool isScoped() const { + return IsScoped; + } + + /// \brief Returns true if this is a C++0x enumeration with fixed underlying + /// type. + bool isFixed() const { + return IsFixed; + } + + /// \brief Returns true if this can be considered a complete type. + bool isComplete() const { + return isDefinition() || isFixed(); + } + /// \brief Returns the enumeration (declared within the template) /// from which this enumeration type was instantiated, or NULL if /// this enumeration was not instantiated from any template. @@ -2135,6 +2190,8 @@ public: static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classof(const EnumDecl *D) { return true; } static bool classofKind(Kind K) { return K == Enum; } + + friend class ASTDeclReader; }; diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 3cd60d3021..fdd1d165a8 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -879,6 +879,9 @@ public: /// \brief Determine whether this type is an integral or enumeration type. bool isIntegralOrEnumerationType() const; + /// \brief Determine whether this type is an integral or unscoped enumeration + /// type. + bool isIntegralOrUnscopedEnumerationType() const; /// Floating point categories. bool isRealFloatingType() const; // C99 6.2.5p10 (float, double, long double) diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 430ed9f011..273abe1183 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -384,6 +384,9 @@ def err_friend_decl_defines_class : Error< def warn_deleted_function_accepted_as_extension: ExtWarn< "deleted function definition accepted as a C++0x extension">, InGroup; +def err_scoped_enum_missing_identifier : Error< + "scoped enumeration requires a name">; + // Language specific pragmas // - Generic warnings def warn_pragma_expected_lparen : Warning< diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 2d2ed63762..7eb5453fa8 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -826,6 +826,20 @@ def err_final_function_overridden : Error< "declaration of %0 overrides a 'final' function">; def err_final_base : Error< "derivation from 'final' %0">; + +// C++0x scoped enumerations +def err_enum_invalid_underlying : Error< + "non-integral type %0 is an invalid underlying type">; +def err_enumerator_too_large : Error< + "enumerator value is not representable in the underlying type %0">; +def err_enumerator_wrapped : Error< + "enumerator value %0 is not representable in the underlying type %1">; +def err_enum_redeclare_type_mismatch : Error< + "enumeration redeclared with different underlying type">; +def err_enum_redeclare_fixed_mismatch : Error< + "enumeration previously declared with %select{non|}0fixed underlying type">; +def err_enum_redeclare_scoped_mismatch : Error< + "enumeration previously declared as %select{un|}0scoped">; // Objective-C++ def err_objc_decls_may_only_appear_in_global_scope : Error< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 3ef942b2e8..b5a092e756 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -785,7 +785,8 @@ public: IdentifierInfo *Name, SourceLocation NameLoc, AttributeList *Attr, AccessSpecifier AS, MultiTemplateParamsArg TemplateParameterLists, - bool &OwnedDecl, bool &IsDependent); + bool &OwnedDecl, bool &IsDependent, bool ScopedEnum, + TypeResult UnderlyingType); TypeResult ActOnDependentTag(Scope *S, unsigned TagSpec, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 1fddd3256f..72d7f600f0 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -5154,10 +5154,10 @@ QualType ASTContext::mergeObjCGCQualifiers(QualType LHS, QualType RHS) { //===----------------------------------------------------------------------===// unsigned ASTContext::getIntWidth(QualType T) { - if (T->isBooleanType()) - return 1; if (EnumType *ET = dyn_cast(T)) T = ET->getDecl()->getIntegerType(); + if (T->isBooleanType()) + return 1; // For builtin types, just use the standard type sizing method return (unsigned)getTypeSize(T); } diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 78cb536550..d2a3788956 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -1622,7 +1622,7 @@ Decl *ASTNodeImporter::VisitEnumDecl(EnumDecl *D) { EnumDecl *D2 = EnumDecl::Create(Importer.getToContext(), DC, Loc, Name.getAsIdentifierInfo(), Importer.Import(D->getTagKeywordLoc()), - 0); + 0, D->isScoped(), D->isFixed()); // Import the qualifier, if any. if (D->getQualifier()) { NestedNameSpecifier *NNS = Importer.Import(D->getQualifier()); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 081e5ee6ad..f455473059 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1615,14 +1615,16 @@ void TagDecl::setQualifierInfo(NestedNameSpecifier *Qualifier, EnumDecl *EnumDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, IdentifierInfo *Id, SourceLocation TKL, - EnumDecl *PrevDecl) { - EnumDecl *Enum = new (C) EnumDecl(DC, L, Id, PrevDecl, TKL); + EnumDecl *PrevDecl, bool IsScoped, bool IsFixed) { + EnumDecl *Enum = new (C) EnumDecl(DC, L, Id, PrevDecl, TKL, + IsScoped, IsFixed); C.getTypeDeclType(Enum, PrevDecl); return Enum; } EnumDecl *EnumDecl::Create(ASTContext &C, EmptyShell Empty) { - return new (C) EnumDecl(0, SourceLocation(), 0, 0, SourceLocation()); + return new (C) EnumDecl(0, SourceLocation(), 0, 0, SourceLocation(), + false, false); } void EnumDecl::completeDefinition(QualType NewType, @@ -1630,7 +1632,8 @@ void EnumDecl::completeDefinition(QualType NewType, unsigned NumPositiveBits, unsigned NumNegativeBits) { assert(!isDefinition() && "Cannot redefine enums!"); - IntegerType = NewType; + if (!IntegerType) + IntegerType = NewType.getTypePtr(); PromotionType = NewPromotionType; setNumPositiveBits(NumPositiveBits); setNumNegativeBits(NumNegativeBits); diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 26ab9250d7..ece9945390 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -516,7 +516,7 @@ bool DeclContext::isDependentContext() const { bool DeclContext::isTransparentContext() const { if (DeclKind == Decl::Enum) - return true; // FIXME: Check for C++0x scoped enums + return !cast(this)->isScoped(); else if (DeclKind == Decl::LinkageSpec) return true; else if (DeclKind >= Decl::firstRecord && DeclKind <= Decl::lastRecord) diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 61390c8539..11afea0446 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -409,11 +409,10 @@ bool Type::isIntegerType() const { if (const BuiltinType *BT = dyn_cast(CanonicalType)) return BT->getKind() >= BuiltinType::Bool && BT->getKind() <= BuiltinType::Int128; - if (const TagType *TT = dyn_cast(CanonicalType)) + if (const EnumType *ET = dyn_cast(CanonicalType)) // Incomplete enum types are not treated as integer types. // FIXME: In C++, enum types are never integer types. - if (TT->getDecl()->isEnum() && TT->getDecl()->isDefinition()) - return true; + return ET->getDecl()->isComplete(); return false; } @@ -449,9 +448,8 @@ bool Type::isIntegralType(ASTContext &Ctx) const { BT->getKind() <= BuiltinType::Int128; if (!Ctx.getLangOptions().CPlusPlus) - if (const TagType *TT = dyn_cast(CanonicalType)) - if (TT->getDecl()->isEnum() && TT->getDecl()->isDefinition()) - return true; // Complete enum types are integral in C. + if (const EnumType *ET = dyn_cast(CanonicalType)) + return ET->getDecl()->isComplete(); // Complete enum types are integral in C. return false; } @@ -463,13 +461,28 @@ bool Type::isIntegralOrEnumerationType() const { // Check for a complete enum type; incomplete enum types are not properly an // enumeration type in the sense required here. - if (const TagType *TT = dyn_cast(CanonicalType)) - if (TT->getDecl()->isEnum() && TT->getDecl()->isDefinition()) - return true; + if (const EnumType *ET = dyn_cast(CanonicalType)) + return ET->getDecl()->isComplete(); return false; } +bool Type::isIntegralOrUnscopedEnumerationType() const { + if (const BuiltinType *BT = dyn_cast(CanonicalType)) + return BT->getKind() >= BuiltinType::Bool && + BT->getKind() <= BuiltinType::Int128; + + // Check for a complete enum type; incomplete enum types are not properly an + // enumeration type in the sense required here. + // C++0x: However, if the underlying type of the enum is fixed, it is + // considered complete. + if (const EnumType *ET = dyn_cast(CanonicalType)) + return ET->getDecl()->isComplete() && !ET->getDecl()->isScoped(); + + return false; +} + + bool Type::isBooleanType() const { if (const BuiltinType *BT = dyn_cast(CanonicalType)) return BT->getKind() == BuiltinType::Bool; @@ -573,8 +586,8 @@ bool Type::isRealType() const { if (const BuiltinType *BT = dyn_cast(CanonicalType)) return BT->getKind() >= BuiltinType::Bool && BT->getKind() <= BuiltinType::LongDouble; - if (const TagType *TT = dyn_cast(CanonicalType)) - return TT->getDecl()->isEnum() && TT->getDecl()->isDefinition(); + if (const EnumType *ET = dyn_cast(CanonicalType)) + return ET->getDecl()->isComplete() && !ET->getDecl()->isScoped(); return false; } @@ -585,20 +598,21 @@ bool Type::isArithmeticType() const { if (const EnumType *ET = dyn_cast(CanonicalType)) // GCC allows forward declaration of enum types (forbid by C99 6.7.2.3p2). // If a body isn't seen by the time we get here, return false. - return ET->getDecl()->isDefinition(); + // + // C++0x: Enumerations are not arithmetic types. For now, just return + // false for scoped enumerations since that will disable any + // unwanted implicit conversions. + return !ET->getDecl()->isScoped() && ET->getDecl()->isComplete(); return isa(CanonicalType); } bool Type::isScalarType() const { if (const BuiltinType *BT = dyn_cast(CanonicalType)) return BT->getKind() != BuiltinType::Void; - if (const TagType *TT = dyn_cast(CanonicalType)) { + if (const EnumType *ET = dyn_cast(CanonicalType)) // Enums are scalar types, but only if they are defined. Incomplete enums // are not treated as scalar types. - if (TT->getDecl()->isEnum() && TT->getDecl()->isDefinition()) - return true; - return false; - } + return ET->getDecl()->isComplete(); return isa(CanonicalType) || isa(CanonicalType) || isa(CanonicalType) || @@ -646,8 +660,12 @@ bool Type::isIncompleteType() const { // Void is the only incomplete builtin type. Per C99 6.2.5p19, it can never // be completed. return isVoidType(); - case Record: case Enum: + // An enumeration with fixed underlying type is complete (C++0x 7.2p3). + if (cast(CanonicalType)->getDecl()->isFixed()) + return false; + // Fall through. + case Record: // A tagged type (struct/union/enum/class) is incomplete if the decl is a // forward declaration, but not a full definition (C99 6.2.5p22). return !cast(CanonicalType)->getDecl()->isDefinition(); @@ -763,7 +781,8 @@ bool Type::isPromotableIntegerType() const { // Enumerated types are promotable to their compatible integer types // (C99 6.3.1.1) a.k.a. its underlying type (C++ [conv.prom]p2). if (const EnumType *ET = getAs()){ - if (this->isDependentType() || ET->getDecl()->getPromotionType().isNull()) + if (this->isDependentType() || ET->getDecl()->getPromotionType().isNull() + || ET->getDecl()->isScoped()) return false; const BuiltinType *BT diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 78c1da5060..e4ed5d64d9 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -599,11 +599,17 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(llvm::Value *Addr, bool Volatile, return V; } +static bool isBooleanUnderlyingType(QualType Ty) { + if (const EnumType *ET = dyn_cast(Ty)) + return ET->getDecl()->getIntegerType()->isBooleanType(); + return false; +} + void CodeGenFunction::EmitStoreOfScalar(llvm::Value *Value, llvm::Value *Addr, bool Volatile, unsigned Alignment, QualType Ty) { - if (Ty->isBooleanType()) { + if (Ty->isBooleanType() || isBooleanUnderlyingType(Ty)) { // Bool can have different representation in memory than in registers. const llvm::PointerType *DstPtr = cast(Addr->getType()); Value = Builder.CreateIntCast(Value, DstPtr->getElementType(), false); diff --git a/lib/CodeGen/CodeGenTypes.cpp b/lib/CodeGen/CodeGenTypes.cpp index 5ab65c5779..87cab311d4 100644 --- a/lib/CodeGen/CodeGenTypes.cpp +++ b/lib/CodeGen/CodeGenTypes.cpp @@ -424,9 +424,13 @@ const llvm::Type *CodeGenTypes::ConvertTagDeclType(const TagDecl *TD) { if (TDTI != TagDeclTypes.end()) return TDTI->second; + const EnumDecl *ED = dyn_cast(TD); + // If this is still a forward declaration, just define an opaque // type to use for this tagged decl. - if (!TD->isDefinition()) { + // C++0x: If this is a enumeration type with fixed underlying type, + // consider it complete. + if (!TD->isDefinition() && !(ED && ED->isFixed())) { llvm::Type *ResultType = llvm::OpaqueType::get(getLLVMContext()); TagDeclTypes.insert(std::make_pair(Key, ResultType)); return ResultType; @@ -434,8 +438,8 @@ const llvm::Type *CodeGenTypes::ConvertTagDeclType(const TagDecl *TD) { // Okay, this is a definition of a type. Compile the implementation now. - if (TD->isEnum()) // Don't bother storing enums in TagDeclTypes. - return ConvertTypeRecursive(cast(TD)->getIntegerType()); + if (ED) // Don't bother storing enums in TagDeclTypes. + return ConvertTypeRecursive(ED->getIntegerType()); // This decl could well be recursive. In this case, insert an opaque // definition of this type, which the recursive uses will get. We will then diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index ac69595227..24a0f39b05 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -517,6 +517,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("cxx_deleted_functions", true) // Accepted as an extension. .Case("cxx_exceptions", LangOpts.Exceptions) .Case("cxx_rtti", LangOpts.RTTI) + .Case("cxx_strong_enums", LangOpts.CPlusPlus0x) .Case("cxx_static_assert", LangOpts.CPlusPlus0x) .Case("cxx_trailing_return", LangOpts.CPlusPlus0x) .Case("objc_nonfragile_abi", LangOpts.ObjCNonFragileABI) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index e6a4b91e2d..3d29e9e702 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1958,6 +1958,21 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, /// 'enum' identifier /// [GNU] 'enum' attributes[opt] identifier /// +/// [C++0x] enum-head '{' enumerator-list[opt] '}' +/// [C++0x] enum-head '{' enumerator-list ',' '}' +/// +/// enum-head: [C++0x] +/// enum-key attributes[opt] identifier[opt] enum-base[opt] +/// enum-key attributes[opt] nested-name-specifier identifier enum-base[opt] +/// +/// enum-key: [C++0x] +/// 'enum' +/// 'enum' 'class' +/// 'enum' 'struct' +/// +/// enum-base: [C++0x] +/// ':' type-specifier-seq +/// /// [C++] elaborated-type-specifier: /// [C++] 'enum' '::'[opt] nested-name-specifier[opt] identifier /// @@ -1992,6 +2007,14 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, } } + bool IsScopedEnum = false; + + if (getLang().CPlusPlus0x && (Tok.is(tok::kw_class) + || Tok.is(tok::kw_struct))) { + ConsumeToken(); + IsScopedEnum = true; + } + // Must have either 'enum name' or 'enum {...}'. if (Tok.isNot(tok::identifier) && Tok.isNot(tok::l_brace)) { Diag(Tok, diag::err_expected_ident_lbrace); @@ -2009,6 +2032,21 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, NameLoc = ConsumeToken(); } + if (!Name && IsScopedEnum) { + // C++0x 7.2p2: The optional identifier shall not be omitted in the + // declaration of a scoped enumeration. + Diag(Tok, diag::err_scoped_enum_missing_identifier); + IsScopedEnum = false; + } + + TypeResult BaseType; + + if (getLang().CPlusPlus0x && Tok.is(tok::colon)) { + ConsumeToken(); + SourceRange Range; + BaseType = ParseTypeName(&Range); + } + // There are three options here. If we have 'enum foo;', then this is a // forward declaration. If we have 'enum foo {...' then this is a // definition. Otherwise we have something like 'enum foo xyz', a reference. @@ -2045,7 +2083,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, StartLoc, SS, Name, NameLoc, Attr.get(), AS, MultiTemplateParamsArg(Actions), - Owned, IsDependent); + Owned, IsDependent, IsScopedEnum, + BaseType); + if (IsDependent) { // This enum has a dependent nested-name-specifier. Handle it as a // dependent tag. diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 59d4f9fad3..e1a97da9c2 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -955,7 +955,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, MultiTemplateParamsArg(Actions, TemplateParams? &(*TemplateParams)[0] : 0, TemplateParams? TemplateParams->size() : 0), - Owned, IsDependent); + Owned, IsDependent, false, + clang::TypeResult()); // If ActOnTag said the type was dependent, try again with the // less common call. diff --git a/lib/Sema/SemaCXXCast.cpp b/lib/Sema/SemaCXXCast.cpp index 21b1a73aa3..7b1e34a7c3 100644 --- a/lib/Sema/SemaCXXCast.cpp +++ b/lib/Sema/SemaCXXCast.cpp @@ -557,6 +557,15 @@ static TryCastResult TryStaticCast(Sema &Self, Expr *&SrcExpr, QualType SrcType = Self.Context.getCanonicalType(SrcExpr->getType()); + // C++0x 5.2.9p9: A value of a scoped enumeration type can be explicitly + // converted to an integral type. + if (Self.getLangOptions().CPlusPlus0x && SrcType->isEnumeralType()) { + if (DestType->isIntegralType(Self.Context)) { + Kind = CK_IntegralCast; + return TC_Success; + } + } + // Reverse integral promotion/conversion. All such conversions are themselves // again integral promotions or conversions and are thus already handled by // p2 (TryDirectInitialization above). diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 025f696e09..fa3a4047e5 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -5341,7 +5341,8 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, IdentifierInfo *Name, SourceLocation NameLoc, AttributeList *Attr, AccessSpecifier AS, MultiTemplateParamsArg TemplateParameterLists, - bool &OwnedDecl, bool &IsDependent) { + bool &OwnedDecl, bool &IsDependent, bool ScopedEnum, + TypeResult UnderlyingType) { // If this is not a definition, it must have a name. assert((Name != 0 || TUK == TUK_Definition) && "Nameless record must be a definition!"); @@ -5386,6 +5387,34 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, } } + // Figure out the underlying type if this a enum declaration. We need to do + // this early, because it's needed to detect if this is an incompatible + // redeclaration. + llvm::PointerUnion EnumUnderlying; + + if (Kind == TTK_Enum) { + if (UnderlyingType.isInvalid() || (!UnderlyingType.get() && ScopedEnum)) + // No underlying type explicitly specified, or we failed to parse the + // type, default to int. + EnumUnderlying = Context.IntTy.getTypePtr(); + else if (UnderlyingType.get()) { + // C++0x 7.2p2: The type-specifier-seq of an enum-base shall name an + // integral type; any cv-qualification is ignored. + TypeSourceInfo *TI = 0; + QualType T = GetTypeFromParser(UnderlyingType.get(), &TI); + EnumUnderlying = TI; + + SourceLocation UnderlyingLoc = TI->getTypeLoc().getBeginLoc(); + + if (!T->isDependentType() && !T->isIntegralType(Context)) { + Diag(UnderlyingLoc, diag::err_enum_invalid_underlying) + << T; + // Recover by falling back to int. + EnumUnderlying = Context.IntTy.getTypePtr(); + } + } + } + DeclContext *SearchDC = CurContext; DeclContext *DC = CurContext; bool isStdBadAlloc = false; @@ -5622,6 +5651,38 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, } } + if (Kind == TTK_Enum && PrevTagDecl->getTagKind() == TTK_Enum) { + const EnumDecl *PrevEnum = cast(PrevTagDecl); + + // All conflicts with previous declarations are recovered by + // returning the previous declaration. + if (ScopedEnum != PrevEnum->isScoped()) { + Diag(KWLoc, diag::err_enum_redeclare_scoped_mismatch) + << PrevEnum->isScoped(); + Diag(PrevTagDecl->getLocation(), diag::note_previous_use); + return PrevTagDecl; + } + else if (EnumUnderlying && PrevEnum->isFixed()) { + QualType T; + if (TypeSourceInfo *TI = EnumUnderlying.dyn_cast()) + T = TI->getType(); + else + T = QualType(EnumUnderlying.get(), 0); + + if (!Context.hasSameUnqualifiedType(T, PrevEnum->getIntegerType())) { + Diag(KWLoc, diag::err_enum_redeclare_type_mismatch); + Diag(PrevTagDecl->getLocation(), diag::note_previous_use); + return PrevTagDecl; + } + } + else if (!EnumUnderlying.isNull() != PrevEnum->isFixed()) { + Diag(KWLoc, diag::err_enum_redeclare_fixed_mismatch) + << PrevEnum->isFixed(); + Diag(PrevTagDecl->getLocation(), diag::note_previous_use); + return PrevTagDecl; + } + } + if (!Invalid) { // If this is a use, just return the declaration we found. @@ -5757,15 +5818,21 @@ CreateNewDecl: // PrevDecl. TagDecl *New; + bool IsForwardReference = false; if (Kind == TTK_Enum) { // FIXME: Tag decls should be chained to any simultaneous vardecls, e.g.: // enum X { A, B, C } D; D should chain to X. New = EnumDecl::Create(Context, SearchDC, Loc, Name, KWLoc, - cast_or_null(PrevDecl)); + cast_or_null(PrevDecl), ScopedEnum, + !EnumUnderlying.isNull()); // If this is an undefined enum, warn. if (TUK != TUK_Definition && !Invalid) { TagDecl *Def; - if (PrevDecl && (Def = cast(PrevDecl)->getDefinition())) { + if (getLangOptions().CPlusPlus0x && cast(New)->isFixed()) { + // C++0x: 7.2p2: opaque-enum-declaration. + // Conflicts are diagnosed above. Do nothing. + } + else if (PrevDecl && (Def = cast(PrevDecl)->getDefinition())) { Diag(Loc, diag::ext_forward_ref_enum_def) << New; Diag(Def->getLocation(), diag::note_previous_definition); @@ -5776,8 +5843,24 @@ CreateNewDecl: else if (getLangOptions().CPlusPlus) DiagID = diag::err_forward_ref_enum; Diag(Loc, DiagID); + + // If this is a forward-declared reference to an enumeration, make a + // note of it; we won't actually be introducing the declaration into + // the declaration context. + if (TUK == TUK_Reference) + IsForwardReference = true; } } + + if (EnumUnderlying) { + EnumDecl *ED = cast(New); + if (TypeSourceInfo *TI = EnumUnderlying.dyn_cast()) + ED->setIntegerTypeSourceInfo(TI); + else + ED->setIntegerType(QualType(EnumUnderlying.get(), 0)); + ED->setPromotionType(ED->getIntegerType()); + } + } else { // struct/union/class @@ -5869,7 +5952,10 @@ CreateNewDecl: PushOnScopeChains(New, EnclosingScope, /* AddToContext = */ false); } else if (Name) { S = getNonFieldDeclScope(S); - PushOnScopeChains(New, S); + PushOnScopeChains(New, S, !IsForwardReference); + if (IsForwardReference) + SearchDC->makeDeclVisibleInContext(New, /* Recoverable = */ false); + } else { CurContext->addDecl(New); } @@ -6804,9 +6890,11 @@ static bool isRepresentableIntegerValue(ASTContext &Context, assert(T->isIntegralType(Context) && "Integral type required!"); unsigned BitWidth = Context.getIntWidth(T); - if (Value.isUnsigned() || Value.isNonNegative()) - return Value.getActiveBits() < BitWidth; - + if (Value.isUnsigned() || Value.isNonNegative()) { + if (T->isSignedIntegerType()) + --BitWidth; + return Value.getActiveBits() <= BitWidth; + } return Value.getMinSignedBits() <= BitWidth; } @@ -6870,12 +6958,26 @@ EnumConstantDecl *Sema::CheckEnumConstant(EnumDecl *Enum, } } - // C++0x [dcl.enum]p5: - // If the underlying type is not fixed, the type of each enumerator - // is the type of its initializing value: - // - If an initializer is specified for an enumerator, the - // initializing value has the same type as the expression. - EltTy = Val->getType(); + if (Enum->isFixed()) { + EltTy = Enum->getIntegerType(); + + // C++0x [dcl.enum]p5: + // ... if the initializing value of an enumerator cannot be + // represented by the underlying type, the program is ill-formed. + if (!isRepresentableIntegerValue(Context, EnumVal, EltTy)) + Diag(IdLoc, diag::err_enumerator_too_large) + << EltTy; + else + ImpCastExprToType(Val, EltTy, CK_IntegralCast); + } + else { + // C++0x [dcl.enum]p5: + // If the underlying type is not fixed, the type of each enumerator + // is the type of its initializing value: + // - If an initializer is specified for an enumerator, the + // initializing value has the same type as the expression. + EltTy = Val->getType(); + } } } } @@ -6892,7 +6994,12 @@ EnumConstantDecl *Sema::CheckEnumConstant(EnumDecl *Enum, // // GCC uses 'int' for its unspecified integral type, as does // C99 6.7.2.2p3. - EltTy = Context.IntTy; + if (Enum->isFixed()) { + EltTy = Enum->getIntegerType(); + } + else { + EltTy = Context.IntTy; + } } else { // Assign the last value + 1. EnumVal = LastEnumConst->getInitVal(); @@ -6912,13 +7019,20 @@ EnumConstantDecl *Sema::CheckEnumConstant(EnumDecl *Enum, // sufficient to contain the incremented value. If no such type // exists, the program is ill-formed. QualType T = getNextLargerIntegralType(Context, EltTy); - if (T.isNull()) { + if (T.isNull() || Enum->isFixed()) { // There is no integral type larger enough to represent this // value. Complain, then allow the value to wrap around. EnumVal = LastEnumConst->getInitVal(); EnumVal.zext(EnumVal.getBitWidth() * 2); - Diag(IdLoc, diag::warn_enumerator_too_large) - << EnumVal.toString(10); + ++EnumVal; + if (Enum->isFixed()) + // When the underlying type is fixed, this is ill-formed. + Diag(IdLoc, diag::err_enumerator_wrapped) + << EnumVal.toString(10) + << EltTy; + else + Diag(IdLoc, diag::warn_enumerator_too_large) + << EnumVal.toString(10); } else { EltTy = T; } @@ -7091,7 +7205,12 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, if (LangOpts.ShortEnums) Packed = true; - if (NumNegativeBits) { + if (Enum->isFixed()) { + BestType = BestPromotionType = Enum->getIntegerType(); + // We don't set BestWidth, because BestType is going to be the + // type of the enumerators. + } + else if (NumNegativeBits) { // If there is a negative value, figure out the smallest integer type (of // int/long/longlong) that fits. // If it's packed, check also if it fits a char or a short. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index fec8cb9166..e35b6c2455 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -5420,6 +5420,12 @@ QualType Sema::CheckSubtractionOperands(Expr *&lex, Expr *&rex, return InvalidOperands(Loc, lex, rex); } +static bool isScopedEnumerationType(QualType T) { + if (const EnumType *ET = dyn_cast(T)) + return ET->getDecl()->isScoped(); + return false; +} + // C99 6.5.7 QualType Sema::CheckShiftOperands(Expr *&lex, Expr *&rex, SourceLocation Loc, bool isCompAssign) { @@ -5428,6 +5434,13 @@ QualType Sema::CheckShiftOperands(Expr *&lex, Expr *&rex, SourceLocation Loc, !rex->getType()->hasIntegerRepresentation()) return InvalidOperands(Loc, lex, rex); + // C++0x: Don't allow scoped enums. FIXME: Use something better than + // hasIntegerRepresentation() above instead of this. + if (isScopedEnumerationType(lex->getType()) || + isScopedEnumerationType(rex->getType())) { + return InvalidOperands(Loc, lex, rex); + } + // Vector shifts promote their scalar inputs to vector type. if (lex->getType()->isVectorType() || rex->getType()->isVectorType()) return CheckVectorOperands(Loc, lex, rex); @@ -5914,7 +5927,8 @@ inline QualType Sema::CheckBitwiseOperands( QualType compType = UsualArithmeticConversions(lex, rex, isCompAssign); - if (lex->getType()->isIntegerType() && rex->getType()->isIntegerType()) + if (lex->getType()->isIntegralOrUnscopedEnumerationType() && + rex->getType()->isIntegralOrUnscopedEnumerationType()) return compType; return InvalidOperands(Loc, lex, rex); } diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 4618dcf216..e193cafbc2 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -751,7 +751,7 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal, ArraySize = ConvertedSize.take(); SizeType = ArraySize->getType(); - if (!SizeType->isIntegralOrEnumerationType()) + if (!SizeType->isIntegralOrUnscopedEnumerationType()) return ExprError(); // Let's see if this is a constant < 0. If so, we reject it out of hand. diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 92d68bba66..1437238f3b 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -1061,7 +1061,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, // Complex promotion (Clang extension) SCS.Second = ICK_Complex_Promotion; FromType = ToType.getUnqualifiedType(); - } else if (FromType->isIntegralOrEnumerationType() && + } else if (FromType->isIntegralOrUnscopedEnumerationType() && ToType->isIntegralType(S.Context)) { // Integral conversions (C++ 4.7). SCS.Second = ICK_Integral_Conversion; @@ -1081,7 +1081,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, FromType = ToType.getUnqualifiedType(); } else if ((FromType->isRealFloatingType() && ToType->isIntegralType(S.Context) && !ToType->isBooleanType()) || - (FromType->isIntegralOrEnumerationType() && + (FromType->isIntegralOrUnscopedEnumerationType() && ToType->isRealFloatingType())) { // Floating-integral conversions (C++ 4.9). SCS.Second = ICK_Floating_Integral; @@ -1097,7 +1097,6 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, SCS.Second = ICK_Pointer_Member; } else if (ToType->isBooleanType() && (FromType->isArithmeticType() || - FromType->isEnumeralType() || FromType->isAnyPointerType() || FromType->isBlockPointerType() || FromType->isMemberPointerType() || @@ -1194,11 +1193,17 @@ bool Sema::IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType) { // unsigned int, long, or unsigned long (C++ 4.5p2). // We pre-calculate the promotion type for enum types. - if (const EnumType *FromEnumType = FromType->getAs()) + if (const EnumType *FromEnumType = FromType->getAs()) { + // C++0x 7.2p9: Note that this implicit enum to int conversion is not + // provided for a scoped enumeration. + if (FromEnumType->getDecl()->isScoped()) + return false; + if (ToType->isIntegerType() && !RequireCompleteType(From->getLocStart(), FromType, PDiag())) return Context.hasSameUnqualifiedType(ToType, FromEnumType->getDecl()->getPromotionType()); + } if (FromType->isWideCharType() && ToType->isIntegerType()) { // Determine whether the type we're converting from is signed or diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 3537b93e90..cceb9677b9 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -4870,7 +4870,8 @@ Sema::ActOnExplicitInstantiation(Scope *S, Decl *TagD = ActOnTag(S, TagSpec, Sema::TUK_Reference, KWLoc, SS, Name, NameLoc, Attr, AS_none, MultiTemplateParamsArg(*this, 0, 0), - Owned, IsDependent); + Owned, IsDependent, false, + TypeResult()); assert(!IsDependent && "explicit instantiation of dependent name not yet handled"); if (!TagD) diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index d0df7fae75..0b4b5101b6 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -594,7 +594,29 @@ Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { EnumDecl *Enum = EnumDecl::Create(SemaRef.Context, Owner, D->getLocation(), D->getIdentifier(), D->getTagKeywordLoc(), - /*PrevDecl=*/0); + /*PrevDecl=*/0, + D->isScoped(), D->isFixed()); + if (D->isFixed()) { + if (TypeSourceInfo* TI = D->getIntegerTypeSourceInfo()) { + // If we have type source information for the underlying type, it means it + // has been explicitly set by the user. Perform substitution on it before + // moving on. + SourceLocation UnderlyingLoc = TI->getTypeLoc().getBeginLoc(); + Enum->setIntegerTypeSourceInfo(SemaRef.SubstType(TI, + TemplateArgs, + UnderlyingLoc, + DeclarationName())); + + if (!Enum->getIntegerTypeSourceInfo()) + Enum->setIntegerType(SemaRef.Context.IntTy); + } + else { + assert(!D->getIntegerType()->isDependentType() + && "Dependent type without type source info"); + Enum->setIntegerType(D->getIntegerType()); + } + } + Enum->setInstantiationOfMemberEnum(D); Enum->setAccess(D->getAccess()); if (SubstQualifier(D, Enum)) return 0; diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index b86dce06b4..32344559d7 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -677,7 +677,7 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, // C99 6.7.5.2p1: The size expression shall have integer type. if (ArraySize && !ArraySize->isTypeDependent() && - !ArraySize->getType()->isIntegerType()) { + !ArraySize->getType()->isIntegralOrUnscopedEnumerationType()) { Diag(ArraySize->getLocStart(), diag::err_array_size_non_int) << ArraySize->getType() << ArraySize->getSourceRange(); return QualType(); diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index e42a5b4923..97adbf73cc 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -209,10 +209,15 @@ void ASTDeclReader::VisitTagDecl(TagDecl *TD) { void ASTDeclReader::VisitEnumDecl(EnumDecl *ED) { VisitTagDecl(ED); - ED->setIntegerType(Reader.GetType(Record[Idx++])); + if (TypeSourceInfo *TI = Reader.GetTypeSourceInfo(F, Record, Idx)) + ED->setIntegerTypeSourceInfo(TI); + else + ED->setIntegerType(Reader.GetType(Record[Idx++])); ED->setPromotionType(Reader.GetType(Record[Idx++])); ED->setNumPositiveBits(Record[Idx++]); ED->setNumNegativeBits(Record[Idx++]); + ED->IsScoped = Record[Idx++]; + ED->IsFixed = Record[Idx++]; ED->setInstantiationOfMemberEnum( cast_or_null(Reader.GetDecl(Record[Idx++]))); } diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 07520d802b..3e1ba89a66 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -176,10 +176,14 @@ void ASTDeclWriter::VisitTagDecl(TagDecl *D) { void ASTDeclWriter::VisitEnumDecl(EnumDecl *D) { VisitTagDecl(D); - Writer.AddTypeRef(D->getIntegerType(), Record); + Writer.AddTypeSourceInfo(D->getIntegerTypeSourceInfo(), Record); + if (!D->getIntegerTypeSourceInfo()) + Writer.AddTypeRef(D->getIntegerType(), Record); Writer.AddTypeRef(D->getPromotionType(), Record); Record.push_back(D->getNumPositiveBits()); Record.push_back(D->getNumNegativeBits()); + Record.push_back(D->isScoped()); + Record.push_back(D->isFixed()); Writer.AddDeclRef(D->getInstantiatedFromMemberEnum(), Record); Code = serialization::DECL_ENUM; } diff --git a/test/SemaCXX/enum-scoped.cpp b/test/SemaCXX/enum-scoped.cpp new file mode 100644 index 0000000000..7fdcf0d573 --- /dev/null +++ b/test/SemaCXX/enum-scoped.cpp @@ -0,0 +1,98 @@ +// RUN: %clang_cc1 -fsyntax-only -pedantic -std=c++0x -verify -triple x86_64-apple-darwin %s + +enum class E1 { + Val1 = 1L +}; + +enum struct E2 { + Val1 = '\0' +}; + +E1 v1 = Val1; // expected-error{{undeclared identifier}} +E1 v2 = E1::Val1; + +static_assert(sizeof(E1) == sizeof(int), "bad size"); +static_assert(sizeof(E1::Val1) == sizeof(int), "bad size"); +static_assert(sizeof(E2) == sizeof(int), "bad size"); +static_assert(sizeof(E2::Val1) == sizeof(int), "bad size"); + +E1 v3 = E2::Val1; // expected-error{{cannot initialize a variable}} +int x1 = E1::Val1; // expected-error{{cannot initialize a variable}} + +enum E3 : char { + Val2 = 1 +}; + +E3 v4 = Val2; +E1 v5 = Val2; // expected-error{{cannot initialize a variable}} + +static_assert(sizeof(E3) == 1, "bad size"); + +int x2 = Val2; + +int a1[Val2]; +int a2[E1::Val1]; // expected-error{{size of array has non-integer type}} + +int* p1 = new int[Val2]; +int* p2 = new int[E1::Val1]; // FIXME Expected-error{{must have integral}} + +enum class E4 { + e1 = -2147483648, // ok + e2 = 2147483647, // ok + e3 = 2147483648 // expected-error{{value is not representable}} +}; + +enum class E5 { + e1 = 2147483647, // ok + e2 // expected-error{{2147483648 is not representable in the underlying}} +}; + +enum class E6 : bool { + e1 = false, e2 = true, + e3 // expected-error{{2 is not representable in the underlying}} +}; + +enum E7 : bool { + e1 = false, e2 = true, + e3 // expected-error{{2 is not representable in the underlying}} +}; + +template +struct X { + enum E : T { + e1, e2, + e3 // expected-error{{2 is not representable in the underlying}} + }; +}; + +X X2; // expected-note{{in instantiation of template}} + +enum Incomplete1; // expected-error{{C++ forbids forward references}} + +enum Complete1 : int; +Complete1 complete1; + +enum class Complete2; +Complete2 complete2; + +// All the redeclarations below are done twice on purpose. Tests that the type +// of the declaration isn't changed. + +enum class Redeclare2; // expected-note{{previous use is here}} expected-note{{previous use is here}} +enum Redeclare2; // expected-error{{previously declared as scoped}} +enum Redeclare2; // expected-error{{previously declared as scoped}} + +enum Redeclare3 : int; // expected-note{{previous use is here}} expected-note{{previous use is here}} +enum Redeclare3; // expected-error{{previously declared with fixed underlying type}} +enum Redeclare3; // expected-error{{previously declared with fixed underlying type}} + +enum class Redeclare5; +enum class Redeclare5 : int; // ok + +enum Redeclare6 : int; // expected-note{{previous use is here}} expected-note{{previous use is here}} +enum Redeclare6 : short; // expected-error{{redeclared with different underlying type}} +enum Redeclare6 : short; // expected-error{{redeclared with different underlying type}} + +enum class Redeclare7; // expected-note{{previous use is here}} expected-note{{previous use is here}} +enum class Redeclare7 : short; // expected-error{{redeclared with different underlying type}} +enum class Redeclare7 : short; // expected-error{{redeclared with different underlying type}} diff --git a/test/SemaTemplate/enum-forward.cpp b/test/SemaTemplate/enum-forward.cpp new file mode 100644 index 0000000000..3a4f05c0ba --- /dev/null +++ b/test/SemaTemplate/enum-forward.cpp @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -fsyntax-only -fms-extensions %s + +template +struct X { + enum E *e; +}; + +X xi; -- 2.40.0