rsmith created this revision.
rsmith added reviewers: aeubanks, rnk.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

We try to avoid materializing a full _GUID APValue wherever possible;
instead, when accessing an individual field, we only extract the
corresponding portion of the UUID string and convert that.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D77962

Files:
  clang/include/clang/Basic/DiagnosticASTKinds.td
  clang/lib/AST/ExprConstant.cpp
  clang/test/CodeGenCXX/microsoft-uuidof.cpp
  clang/test/Parser/MicrosoftExtensions.cpp
  clang/test/SemaCXX/ms-uuid.cpp

Index: clang/test/SemaCXX/ms-uuid.cpp
===================================================================
--- clang/test/SemaCXX/ms-uuid.cpp
+++ clang/test/SemaCXX/ms-uuid.cpp
@@ -2,10 +2,10 @@
 // RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify -fms-extensions %s -Wno-deprecated-declarations
 
 typedef struct _GUID {
-  unsigned long Data1;
-  unsigned short Data2;
-  unsigned short Data3;
-  unsigned char Data4[8];
+  __UINT32_TYPE__ Data1;
+  __UINT16_TYPE__ Data2;
+  __UINT16_TYPE__ Data3;
+  __UINT8_TYPE__ Data4[8];
 } GUID;
 
 namespace {
@@ -111,4 +111,24 @@
 // declaration has a uuid attribute
 struct X{};
 
-struct __declspec(uuid("00000000-0000-0000-0000-000000000000")) X;
\ No newline at end of file
+struct __declspec(uuid("00000000-0000-0000-0000-000000000000")) X;
+
+namespace ConstantEvaluation {
+  class __declspec(uuid("1babb1ed-feed-c01d-1ced-decafc0ffee5")) Request;
+  constexpr GUID a = __uuidof(Request);
+  static_assert(a.Data1 == 0x1babb1ed, "");
+  static_assert(__uuidof(Request).Data1 == 0x1babb1ed, "");
+  static_assert(a.Data2 == 0xfeed, "");
+  static_assert(__uuidof(Request).Data2 == 0xfeed, "");
+  static_assert(a.Data3 == 0xc01d, "");
+  static_assert(__uuidof(Request).Data3 == 0xc01d, "");
+  static_assert(a.Data4[0] == 0x1c, "");
+  static_assert(__uuidof(Request).Data4[0] == 0x1c, "");
+  static_assert(a.Data4[1] == 0xed, "");
+  static_assert(__uuidof(Request).Data4[1] == 0xed, "");
+  static_assert(a.Data4[2] == 0xde, "");
+  static_assert(__uuidof(Request).Data4[2] == 0xde, "");
+  static_assert(a.Data4[7] == 0xe5, "");
+  static_assert(__uuidof(Request).Data4[7] == 0xe5, "");
+  constexpr int k = __uuidof(Request).Data4[8]; // expected-error {{constant expression}} expected-note {{past-the-end}}
+}
Index: clang/test/Parser/MicrosoftExtensions.cpp
===================================================================
--- clang/test/Parser/MicrosoftExtensions.cpp
+++ clang/test/Parser/MicrosoftExtensions.cpp
@@ -137,9 +137,7 @@
 
 COM_CLASS_TEMPLATE_REF<int, __uuidof(struct_with_uuid)> good_template_arg;
 
-COM_CLASS_TEMPLATE<int, __uuidof(struct_with_uuid)> bad_template_arg; // expected-error {{non-type template argument of type 'const _GUID' is not a constant expression}}
-// expected-note@-1 {{read of object '__uuidof(struct_with_uuid)' whose value is not known}}
-// expected-note@-2 {{temporary created here}}
+COM_CLASS_TEMPLATE<int, __uuidof(struct_with_uuid)> bad_template_arg; // expected-error {{non-type template argument of type 'const _GUID' cannot be converted to a value of type 'const GUID *'}}
 
 namespace PR16911 {
 struct __declspec(uuid("{12345678-1234-1234-1234-1234567890aB}")) uuid;
Index: clang/test/CodeGenCXX/microsoft-uuidof.cpp
===================================================================
--- clang/test/CodeGenCXX/microsoft-uuidof.cpp
+++ clang/test/CodeGenCXX/microsoft-uuidof.cpp
@@ -30,16 +30,22 @@
 struct __declspec(uuid("{12345678-1234-1234-1234-1234567890ac}")) Curly;
 #endif
 
+void side_effect();
+
 #ifdef DEFINE_GUID
 // Make sure we can properly generate code when the UUID has curly braces on it.
-GUID thing = __uuidof(Curly);
+GUID thing = (side_effect(), __uuidof(Curly));
 // CHECK-DEFINE-GUID: @thing = global %struct._GUID zeroinitializer, align 4
 // CHECK-DEFINE-WRONG-GUID: @thing = global %struct._GUID zeroinitializer, align 4
 
 // This gets initialized in a static initializer.
 // CHECK-DEFINE-GUID: @g = global %struct._GUID zeroinitializer, align 4
 // CHECK-DEFINE-WRONG-GUID: @g = global %struct._GUID zeroinitializer, align 4
-GUID g = __uuidof(S1);
+GUID g = (side_effect(), __uuidof(S1));
+
+// CHECK-DEFINE-GUID: @const_init = global %struct._GUID { i32 305419896, i16 4660, i16 4660, [8 x i8] c"\124\124Vx\90\AC" }
+// CHECK-DEFINE-WRONG-GUID: @const_init = global %struct._GUID { i32 305419896 }
+GUID const_init = __uuidof(Curly);
 #endif
 
 // First global use of __uuidof(S1) forces the creation of the global.
@@ -83,19 +89,19 @@
   // CHECK-DEFINE-WRONG-GUID: [[U1:%.+]] = bitcast %struct._GUID* %s1_1 to i8*
   // CHECK-DEFINE-GUID: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 [[U1]], i8* align 4 bitcast ({ i32, i16, i16, [8 x i8] }* @_GUID_12345678_1234_1234_1234_1234567890ab to i8*), i32 16, i1 false)
   // CHECK-DEFINE-WRONG-GUID: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 [[U1]], i8* align 4 bitcast ({ i32, i16, i16, [8 x i8] }* @_GUID_12345678_1234_1234_1234_1234567890ab to i8*), i32 4, i1 false)
-  GUID s1_1 = __uuidof(S1);
+  GUID s1_1 = (side_effect(), __uuidof(S1));
 
   // CHECK-DEFINE-GUID: [[U2:%.+]] = bitcast %struct._GUID* %s1_2 to i8*
   // CHECK-DEFINE-WRONG-GUID: [[U2:%.+]] = bitcast %struct._GUID* %s1_2 to i8*
   // CHECK-DEFINE-GUID: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 [[U2]], i8* align 4 bitcast ({ i32, i16, i16, [8 x i8] }* @_GUID_12345678_1234_1234_1234_1234567890ab to i8*), i32 16, i1 false)
   // CHECK-DEFINE-WRONG-GUID: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 [[U2]], i8* align 4 bitcast ({ i32, i16, i16, [8 x i8] }* @_GUID_12345678_1234_1234_1234_1234567890ab to i8*), i32 4, i1 false)
-  GUID s1_2 = __uuidof(S1);
+  GUID s1_2 = (side_effect(), __uuidof(S1));
 
   // CHECK-DEFINE-GUID: [[U3:%.+]] = bitcast %struct._GUID* %s1_3 to i8*
   // CHECK-DEFINE-WRONG-GUID: [[U3:%.+]] = bitcast %struct._GUID* %s1_3 to i8*
   // CHECK-DEFINE-GUID: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 [[U3]], i8* align 4 bitcast ({ i32, i16, i16, [8 x i8] }* @_GUID_12345678_1234_1234_1234_1234567890ab to i8*), i32 16, i1 false)
   // CHECK-DEFINE-WRONG-GUID: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 [[U3]], i8* align 4 bitcast ({ i32, i16, i16, [8 x i8] }* @_GUID_12345678_1234_1234_1234_1234567890ab to i8*), i32 4, i1 false)
-  GUID s1_3 = __uuidof(s1);
+  GUID s1_3 = (side_effect(), __uuidof(s1));
 }
 #endif
 
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -3047,6 +3047,113 @@
   Array.swap(NewValue);
 }
 
+/// Extract a field from an object represented by the given UUID string \p Uuid.
+///
+/// \p Uuid is a string of the form 12345678-1234-1234-1234-1234567890ab, split
+/// into fields as a uint32, then two uint16's, then 8 uint8's (with a hyphen
+/// between the first two and the last six). The expected memory layout for
+/// the resulting object is the same (with the fields in target endianness).
+///
+/// We don't require any particular exact struct definition, but we do require
+/// that only the expected fields are accessed.
+static bool extractUuidField(EvalInfo &Info, const Expr *E, StringRef Uuid,
+                             QualType Type, CharUnits Offset, APValue &Result) {
+  auto TryExtractInt = [&](unsigned Start, unsigned Nibbles) {
+    const unsigned Bits = Nibbles * 4;
+
+    APInt Value(Bits, 0);
+    // FIXME: If the designator denotes a bit-field, this will compute the
+    // wrong width.
+    if (Info.Ctx.getIntWidth(Type) != Bits ||
+        Uuid.substr(Start, Nibbles).getAsInteger(16, Value) ||
+        Value.getActiveBits() > Bits)
+      return false;
+    Result = APValue(APSInt(Value.zextOrTrunc(Bits),
+                            Type->isUnsignedIntegerOrEnumerationType()));
+    return true;
+  };
+
+  // We might be asked to extract a particular field. Do so directly.
+  // FIXME: CodeGen supports other layouts for struct _GUID. We could support
+  // fairly arbitrary integer extraction here if we ever needed to (but beware
+  // of hyphens and endianness).
+  auto TryKnownFields = [&] {
+    unsigned Off = Offset.getQuantity();
+    switch (Off) {
+    case 0: return TryExtractInt(0, 8);
+    case 4: return TryExtractInt(9, 4);
+    case 6: return TryExtractInt(14, 4);
+    case 8: return TryExtractInt(19, 2);
+    case 9: return TryExtractInt(21, 2);
+    default:
+      if (Off >= 10 && Off < 16)
+        return TryExtractInt(4 + Off * 2, 2);
+      return false;
+    }
+  };
+
+  if (Type->isIntegralOrEnumerationType() && TryKnownFields())
+    return true;
+
+  // We might be asked to extract the entire _GUID struct, for example when
+  // invoking its trivial copy constructor. Extract all the fields and form
+  // a struct constant.
+  auto TryRecord = [&] {
+    const RecordDecl *RD = Type->getAsRecordDecl();
+    if (RD->isUnion())
+      return false;
+    if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
+      if (CXXRD->getNumBases())
+        return false;
+
+    Result = APValue(APValue::UninitStruct(), 0,
+                     std::distance(RD->field_begin(), RD->field_end()));
+    const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
+    for (const FieldDecl *FD : RD->fields()) {
+      if (FD->isUnnamedBitfield())
+        continue;
+      else if (FD->isBitField())
+        return false;
+      unsigned I = FD->getFieldIndex();
+      CharUnits FieldOffset =
+          Info.Ctx.toCharUnitsFromBits(Layout.getFieldOffset(I));
+      if (!extractUuidField(Info, E, Uuid, FD->getType(), Offset + FieldOffset,
+                            Result.getStructField(I)))
+        return false;
+    }
+    return true;
+  };
+
+  if (Type->isStructureOrClassType() && TryRecord())
+    return true;
+
+  // We might be asked to extract the final field as an 'unsigned char[8]'
+  // array, as part of extracting the entire struct. Extract all the elements
+  // and form an array constant.
+  auto TryArray = [&] {
+    const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(Type);
+    if (!CAT)
+      return false;
+    uint64_t Elts = CAT->getSize().getZExtValue();
+    Result = APValue(APValue::UninitArray(), Elts, Elts);
+    CharUnits EltSize = Info.Ctx.getTypeSizeInChars(CAT->getElementType());
+    for (uint64_t I = 0; I != Elts; ++I) {
+      if (!extractUuidField(Info, E, Uuid, CAT->getElementType(),
+                            Offset + I * EltSize,
+                            Result.getArrayInitializedElt(I)))
+        return false;
+    }
+    return true;
+  };
+
+  if (Type->isConstantArrayType() && TryArray())
+    return true;
+
+  // This isn't something we know how to extract.
+  Info.FFDiag(E, diag::note_constexpr_unsupported_layout) << E->getType();
+  return false;
+}
+
 /// Determine whether a type would actually be read by an lvalue-to-rvalue
 /// conversion. If it's of class type, we may assume that the copy operation
 /// is trivial. Note that this is never true for a union type with fields
@@ -3837,6 +3944,18 @@
       uint64_t CharIndex = LVal.Designator.Entries[0].getAsArrayIndex();
       RVal = APValue(extractStringLiteralCharacter(Info, Base, CharIndex));
       return true;
+    } else if (auto *UuidOf = dyn_cast<CXXUuidofExpr>(Base)) {
+      // Special-case reading from __uuidof expressions so we don't have to
+      // construct an APValue for the whole uuid.
+      if (LVal.Designator.isOnePastTheEnd()) {
+        if (Info.getLangOpts().CPlusPlus11)
+          Info.FFDiag(Conv, diag::note_constexpr_access_past_end) << AK;
+        else
+          Info.FFDiag(Conv);
+        return false;
+      }
+      return extractUuidField(Info, UuidOf, UuidOf->getUuidStr(), Type,
+                              LVal.Offset, RVal);
     }
   }
 
Index: clang/include/clang/Basic/DiagnosticASTKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticASTKinds.td
+++ clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -338,6 +338,8 @@
 def note_constexpr_memory_leak : Note<
   "allocation performed here was not deallocated"
   "%plural{0:|: (along with %0 other memory leak%s0)}0">;
+def note_constexpr_unsupported_layout : Note<
+  "type %0 has unexpected layout">;
 def err_experimental_clang_interp_failed : Error<
   "the experimental clang interpreter failed to evaluate an expression">;
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D77962: P... Richard Smith - zygoloid via Phabricator via cfe-commits

Reply via email to