diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 0e1fb98bf7..7140537915 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -7277,30 +7277,43 @@ bool IntExprEvaluator::CheckReferencedDecl(const Expr* E, const Decl* D) { return false; } +/// Values returned by __builtin_classify_type, chosen to match the values +/// produced by GCC's builtin. +enum class GCCTypeClass { + None = -1, + Void = 0, + Integer = 1, + // GCC reserves 2 for character types, but instead classifies them as + // integers. + Enum = 3, + Bool = 4, + Pointer = 5, + // GCC reserves 6 for references, but appears to never use it (because + // expressions never have reference type, presumably). + PointerToDataMember = 7, + RealFloat = 8, + Complex = 9, + // GCC reserves 10 for functions, but does not use it since GCC version 6 due + // to decay to pointer. (Prior to version 6 it was only used in C++ mode). + // GCC claims to reserve 11 for pointers to member functions, but *actually* + // uses 12 for that purpose, same as for a class or struct. Maybe it + // internally implements a pointer to member as a struct? Who knows. + PointerToMemberFunction = 12, // Not a bug, see above. + ClassOrStruct = 12, + Union = 13, + // GCC reserves 14 for arrays, but does not use it since GCC version 6 due to + // decay to pointer. (Prior to version 6 it was only used in C++ mode). + // GCC reserves 15 for strings, but actually uses 5 (pointer) for string + // literals. +}; + /// EvaluateBuiltinClassifyType - Evaluate __builtin_classify_type the same way /// as GCC. -static int EvaluateBuiltinClassifyType(const CallExpr *E, - const LangOptions &LangOpts) { - // The following enum mimics the values returned by GCC. - // FIXME: Does GCC differ between lvalue and rvalue references here? - enum gcc_type_class { - no_type_class = -1, - void_type_class, integer_type_class, char_type_class, - enumeral_type_class, boolean_type_class, - pointer_type_class, reference_type_class, offset_type_class, - real_type_class, complex_type_class, - function_type_class, method_type_class, - record_type_class, union_type_class, - array_type_class, string_type_class, - lang_type_class - }; +static GCCTypeClass +EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts) { + assert(!T->isDependentType() && "unexpected dependent type"); - // If no argument was supplied, default to "no_type_class". This isn't - // ideal, however it is what gcc does. - if (E->getNumArgs() == 0) - return no_type_class; - - QualType CanTy = E->getArg(0)->getType().getCanonicalType(); + QualType CanTy = T.getCanonicalType(); const BuiltinType *BT = dyn_cast(CanTy); switch (CanTy->getTypeClass()) { @@ -7309,37 +7322,41 @@ static int EvaluateBuiltinClassifyType(const CallExpr *E, #define NON_CANONICAL_TYPE(ID, BASE) case Type::ID: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(ID, BASE) case Type::ID: #include "clang/AST/TypeNodes.def" - llvm_unreachable("CallExpr::isBuiltinClassifyType(): unimplemented type"); + case Type::Auto: + case Type::DeducedTemplateSpecialization: + llvm_unreachable("unexpected non-canonical or dependent type"); case Type::Builtin: switch (BT->getKind()) { #define BUILTIN_TYPE(ID, SINGLETON_ID) -#define SIGNED_TYPE(ID, SINGLETON_ID) case BuiltinType::ID: return integer_type_class; -#define FLOATING_TYPE(ID, SINGLETON_ID) case BuiltinType::ID: return real_type_class; -#define PLACEHOLDER_TYPE(ID, SINGLETON_ID) case BuiltinType::ID: break; +#define SIGNED_TYPE(ID, SINGLETON_ID) \ + case BuiltinType::ID: return GCCTypeClass::Integer; +#define FLOATING_TYPE(ID, SINGLETON_ID) \ + case BuiltinType::ID: return GCCTypeClass::RealFloat; +#define PLACEHOLDER_TYPE(ID, SINGLETON_ID) \ + case BuiltinType::ID: break; #include "clang/AST/BuiltinTypes.def" case BuiltinType::Void: - return void_type_class; + return GCCTypeClass::Void; case BuiltinType::Bool: - return boolean_type_class; + return GCCTypeClass::Bool; - case BuiltinType::Char_U: // gcc doesn't appear to use char_type_class + case BuiltinType::Char_U: case BuiltinType::UChar: + case BuiltinType::WChar_U: + case BuiltinType::Char8: + case BuiltinType::Char16: + case BuiltinType::Char32: case BuiltinType::UShort: case BuiltinType::UInt: case BuiltinType::ULong: case BuiltinType::ULongLong: case BuiltinType::UInt128: - return integer_type_class; + return GCCTypeClass::Integer; case BuiltinType::NullPtr: - return pointer_type_class; - case BuiltinType::WChar_U: - case BuiltinType::Char8: - case BuiltinType::Char16: - case BuiltinType::Char32: case BuiltinType::ObjCId: case BuiltinType::ObjCClass: case BuiltinType::ObjCSel: @@ -7351,74 +7368,73 @@ static int EvaluateBuiltinClassifyType(const CallExpr *E, case BuiltinType::OCLClkEvent: case BuiltinType::OCLQueue: case BuiltinType::OCLReserveID: + return GCCTypeClass::None; + case BuiltinType::Dependent: - llvm_unreachable("CallExpr::isBuiltinClassifyType(): unimplemented type"); + llvm_unreachable("unexpected dependent type"); }; - break; + llvm_unreachable("unexpected placeholder type"); case Type::Enum: - return LangOpts.CPlusPlus ? enumeral_type_class : integer_type_class; - break; + return LangOpts.CPlusPlus ? GCCTypeClass::Enum : GCCTypeClass::Integer; case Type::Pointer: - return pointer_type_class; - break; - - case Type::MemberPointer: - if (CanTy->isMemberDataPointerType()) - return offset_type_class; - else { - // We expect member pointers to be either data or function pointers, - // nothing else. - assert(CanTy->isMemberFunctionPointerType()); - return method_type_class; - } - - case Type::Complex: - return complex_type_class; - - case Type::FunctionNoProto: - case Type::FunctionProto: - return LangOpts.CPlusPlus ? function_type_class : pointer_type_class; - - case Type::Record: - if (const RecordType *RT = CanTy->getAs()) { - switch (RT->getDecl()->getTagKind()) { - case TagTypeKind::TTK_Struct: - case TagTypeKind::TTK_Class: - case TagTypeKind::TTK_Interface: - return record_type_class; - - case TagTypeKind::TTK_Enum: - return LangOpts.CPlusPlus ? enumeral_type_class : integer_type_class; - - case TagTypeKind::TTK_Union: - return union_type_class; - } - } - llvm_unreachable("CallExpr::isBuiltinClassifyType(): unimplemented type"); - case Type::ConstantArray: case Type::VariableArray: case Type::IncompleteArray: - return LangOpts.CPlusPlus ? array_type_class : pointer_type_class; + case Type::FunctionNoProto: + case Type::FunctionProto: + return GCCTypeClass::Pointer; + + case Type::MemberPointer: + return CanTy->isMemberDataPointerType() + ? GCCTypeClass::PointerToDataMember + : GCCTypeClass::PointerToMemberFunction; + + case Type::Complex: + return GCCTypeClass::Complex; + + case Type::Record: + return CanTy->isUnionType() ? GCCTypeClass::Union + : GCCTypeClass::ClassOrStruct; + + case Type::Atomic: + // GCC classifies _Atomic T the same as T. + return EvaluateBuiltinClassifyType( + CanTy->castAs()->getValueType(), LangOpts); case Type::BlockPointer: - case Type::LValueReference: - case Type::RValueReference: case Type::Vector: case Type::ExtVector: - case Type::Auto: - case Type::DeducedTemplateSpecialization: case Type::ObjCObject: case Type::ObjCInterface: case Type::ObjCObjectPointer: case Type::Pipe: - case Type::Atomic: - llvm_unreachable("CallExpr::isBuiltinClassifyType(): unimplemented type"); + // GCC classifies vectors as None. We follow its lead and classify all + // other types that don't fit into the regular classification the same way. + return GCCTypeClass::None; + + case Type::LValueReference: + case Type::RValueReference: + llvm_unreachable("invalid type for expression"); } - llvm_unreachable("CallExpr::isBuiltinClassifyType(): unimplemented type"); + llvm_unreachable("unexpected type class"); +} + +/// EvaluateBuiltinClassifyType - Evaluate __builtin_classify_type the same way +/// as GCC. +static GCCTypeClass +EvaluateBuiltinClassifyType(const CallExpr *E, const LangOptions &LangOpts) { + // If no argument was supplied, default to None. This isn't + // ideal, however it is what gcc does. + if (E->getNumArgs() == 0) + return GCCTypeClass::None; + + // FIXME: Bizarrely, GCC treats a call with more than one argument as not + // being an ICE, but still folds it to a constant using the type of the first + // argument. + return EvaluateBuiltinClassifyType(E->getArg(0)->getType(), LangOpts); } /// EvaluateBuiltinConstantPForLValue - Determine the result of @@ -7842,7 +7858,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, } case Builtin::BI__builtin_classify_type: - return Success(EvaluateBuiltinClassifyType(E, Info.getLangOpts()), E); + return Success((int)EvaluateBuiltinClassifyType(E, Info.getLangOpts()), E); // FIXME: BI__builtin_clrsb // FIXME: BI__builtin_clrsbl diff --git a/test/Sema/builtin-classify-type.c b/test/Sema/builtin-classify-type.c index 376e73d111..814c1aeec6 100644 --- a/test/Sema/builtin-classify-type.c +++ b/test/Sema/builtin-classify-type.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify %s -fblocks // expected-no-diagnostics @@ -25,6 +25,14 @@ void foo() { struct { int a; float b; } s_obj; union { int a; float b; } u_obj; int arr[10]; + int (^block)(); + __attribute__((vector_size(16))) int vec; + typedef __attribute__((ext_vector_type(4))) int evec_t; + evec_t evec; + _Atomic int atomic_i; + _Atomic double atomic_d; + _Complex int complex_i; + _Complex double complex_d; int a1[__builtin_classify_type(f()) == void_type_class ? 1 : -1]; int a2[__builtin_classify_type(i) == integer_type_class ? 1 : -1]; @@ -38,5 +46,14 @@ void foo() { int a10[__builtin_classify_type(u_obj) == union_type_class ? 1 : -1]; int a11[__builtin_classify_type(arr) == pointer_type_class ? 1 : -1]; int a12[__builtin_classify_type("abc") == pointer_type_class ? 1 : -1]; + int a13[__builtin_classify_type(block) == no_type_class ? 1 : -1]; + int a14[__builtin_classify_type(vec) == no_type_class ? 1 : -1]; + int a15[__builtin_classify_type(evec) == no_type_class ? 1 : -1]; + int a16[__builtin_classify_type(atomic_i) == integer_type_class ? 1 : -1]; + int a17[__builtin_classify_type(atomic_d) == real_type_class ? 1 : -1]; + int a18[__builtin_classify_type(complex_i) == complex_type_class ? 1 : -1]; + int a19[__builtin_classify_type(complex_d) == complex_type_class ? 1 : -1]; } +extern int (^p)(); +int n = __builtin_classify_type(p); diff --git a/test/SemaCXX/builtin-classify-type.cpp b/test/SemaCXX/builtin-classify-type.cpp index f700c1d5ba..ebc81425e4 100644 --- a/test/SemaCXX/builtin-classify-type.cpp +++ b/test/SemaCXX/builtin-classify-type.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify -fblocks %s // expected-no-diagnostics @@ -34,6 +34,14 @@ void foo() { cl cl_obj; union { int a; float b; } u_obj; int arr[10]; + int (^block)(); + __attribute__((vector_size(16))) int vec; + typedef __attribute__((ext_vector_type(4))) int evec_t; + evec_t evec; + _Atomic int atomic_i; + _Atomic double atomic_d; + _Complex int complex_i; + _Complex double complex_d; int a1[__builtin_classify_type(f()) == void_type_class ? 1 : -1]; int a2[__builtin_classify_type(i) == integer_type_class ? 1 : -1]; @@ -44,11 +52,18 @@ void foo() { int a7[__builtin_classify_type(r) == integer_type_class ? 1 : -1]; int a8[__builtin_classify_type(&cl::baz) == offset_type_class ? 1 : -1]; int a9[__builtin_classify_type(d) == real_type_class ? 1 : -1]; - int a10[__builtin_classify_type(f) == function_type_class ? 1 : -1]; - int a11[__builtin_classify_type(&cl::bar) == method_type_class ? 1 : -1]; + int a10[__builtin_classify_type(f) == pointer_type_class ? 1 : -1]; + int a11[__builtin_classify_type(&cl::bar) == record_type_class ? 1 : -1]; int a12[__builtin_classify_type(cl_obj) == record_type_class ? 1 : -1]; int a13[__builtin_classify_type(u_obj) == union_type_class ? 1 : -1]; - int a14[__builtin_classify_type(arr) == array_type_class ? 1 : -1]; - int a15[__builtin_classify_type("abc") == array_type_class ? 1 : -1]; + int a14[__builtin_classify_type(arr) == pointer_type_class ? 1 : -1]; + int a15[__builtin_classify_type("abc") == pointer_type_class ? 1 : -1]; + int a16[__builtin_classify_type(block) == no_type_class ? 1 : -1]; + int a17[__builtin_classify_type(vec) == no_type_class ? 1 : -1]; + int a18[__builtin_classify_type(evec) == no_type_class ? 1 : -1]; + int a19[__builtin_classify_type(atomic_i) == integer_type_class ? 1 : -1]; + int a20[__builtin_classify_type(atomic_d) == real_type_class ? 1 : -1]; + int a21[__builtin_classify_type(complex_i) == complex_type_class ? 1 : -1]; + int a22[__builtin_classify_type(complex_d) == complex_type_class ? 1 : -1]; }