Rework __builtin_classify_type support to better match GCC and to not assert on

unusual types.

Following the observed behavior of GCC, we now return -1 for vector types
(along with all of our extensions that GCC doesn't support), and for atomic
types we classify the underlying type.

GCC appears to have changed its classification for function and array arguments
between version 5 and version 6. Previously it would classify them as pointers
in C and as functions or arrays in C++, but from version 6 onwards, it
classifies them as pointers. We now follow the more recent GCC behavior rather
than emulating what I can only assume to be a historical bug in their C++
support for this builtin.

Finally, no version of GCC that I can find has ever used the "method"
classification for C++ pointers to member functions. Instead, GCC classifies
them as record types, presumably reflecting an internal implementation detail,
but whatever the reason we now produce compatible results.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@333126 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Richard Smith 2018-05-23 21:18:00 +00:00
parent 54f3f9c02f
commit 02f3a0b412
3 changed files with 138 additions and 90 deletions

View File

@ -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<BuiltinType>(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<RecordType>()) {
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<AtomicType>()->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

View File

@ -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);

View File

@ -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];
}