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

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Wed May 23 14:18:00 PDT 2018


Author: rsmith
Date: Wed May 23 14:18:00 2018
New Revision: 333126

URL: http://llvm.org/viewvc/llvm-project?rev=333126&view=rev
Log:
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.

Modified:
    cfe/trunk/lib/AST/ExprConstant.cpp
    cfe/trunk/test/Sema/builtin-classify-type.c
    cfe/trunk/test/SemaCXX/builtin-classify-type.cpp

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=333126&r1=333125&r2=333126&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Wed May 23 14:18:00 2018
@@ -7277,30 +7277,43 @@ bool IntExprEvaluator::CheckReferencedDe
   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
-  };
-
-  // 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;
+static GCCTypeClass
+EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts) {
+  assert(!T->isDependentType() && "unexpected dependent type");
 
-  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(c
 #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(c
     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::ConstantArray:
+  case Type::VariableArray:
+  case Type::IncompleteArray:
+  case Type::FunctionNoProto:
+  case Type::FunctionProto:
+    return GCCTypeClass::Pointer;
 
   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;
-    }
+    return CanTy->isMemberDataPointerType()
+               ? GCCTypeClass::PointerToDataMember
+               : GCCTypeClass::PointerToMemberFunction;
 
   case Type::Complex:
-    return complex_type_class;
-
-  case Type::FunctionNoProto:
-  case Type::FunctionProto:
-    return LangOpts.CPlusPlus ? function_type_class : pointer_type_class;
+    return GCCTypeClass::Complex;
 
   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");
+    return CanTy->isUnionType() ? GCCTypeClass::Union
+                                : GCCTypeClass::ClassOrStruct;
 
-  case Type::ConstantArray:
-  case Type::VariableArray:
-  case Type::IncompleteArray:
-    return LangOpts.CPlusPlus ? array_type_class : pointer_type_class;
+  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::VisitBuiltinCallE
   }
 
   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

Modified: cfe/trunk/test/Sema/builtin-classify-type.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/builtin-classify-type.c?rev=333126&r1=333125&r2=333126&view=diff
==============================================================================
--- cfe/trunk/test/Sema/builtin-classify-type.c (original)
+++ cfe/trunk/test/Sema/builtin-classify-type.c Wed May 23 14:18:00 2018
@@ -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);

Modified: cfe/trunk/test/SemaCXX/builtin-classify-type.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/builtin-classify-type.cpp?rev=333126&r1=333125&r2=333126&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/builtin-classify-type.cpp (original)
+++ cfe/trunk/test/SemaCXX/builtin-classify-type.cpp Wed May 23 14:18:00 2018
@@ -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];
 }
 




More information about the cfe-commits mailing list