diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/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">; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/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(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(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); } } diff --git a/clang/test/CodeGenCXX/microsoft-uuidof.cpp b/clang/test/CodeGenCXX/microsoft-uuidof.cpp --- a/clang/test/CodeGenCXX/microsoft-uuidof.cpp +++ b/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 diff --git a/clang/test/Parser/MicrosoftExtensions.cpp b/clang/test/Parser/MicrosoftExtensions.cpp --- a/clang/test/Parser/MicrosoftExtensions.cpp +++ b/clang/test/Parser/MicrosoftExtensions.cpp @@ -137,9 +137,7 @@ COM_CLASS_TEMPLATE_REF good_template_arg; -COM_CLASS_TEMPLATE 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 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; diff --git a/clang/test/SemaCXX/ms-uuid.cpp b/clang/test/SemaCXX/ms-uuid.cpp --- a/clang/test/SemaCXX/ms-uuid.cpp +++ b/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}} +}