From 12053f365e12bc5556263557599ace16bb61b67a Mon Sep 17 00:00:00 2001 From: Cameron DaCamara Date: Sat, 4 Oct 2025 17:59:39 -0700 Subject: [PATCH] Sync MSVC-specific changes to SDK This contains changes which correspond to: * https://github.com/microsoft/ifc-spec/issues/146 --- include/ifc/abstract-sgraph.hxx | 12 +++- include/ifc/operators.hxx | 2 + include/ifc/source-word.hxx | 1 + include/ifc/version.hxx | 10 +++- samples/sgraph-js/sgraph/decls.js | 18 +++++- samples/sgraph-js/sgraph/resolve.js | 29 ++++++++++ samples/sgraph-js/sgraph/sgraph.js | 84 ++++++++++++++++++++++++++-- samples/sgraph-js/sgraph/util.js | 30 ++++++++++ samples/sgraph-js/ui/ifc-explorer.js | 19 ++++++- samples/sgraph-js/ui/options.js | 2 +- src/sgraph.cxx | 3 +- 11 files changed, 196 insertions(+), 14 deletions(-) diff --git a/include/ifc/abstract-sgraph.hxx b/include/ifc/abstract-sgraph.hxx index f2ebf94..4e3fb34 100644 --- a/include/ifc/abstract-sgraph.hxx +++ b/include/ifc/abstract-sgraph.hxx @@ -316,7 +316,7 @@ namespace ifc { Destructor, // A destructor declaration. Reference, // A reference to a declaration from a given module. Using, // A using declaration. FIXME: See bug https://github.com/microsoft/ifc-spec/issues/122 - UnusedSort0, // Empty slot + Prolongation, // An out-of-home-scope definition for an entity previously declared in its home scope. Friend, // A friend declaration. FIXME: See bug https://github.com/microsoft/ifc-spec/issues/123 Expansion, // A pack-expansion of a declaration DeductionGuide, // C(T) -> C @@ -2448,6 +2448,13 @@ namespace ifc { bool is_hidden{}; // Is this using-declaration hidden? }; + struct ProlongationDecl : Tag { + Identity identity{}; + DeclIndex enclosing_scope{}; // Enclosing scope of this declaration. + DeclIndex home_scope{}; // The scope to which this decl belongs. + DeclIndex original_decl{}; // The original decl in home_scope. + }; + struct FriendDecl : Tag { ExprIndex index{}; // The expression representing a reference to the declaration. // Note: most of the time this is a NamedDeclExpression but it @@ -3189,6 +3196,7 @@ namespace ifc { Attributes, // attributes associated with a declaration Deprecated, // deprecation message. DeductionGuides, // Declared deduction guides associated with a class + Prolongations, // Prolongation declarations associated with a scope Count, }; @@ -3279,6 +3287,7 @@ namespace ifc { struct Attributes : AssociatedTrait, TraitTag {}; struct Deprecated : AssociatedTrait, TraitTag {}; struct DeductionGuides : AssociatedTrait, TraitTag {}; + struct Prolongations : AssociatedTrait>, TraitTag {}; } // namespace symbolic::trait // Msvc specific traits. Should they be in their own namespace, like symbolic::MsvcTrait? @@ -3431,6 +3440,7 @@ namespace ifc { // semantics of the current TU. PartitionSummaryData debug_records; // Implementation-specific data for debugging. PartitionSummaryData gmf_specializations; // Decl-reachable template specializations in the global module fragment. + PartitionSummaryData prolongations; // An association between scopes and prolongation declarations. // Facilities for iterating over the ToC as a sequence of partition summaries. // Note: the use of reinterpret_cast below is avoidable. One way uses diff --git a/include/ifc/operators.hxx b/include/ifc/operators.hxx index a0be473..58754b5 100644 --- a/include/ifc/operators.hxx +++ b/include/ifc/operators.hxx @@ -145,6 +145,8 @@ namespace ifc { MsvcConfusedTemporaryArray, // The backing temporary array for a std::initializer_list MsvcConfusedDependentNestedDecl, // The parser saw a nested-name as part of a declaration of a dependent expression (e.g. 'friend void X::mf()') and 'mf' is the // named declaration here. + MsvcConfusedTemplateAliasSubstitution, // Represents a non-type template argument substitution inside of a template alias specialization. This is unique from 'MsvcConfusedSubstitution' + // above due to C++ semantics requiring this type of substitution to be transparent. Last }; diff --git a/include/ifc/source-word.hxx b/include/ifc/source-word.hxx index 23c747a..29f4e78 100644 --- a/include/ifc/source-word.hxx +++ b/include/ifc/source-word.hxx @@ -129,6 +129,7 @@ namespace ifc { MsvcCastTargetType, // Binding of a resolve type in a cast expression. FIXME: to be removed along with // other YACC oddities. MsvcTemplateId, // A reference to a known template template specialization. + MsvcResolvedExpression,// Binding of a resolved expression }; enum class Operator : uint16_t { diff --git a/include/ifc/version.hxx b/include/ifc/version.hxx index c7a08dc..d8a7e81 100644 --- a/include/ifc/version.hxx +++ b/include/ifc/version.hxx @@ -5,7 +5,6 @@ #define IFC_VERSION_INCLUDED #include - #include namespace ifc { @@ -18,11 +17,16 @@ namespace ifc { auto operator<=>(const FormatVersion&) const = default; }; + // Minimum and current version components. + inline constexpr Version MajorVersion { 0 }; + inline constexpr Version MinimumMinorVersion { 43 }; + inline constexpr Version CurrentMinorVersion { 44 }; + // Minimum supported file format version - inline constexpr FormatVersion MinimumFormatVersion{Version{0}, Version{43}}; + inline constexpr FormatVersion MinimumFormatVersion{MajorVersion, MinimumMinorVersion}; // The current version of file format emitted by the toolset - inline constexpr FormatVersion CurrentFormatVersion = MinimumFormatVersion; + inline constexpr FormatVersion CurrentFormatVersion{MajorVersion, CurrentMinorVersion}; } // namespace ifc #endif // IFC_VERSION_INCLUDED diff --git a/samples/sgraph-js/sgraph/decls.js b/samples/sgraph-js/sgraph/decls.js index 6fb5270..f19235f 100644 --- a/samples/sgraph-js/sgraph/decls.js +++ b/samples/sgraph-js/sgraph/decls.js @@ -196,7 +196,7 @@ class PartialSpecializationDecl { this.home_scope = new DeclIndex(reader); this.chart = new ChartIndex(reader); this.entity = new ParameterizedEntity(reader); - this.specialization_info = reader.read_uint32(); + this.specialization_form = new SpecFormIndex(reader); this.basic_spec = new BasicSpecifiers(reader); this.access = new Access(reader); this.properties = new ReachableProperties(reader); @@ -219,7 +219,7 @@ class SpecializationDecl { static partition_name = "decl.specialization"; constructor(reader) { - this.specialization_info = reader.read_uint32(); + this.specialization_form = new SpecFormIndex(reader); this.decl = new DeclIndex(reader); this.sort = new SpecializationSort(reader); this.pad1 = new StructPadding(reader); @@ -365,6 +365,17 @@ class UsingDecl { } } +class ProlongationDecl { + static partition_name = "decl.prolongation"; + + constructor(reader) { + this.identity = new IdentityNameIndex(reader); + this.enclosing_scope = new DeclIndex(reader); + this.home_scope = new DeclIndex(reader); + this.original_decl = new DeclIndex(reader); + } +} + class FriendDecl { static partition_name = "decl.friend"; @@ -515,6 +526,8 @@ function symbolic_for_decl_sort(sort) { return ReferenceDecl; case DeclIndex.Sort.Using: return UsingDecl; + case DeclIndex.Sort.Prolongation: + return ProlongationDecl; case DeclIndex.Sort.Friend: return FriendDecl; case DeclIndex.Sort.Expansion: @@ -535,7 +548,6 @@ function symbolic_for_decl_sort(sort) { return SegmentDecl; case DeclIndex.Sort.VendorExtension: return VendorDecl; - case DeclIndex.Sort.UnusedSort0: default: console.error(`Bad sort: ${sort}`); return null; diff --git a/samples/sgraph-js/sgraph/resolve.js b/samples/sgraph-js/sgraph/resolve.js index 3804584..f4a77f5 100644 --- a/samples/sgraph-js/sgraph/resolve.js +++ b/samples/sgraph-js/sgraph/resolve.js @@ -6,6 +6,7 @@ class Resolver { this.reader = reader; this.toc = toc; this.string_table = string_table; + this.msvc_traits = this.associated_trait_table(AssociatedMsvcTraits); } decl_index_in_bounds(index) { @@ -166,6 +167,34 @@ class Resolver { } return elms; } + + resolve_spec_form(form) { + const partition = this.toc.partition_by_name(form.T.partition_name); + return this.read(form.T, form.value); + } + + associated_trait_table(T) { + const partition = this.toc.partition_by_name(T.partition_name); + var elms = new Array(); + if (partition == undefined) + return elms; + var offset = partition.tell(0); + for (var i = 0; i < partition.cardinality; ++i) { + var k = this.offset_read(T.K, offset); + var v = new T.V(this.reader); + elms.push({ key: k, value: v }); + offset += partition.entry_size; + } + return elms; + } + + associated_trait_index(key, table) { + var idx = lower_bound(table, e => [...Object.values(key)] <= [...Object.values(e.key)]); + if (idx < table.length && [...Object.values(key)].equal([...Object.values(table[idx].key)])) { + return table[idx].value; + } + return undefined; + } } class ResolvedLocus { diff --git a/samples/sgraph-js/sgraph/sgraph.js b/samples/sgraph-js/sgraph/sgraph.js index 8d28a93..0d0c1d1 100644 --- a/samples/sgraph-js/sgraph/sgraph.js +++ b/samples/sgraph-js/sgraph/sgraph.js @@ -1,9 +1,9 @@ // Copyright Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// Based on IFC specification 0.43. +// Based on IFC specification 0.44. function implemented_ifc_version() { - return new Version(0, 43); + return new Version(0, 44); } function ifc_version_compatible(version) { @@ -69,7 +69,7 @@ class DeclIndex { Destructor: 19, // A destructor declaration. Reference: 20, // A reference to a declaration from a given module. Using: 21, // A using declaration. FIXME: See bug https://github.com/microsoft/ifc-spec/issues/122 - UnusedSort0: 22, // Empty slot + Prolongation: 22, // An out-of-home-scope definition for an entity previously declared in its home scope. Friend: 23, // A friend declaration. FIXME: See bug https://github.com/microsoft/ifc-spec/issues/123 Expansion: 24, // A pack-expansion of a declaration DeductionGuide: 25, // C(T) -> C @@ -859,4 +859,80 @@ class StructPadding { constructor(reader) { this.value = reader.read_uint8(); } -} \ No newline at end of file +} + +// Symbolic representation of a specialization request, whether implicit or explicit. +class SpecializationForm { + static partition_name = "form.spec"; + + constructor(reader) { + this.template_decl = new DeclIndex(reader); + this.arguments = new ExprIndex(reader); + } +} + +class SpecFormIndex { + constructor(reader) { + this.T = SpecializationForm; + this.value = reader.read_uint32(); + } +} + +// MSVC-specific vendor traits. +class MsvcVendorTraits { + static partition_name = ".msvc.trait.vendor-traits"; + + static Values = { + None: 0, + ForceInline: 1 << 0, // __forceinline function + Naked: 1 << 1, // __declspec(naked) + NoAlias: 1 << 2, // __declspec(noalias) + NoInline: 1 << 3, // __declspec(noinline) + Restrict: 1 << 4, // __declspec(restrict) + SafeBuffers: 1 << 5, // __declspec(safebuffers) + DllExport: 1 << 6, // __declspec(dllexport) + DllImport: 1 << 7, // __declspec(dllimport) + CodeSegment: 1 << 8, // __declspec(code_seg("segment")) + NoVtable: 1 << 9, // __declspec(novtable) for a class type. + IntrinsicType: 1 << 10, // __declspec(intrin_type) + EmptyBases: 1 << 11, // __declspec(empty_bases) + Process: 1 << 12, // __declspec(process) + Allocate: 1 << 13, // __declspec(allocate("segment")) + SelectAny: 1 << 14, // __declspec(selectany) + Comdat: 1 << 15, + Uuid: 1 << 16, // __declspec(uuid(....)) + NoCtorDisplacement: 1 << 17, // #pragma vtordisp(0) + DefaultCtorDisplacement: 1 << 18, // #pragma vtordisp(1) + FullCtorDisplacement: 1 << 19, // #pragma vtordisp(2) + NoSanitizeAddress: 1 << 20, // __declspec(no_sanitize_address) + NoUniqueAddress: 1 << 21, // '[[msvc::no_unique_address]]' + NoInitAll: 1 << 22, // __declspec(no_init_all) + DynamicInitialization: 1 << 23, // Indicates that this entity is used for implementing + // aspects of dynamic initialization. + LexicalScopeIndex: 1 << 24, // Indicates this entity has a local lexical scope index associated with it. + ResumableFunction: 1 << 25, // Indicates this function was a transformed coroutine function. + PersistentTemporary: 1 << 26, // a c1xx-ism which will create long-lived temporary symbols when expressions + // need their result to live beyond the full expression, e.g. lifetime + // extended temporaries. + IneligibleForNRVO: 1 << 27, // a c1xx-ism in which the front-end conveys to the back-end that a particular + // function cannot utilize NRVO on this function. This is important due to the + // MSVC C++ calling convention which passes UDTs on the stack as a hidden parameter + // to functions returning that type. + MultiBytePTMRep: 1 << 28 // a c1xx-ism which indicates that the pointer-to-member representation for a class + // type is a generalized multi-byte representation for ABI purposes. + }; + + constructor(reader) { + this.value = reader.read_uint32(); + } +} + +class AssociatedTrait { + constructor(part, K, V) { + this.partition_name = part; + this.K = K; + this.V = V; + } +} + +const AssociatedMsvcTraits = new AssociatedTrait(MsvcVendorTraits.partition_name, DeclIndex, MsvcVendorTraits); diff --git a/samples/sgraph-js/sgraph/util.js b/samples/sgraph-js/sgraph/util.js index 9e30f13..41dbe92 100644 --- a/samples/sgraph-js/sgraph/util.js +++ b/samples/sgraph-js/sgraph/util.js @@ -40,4 +40,34 @@ function debounce(func, timeout = 300) { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, timeout); }; +} + +function binary_search(array, pred) { + let low = -1; + let high = array.length; + while (low + 1 < high) { + const mid = low + ((high - low) >> 1); + if (pred(array[mid])) { + high = mid; + } else { + low = mid; + } + } + return high; +} + +// 'pred' should model "val <= x". +function lower_bound(array, pred) { + return binary_search(array, pred); +} + +// Useful array prototypes. +Array.prototype.equal = function(other) { + if (this.length != other.length) + return false; + for (var i = 0; i < this.length; ++i) { + if (this[i] != other[i]) + return false; + } + return true; } \ No newline at end of file diff --git a/samples/sgraph-js/ui/ifc-explorer.js b/samples/sgraph-js/ui/ifc-explorer.js index 0c7b932..5cc811a 100644 --- a/samples/sgraph-js/ui/ifc-explorer.js +++ b/samples/sgraph-js/ui/ifc-explorer.js @@ -138,6 +138,13 @@ class IFCExplorerJSONReplacer { return resolved_seq; } + static replace_spec_form_index(index) { + if (index.value == 0) + return "SpecFormIndex{null}"; + const spec_form = sgraph.resolver.resolve_spec_form(index); + return { template_decl: spec_form.template_decl, arguments: spec_form.arguments }; + } + static replace_scope_index(index) { if (null_scope(index)) { return { scope_index: index.value, decls: [] }; @@ -184,6 +191,8 @@ function ifc_explorer_json_replacer(key, value) { return IFCExplorerJSONReplacer.replace_locus(value); if (value instanceof ScopeIndex) return IFCExplorerJSONReplacer.replace_scope_index(value); + if (value instanceof SpecFormIndex) + return IFCExplorerJSONReplacer.replace_spec_form_index(value); if (value instanceof Operator) return IFCExplorerJSONReplacer.replace_operator(value); if (value instanceof HeapSequence) @@ -198,6 +207,10 @@ function ifc_explorer_json_replacer(key, value) { return IFCExplorerJSONReplacer.generic_bitset_to_string(FunctionTraits, value); if (value instanceof FunctionTypeTraits) return IFCExplorerJSONReplacer.generic_bitset_to_string(FunctionTypeTraits, value); + if (value instanceof ObjectTraits) + return IFCExplorerJSONReplacer.generic_bitset_to_string(ObjectTraits, value); + if (value instanceof MsvcVendorTraits) + return IFCExplorerJSONReplacer.generic_bitset_to_string(MsvcVendorTraits, value); if (value instanceof Qualifier) return IFCExplorerJSONReplacer.generic_bitset_to_string(Qualifier, value); if (value instanceof Phases) @@ -252,7 +265,11 @@ function set_ifc_explorer_selected_decl(index, from_history, skip_navigation) { ifc_explorer.decls.index_edit.value = index.index; const symbolic = symbolic_for_decl_sort(index.sort); - const symbolic_decl = sgraph.resolver.read(symbolic, index.index); + var symbolic_decl = sgraph.resolver.read(symbolic, index.index); + const msvc_traits = sgraph.resolver.associated_trait_index(index, sgraph.resolver.msvc_traits); + if (msvc_traits != undefined) { + symbolic_decl.msvc_traits = msvc_traits; + } const json_str = JSON.stringify(symbolic_decl, ifc_explorer_json_replacer, 2); const container = document.createElement("pre"); const sort = sort_to_string(DeclIndex, index.sort); diff --git a/samples/sgraph-js/ui/options.js b/samples/sgraph-js/ui/options.js index bd6fa62..96aa440 100644 --- a/samples/sgraph-js/ui/options.js +++ b/samples/sgraph-js/ui/options.js @@ -135,7 +135,7 @@ class Options { case DeclIndex.Sort.VendorExtension: return 'tan'; case DeclIndex.Sort.DefaultArgument: - case DeclIndex.Sort.UnusedSort0: + case DeclIndex.Sort.Prolongation: case DeclIndex.Sort.Count: return 'black'; default: diff --git a/src/sgraph.cxx b/src/sgraph.cxx index dfe2168..6922381 100644 --- a/src/sgraph.cxx +++ b/src/sgraph.cxx @@ -59,7 +59,7 @@ namespace ifc { {DeclSort::Destructor, "decl.destructor"}, {DeclSort::Reference, "decl.reference"}, {DeclSort::Using, "decl.using-declaration"}, - {DeclSort::UnusedSort0, "decl.unused0"}, + {DeclSort::Prolongation, "decl.prolongation"}, {DeclSort::Friend, "decl.friend"}, {DeclSort::Expansion, "decl.expansion"}, {DeclSort::DeductionGuide, "decl.deduction-guide"}, @@ -358,6 +358,7 @@ namespace ifc { {TraitSort::Friends, "trait.friend"}, {TraitSort::Specializations, "trait.specialization"}, {TraitSort::Requires, "trait.requires"}, {TraitSort::Attributes, "trait.attribute"}, {TraitSort::Deprecated, "trait.deprecated"}, {TraitSort::DeductionGuides, "trait.deduction-guides"}, + {TraitSort::Prolongations, "trait.prolongation"}, }; static_assert(retractible_by_key(traitsort_table));