diff --git a/crates/libs/clang/src/canon.rs b/crates/libs/clang/src/canon.rs index d1cb5379e3e..88b08a9ee21 100644 --- a/crates/libs/clang/src/canon.rs +++ b/crates/libs/clang/src/canon.rs @@ -42,7 +42,7 @@ pub(crate) fn resolve_typedef(cursor: &Type, parser: &mut Parser<'_>) -> metadat } else if decl.is_from_main_file() && parser.symbols.is_empty() { metadata::Type::value_named(parser.namespace, &name) } else { - parser.pending_typedefs.push(decl); + parser.pending_declarations.push(decl); metadata::Type::value_named(parser.namespace, &name) } } diff --git a/crates/libs/clang/src/cx.rs b/crates/libs/clang/src/cx.rs index bba95cbb961..0a463d461b1 100644 --- a/crates/libs/clang/src/cx.rs +++ b/crates/libs/clang/src/cx.rs @@ -688,6 +688,21 @@ impl Type { self.function_pointee().is_some() } + pub fn is_incomplete_record(&self) -> bool { + let ty = self.canonical_type(); + ty.kind() == CXType_Record && !ty.ty().has_definition() + } + + fn incomplete_record_name(&self, parser: &Parser<'_>) -> Option { + let ty = self.canonical_type(); + if ty.kind() != CXType_Record || ty.ty().has_definition() { + return None; + } + let decl = ty.ty(); + let tag = decl.name(); + Some(parser.tag_rename.get(&tag).cloned().unwrap_or(tag)) + } + pub fn function_pointee(&self) -> Option { match self.kind() { // Function-type typedefs emit as callbacks like pointer typedefs. @@ -866,17 +881,25 @@ impl Type { .to_string() }; let definition = decl.definition(); - if self.kind() == CXType_Record + if matches!(self.kind(), CXType_Record | CXType_Enum) && parser.header_root.is_none() && ns == parser.namespace && decl.has_definition() && (!definition.is_from_main_file() || !parser.symbols.is_empty()) { - parser.pending_records.push(definition); + parser.pending_declarations.push(definition); + } else if self.kind() == CXType_Enum + && parser.header_root.is_none() + && ns == parser.namespace + && !decl.has_definition() + { + if let Some((_, repr)) = enum_repr_type(decl) { + parser.pending_enum_aliases.push((name.clone(), repr)); + } else { + parser.pending_invalid_enums.push(decl); + } } - // Incomplete namespaced records remain unresolved. Deciding whether a particular - // use can be represented by an opaque declaration requires usage-shape analysis. - // Pointer-only incomplete records need an opaque forward declaration target. + // Per-header pointer-only records need an opaque target in their owning partition. if parser.header_root.is_some() && !is_anonymous_name(&name) && !name.ends_with("__") @@ -924,6 +947,14 @@ impl Type { return pointee.to_type(parser); } let inner = pointee.to_type(parser); + if parser.header_root.is_none() + && pointee.is_incomplete_record() + && let metadata::Type::ValueName(name) = &inner + && name.namespace == parser.namespace + && let Some(name) = pointee.incomplete_record_name(parser) + { + parser.pending_opaque_records.push(name); + } if pointee.is_const() { match inner { metadata::Type::PtrConst(t, n) => metadata::Type::PtrConst(t, n + 1), @@ -945,6 +976,14 @@ impl Type { return pointee.to_type(parser); } let inner = pointee.to_type(parser); + if parser.header_root.is_none() + && pointee.is_incomplete_record() + && let metadata::Type::ValueName(name) = &inner + && name.namespace == parser.namespace + && let Some(name) = pointee.incomplete_record_name(parser) + { + parser.pending_opaque_records.push(name); + } if pointee.is_const() { match inner { metadata::Type::PtrConst(t, n) => metadata::Type::PtrConst(t, n + 1), diff --git a/crates/libs/clang/src/enum.rs b/crates/libs/clang/src/enum.rs index 2b197fa5eb1..530af765afe 100644 --- a/crates/libs/clang/src/enum.rs +++ b/crates/libs/clang/src/enum.rs @@ -12,17 +12,9 @@ pub struct Enum { impl Enum { pub fn parse(cursor: Cursor) -> Result { - let repr = match cursor.enum_repr().kind() { - CXType_Int | CXType_Long => "i32", - CXType_UInt | CXType_ULong => "u32", - CXType_Short => "i16", - CXType_UShort => "u16", - CXType_Char_S | CXType_SChar => "i8", - CXType_Char_U | CXType_UChar => "u8", - CXType_LongLong => "i64", - CXType_ULongLong => "u64", - _ => "i32", - }; + let (repr, _) = enum_repr_type(cursor).ok_or_else(|| { + Error::new("unsupported enum representation", &cursor.file_name(), 0, 0) + })?; let name = cursor.name(); let scoped = cursor.is_scoped_enum(); @@ -100,3 +92,17 @@ impl Enum { }) } } + +pub(crate) fn enum_repr_type(cursor: Cursor) -> Option<(&'static str, metadata::Type)> { + Some(match cursor.enum_repr().canonical_type().kind() { + CXType_Int | CXType_Long => ("i32", metadata::Type::I32), + CXType_UInt | CXType_ULong => ("u32", metadata::Type::U32), + CXType_Short => ("i16", metadata::Type::I16), + CXType_UShort => ("u16", metadata::Type::U16), + CXType_Char_S | CXType_SChar => ("i8", metadata::Type::I8), + CXType_Char_U | CXType_UChar => ("u8", metadata::Type::U8), + CXType_LongLong => ("i64", metadata::Type::I64), + CXType_ULongLong => ("u64", metadata::Type::U64), + _ => return None, + }) +} diff --git a/crates/libs/clang/src/lib.rs b/crates/libs/clang/src/lib.rs index ea53bc284fe..395f21e894e 100644 --- a/crates/libs/clang/src/lib.rs +++ b/crates/libs/clang/src/lib.rs @@ -78,9 +78,13 @@ pub(crate) struct Parser<'a> { pub tag_rename: &'a HashMap, /// Enum reprs taken from integer typedefs in the C flags/enum idiom. pub enum_merge: &'a HashMap, + /// Nominal declarations available in this translation unit, keyed by projected name. + pub declarations: &'a HashMap, pub tu: &'a TranslationUnit, - pub pending_typedefs: Vec, - pub pending_records: Vec, + pub pending_declarations: Vec, + pub pending_opaque_records: Vec, + pub pending_enum_aliases: Vec<(String, metadata::Type)>, + pub pending_invalid_enums: Vec, pub pending_macros: Vec, processing_dependency: bool, /// Per-header mode: incomplete pointer-only records emitted as opaque structs. @@ -120,6 +124,7 @@ impl<'a> Parser<'a> { ref_map: &'a HashMap, tag_rename: &'a HashMap, enum_merge: &'a HashMap, + declarations: &'a HashMap, macro_defs: &'a HashMap>, tu: &'a TranslationUnit, symbols: &'a HashSet, @@ -133,9 +138,12 @@ impl<'a> Parser<'a> { header_names: None, tag_rename, enum_merge, + declarations, tu, - pending_typedefs: vec![], - pending_records: vec![], + pending_declarations: vec![], + pending_opaque_records: vec![], + pending_enum_aliases: vec![], + pending_invalid_enums: vec![], pending_macros: vec![], processing_dependency: false, pending_opaque: vec![], @@ -234,8 +242,12 @@ impl<'a> Parser<'a> { if semantic_scalar_definition(&name, child.kind()).is_some() { return Ok(()); } - // Do not clobber a real definition aliased by another tag. - if !self.ref_map.contains_key(&name) && !collector.contains_key(&name) { + // Per-header mode retains declarations in their owning partition. Namespaced + // mode emits an opaque record only after observing a pointer-only use. + if self.header_root.is_some() + && !self.ref_map.contains_key(&name) + && !collector.contains_key(&name) + { collector.insert(Item::Struct(Struct::opaque(&name))); } } @@ -1183,6 +1195,7 @@ impl Clang { let enum_merge = merge_enum_typedef_idiom(tu, &mut tag_rename); // Share TU-wide macro definitions across per-header parsers. let macro_defs = collect_macro_defs(tu); + let declarations = HashMap::new(); // Flatten linkage blocks and deduplicate by clang identity across repeated SDK // declarations; the defining header only selects the output file. @@ -1257,6 +1270,7 @@ impl Clang { &empty_ref, &tag_rename, &enum_merge, + &declarations, ¯o_defs, tu, &empty_symbols, @@ -1381,6 +1395,12 @@ impl Clang { // Reuse translation units across all specs. let parsed = self.parse_inputs()?; let arg_refs: Vec<&str> = parsed.args.iter().map(String::as_str).collect(); + let mut declarations = HashMap::new(); + for (_, tu) in parsed.h_tus.iter().chain(&parsed.str_tus) { + let mut tag_rename = build_tag_rename_map(tu); + assign_nested_names(tu, &mut tag_rename); + extend_declaration_map(&mut declarations, tu, &tag_rename); + } // Pass 1: learn unique type-name owners across specs. Shared typedef artifacts stay // local by being dropped from the owner table. @@ -1389,11 +1409,18 @@ impl Clang { let ref_map = build_ref_map(reference, spec.namespace); let mut collector = Collector::new(); for (_, tu) in &parsed.h_tus { - self.process_tu(tu, &mut collector, &ref_map, spec)?; + self.process_tu(tu, &mut collector, &ref_map, &declarations, spec, None)?; } for (_, tu) in &parsed.str_tus { - self.process_tu(tu, &mut collector, &ref_map, spec)?; + self.process_tu(tu, &mut collector, &ref_map, &declarations, spec, None)?; } + self.process_cross_tu_layout_dependencies( + &parsed, + &mut collector, + &ref_map, + &declarations, + spec, + )?; for name in collector.keys() { owners .entry(name.clone()) @@ -1418,18 +1445,27 @@ impl Clang { let mut collector = Collector::new(); for (input, tu) in &parsed.h_tus { - let pending = self.process_tu(tu, &mut collector, &ref_map, spec)?; + let pending = + self.process_tu(tu, &mut collector, &ref_map, &declarations, spec, None)?; for c in Const::evaluate_macros(input, &pending, &parsed.index, &arg_refs)? { collector.insert(Item::Const(c)); } } for (content, tu) in &parsed.str_tus { - let pending = self.process_tu(tu, &mut collector, &ref_map, spec)?; + let pending = + self.process_tu(tu, &mut collector, &ref_map, &declarations, spec, None)?; for c in Const::evaluate_macros_str(content, &pending, &parsed.index, &arg_refs)? { collector.insert(Item::Const(c)); } } + self.process_cross_tu_layout_dependencies( + &parsed, + &mut collector, + &ref_map, + &declarations, + spec, + )?; outputs.push(emit_module(spec.namespace, &collector)?); } @@ -1443,7 +1479,9 @@ impl Clang { tu: &TranslationUnit, collector: &mut Collector, ref_map: &HashMap, + declarations: &HashMap, spec: &NamespaceSpec<'_>, + required_declarations: Option<&HashSet>, ) -> Result, Error> { for diag in tu.diagnostics() { if diag.is_err() { @@ -1463,6 +1501,7 @@ impl Clang { assign_nested_names(tu, &mut tag_rename); let enum_merge = merge_enum_typedef_idiom(tu, &mut tag_rename); let macro_defs = collect_macro_defs(tu); + let local_declarations = build_declaration_map(tu, &tag_rename); let mut parser = Parser::new( spec.namespace, @@ -1471,6 +1510,7 @@ impl Clang { ref_map, &tag_rename, &enum_merge, + &local_declarations, ¯o_defs, tu, spec.symbols, @@ -1500,23 +1540,65 @@ impl Clang { parser.process_cursor(child, collector, false)?; } - // Drain referenced type dependencies; parsing one definition can enqueue more. - let mut seen_typedefs: HashSet = HashSet::new(); - let mut seen_records: HashSet = HashSet::new(); - let mut typedef_index = 0; - let mut record_index = 0; - while typedef_index < parser.pending_typedefs.len() - || record_index < parser.pending_records.len() - { - while typedef_index < parser.pending_typedefs.len() { - let cursor = parser.pending_typedefs[typedef_index]; - typedef_index += 1; - let name = cursor.name(); - // Skip anything already resolved. - if !seen_typedefs.insert(name.clone()) - || collector.contains_key(&name) - || parser.ref_map.contains_key(&name) + if let Some(required) = required_declarations { + for name in required { + let is_complete = match collector.get(name) { + Some(Item::Struct(item)) => !item.is_opaque, + Some(_) => true, + None => false, + }; + if is_complete || parser.ref_map.contains_key(name) { + continue; + } + if let Some(cursor) = parser.declarations.get(name) + && (cursor.kind() == CXCursor_TypedefDecl || cursor.has_definition()) { + parser.pending_declarations.push(*cursor); + } + } + } + + // Constants have no type cursor, so seed their named dependencies from the TU index. + let mut referenced = HashSet::new(); + for item in collector.values() { + if matches!(item, Item::Const(_) | Item::PropertyKeyConst(_)) { + item_refs(item, &mut referenced); + } + } + for name in referenced { + if collector.contains_key(&name) || parser.ref_map.contains_key(&name) { + continue; + } + if let Some(cursor) = parser.declarations.get(&name) { + if cursor.kind() == CXCursor_EnumDecl && !cursor.has_definition() { + if let Some((_, repr)) = enum_repr_type(*cursor) { + parser.pending_enum_aliases.push((name, repr)); + } else { + parser.pending_invalid_enums.push(*cursor); + } + } else if cursor.kind() == CXCursor_TypedefDecl || cursor.has_definition() { + parser.pending_declarations.push(*cursor); + } + } + } + + // Drain the declaration worklist; parsing one declaration can enqueue more. + let mut seen: HashSet = HashSet::new(); + let mut index = 0; + while index < parser.pending_declarations.len() { + let cursor = parser.pending_declarations[index]; + index += 1; + let mut identity = cursor.usr(); + if identity.is_empty() { + identity = cursor.location_id(); + } + if !seen.insert(identity) { + continue; + } + + if cursor.kind() == CXCursor_TypedefDecl { + let name = cursor.name(); + if collector.contains_key(&name) || parser.ref_map.contains_key(&name) { continue; } if let Some(cb) = Callback::parse(cursor, &mut parser)? { @@ -1524,17 +1606,67 @@ impl Clang { } else if let Some(td) = Typedef::parse(cursor, &mut parser)? { collector.insert(Item::Typedef(td)); } + } else { + parser.processing_dependency = true; + let result = parser.process_cursor(cursor, collector, false); + parser.processing_dependency = false; + result?; } + } - while record_index < parser.pending_records.len() { - let cursor = parser.pending_records[record_index]; - record_index += 1; - if seen_records.insert(cursor.usr()) { - parser.processing_dependency = true; - let result = parser.process_cursor(cursor, collector, false); - parser.processing_dependency = false; - result?; - } + for (name, ty) in std::mem::take(&mut parser.pending_enum_aliases) { + if !collector.contains_key(&name) && !parser.ref_map.contains_key(&name) { + collector.insert(Item::Typedef(Typedef { name, ty })); + } + } + if let Some(cursor) = parser.pending_invalid_enums.first() { + return Err(Error::new( + "unsupported enum representation", + &cursor.file_name(), + 0, + 0, + )); + } + + let mut layout_refs = HashSet::new(); + for item in collector.values() { + item_layout_refs(item, &mut layout_refs); + } + for name in &layout_refs { + let Some(cursor) = declarations.get(name) else { + continue; + }; + let ty = if cursor.kind() == CXCursor_TypedefDecl { + cursor.typedef_underlying_type() + } else { + cursor.ty() + }; + let canonical = ty.canonical_type().ty(); + let canonical_usr = canonical.usr(); + let complete_elsewhere = ty.is_incomplete_record() + && declarations.values().any(|candidate| { + matches!( + candidate.kind(), + CXCursor_StructDecl | CXCursor_UnionDecl | CXCursor_ClassDecl + ) && candidate.has_definition() + && if canonical_usr.is_empty() { + candidate.name() == canonical.name() + } else { + candidate.usr() == canonical_usr + } + }); + if ty.is_incomplete_record() && !complete_elsewhere { + return Err(Error::new( + "incomplete record used by value", + &cursor.file_name(), + 0, + 0, + )); + } + } + for name in std::mem::take(&mut parser.pending_opaque_records) { + if !collector.contains_key(&name) && !parser.ref_map.contains_key(&name) { + collector.insert(Item::Struct(Struct::opaque(&name))); } } @@ -1543,6 +1675,54 @@ impl Clang { Ok(parser.pending_macros) } + + fn process_cross_tu_layout_dependencies( + &self, + parsed: &ParsedInputs, + collector: &mut Collector, + ref_map: &HashMap, + declarations: &HashMap, + spec: &NamespaceSpec<'_>, + ) -> Result<(), Error> { + if spec.symbols.is_empty() { + return Ok(()); + } + + let mut processed = HashSet::new(); + loop { + let mut required = HashSet::new(); + for item in collector.values() { + item_layout_refs(item, &mut required); + } + loop { + let len = required.len(); + let aliases: Vec<_> = required + .iter() + .filter_map(|name| match collector.get(name) { + Some(Item::Typedef(item)) => Some(&item.ty), + _ => None, + }) + .collect(); + for ty in aliases { + type_layout_refs(ty, &mut required); + } + if required.len() == len { + break; + } + } + required.retain(|name| !processed.contains(name)); + if required.is_empty() { + break; + } + processed.extend(required.iter().cloned()); + + for (_, tu) in parsed.h_tus.iter().chain(&parsed.str_tus) { + self.process_tu(tu, collector, ref_map, declarations, spec, Some(&required))?; + } + } + + Ok(()) + } } /// Owns libclang state; field order ensures TUs drop before the library unloads. @@ -1851,6 +2031,7 @@ mod tests { bitfields: vec![], }], is_union: true, + is_opaque: false, packing: None, alignment: None, }; diff --git a/crates/libs/clang/src/naming.rs b/crates/libs/clang/src/naming.rs index bf23cc90906..70358059230 100644 --- a/crates/libs/clang/src/naming.rs +++ b/crates/libs/clang/src/naming.rs @@ -24,6 +24,77 @@ pub(crate) fn build_tag_rename_map(tu: &TranslationUnit) -> HashMap, +) -> HashMap { + let mut map = HashMap::new(); + extend_declaration_map(&mut map, tu, tag_rename); + map +} + +/// Add one translation unit's declarations to a shared projected-name index. +pub(crate) fn extend_declaration_map( + map: &mut HashMap, + tu: &TranslationUnit, + tag_rename: &HashMap, +) { + fn rank(cursor: Cursor) -> u8 { + match cursor.kind() { + CXCursor_StructDecl | CXCursor_UnionDecl | CXCursor_ClassDecl | CXCursor_EnumDecl + if cursor.is_definition() => + { + 2 + } + CXCursor_TypedefDecl => 1, + _ => 0, + } + } + + fn insert(map: &mut HashMap, name: String, cursor: Cursor) { + map.entry(name) + .and_modify(|existing| { + if rank(cursor) > rank(*existing) { + *existing = cursor; + } + }) + .or_insert(cursor); + } + + fn walk( + cursor: Cursor, + tag_rename: &HashMap, + map: &mut HashMap, + ) { + for child in cursor.children() { + if child.kind() == CXCursor_LinkageSpec { + walk(child, tag_rename, map); + continue; + } + + match child.kind() { + CXCursor_TypedefDecl => insert(map, child.name(), child), + CXCursor_StructDecl | CXCursor_UnionDecl | CXCursor_ClassDecl + | CXCursor_EnumDecl => { + let tag = child.name(); + let name = if is_anonymous_name(&tag) { + tag_rename.get(&child.location_id()).cloned().unwrap_or(tag) + } else { + tag_rename.get(&tag).cloned().unwrap_or(tag) + }; + if !is_anonymous_name(&name) { + insert(map, name, child); + } + } + _ => {} + } + } + } + + walk(tu.cursor(), tag_rename, map); +} + /// Merge `enum _FOO { ... }; typedef DWORD FOO;` into one public enum. /// /// The typedef supplies the backing type and signedness; the enum supplies the members. diff --git a/crates/libs/clang/src/scope.rs b/crates/libs/clang/src/scope.rs index 63098f953b9..e1344db644e 100644 --- a/crates/libs/clang/src/scope.rs +++ b/crates/libs/clang/src/scope.rs @@ -172,6 +172,92 @@ pub(crate) fn item_refs(item: &Item, out: &mut HashSet) { } } +fn collect_layout_type_refs(ty: &metadata::Type, out: &mut HashSet) { + match ty { + metadata::Type::ClassName(name) | metadata::Type::ValueName(name) => { + out.insert(name.name.clone()); + } + metadata::Type::ArrayFixed(inner, _) => collect_layout_type_refs(inner, out), + metadata::Type::Array(_) + | metadata::Type::RefMut(_) + | metadata::Type::RefConst(_) + | metadata::Type::PtrMut(_, _) + | metadata::Type::PtrConst(_, _) + | metadata::Type::Bool + | metadata::Type::Char + | metadata::Type::I8 + | metadata::Type::U8 + | metadata::Type::I16 + | metadata::Type::U16 + | metadata::Type::I32 + | metadata::Type::U32 + | metadata::Type::I64 + | metadata::Type::U64 + | metadata::Type::F32 + | metadata::Type::F64 + | metadata::Type::ISize + | metadata::Type::USize + | metadata::Type::String + | metadata::Type::Object + | metadata::Type::Generic(_, _) + | metadata::Type::Void => {} + } +} + +pub(crate) fn type_layout_refs(ty: &metadata::Type, out: &mut HashSet) { + collect_layout_type_refs(ty, out); +} + +fn collect_layout_field_refs(fields: &[Field], out: &mut HashSet) { + for field in fields { + collect_layout_type_refs(&field.ty, out); + if let Some(nested) = &field.nested { + collect_layout_field_refs(&nested.fields, out); + } + } +} + +/// Collect nominal types whose ABI use requires a complete layout. +pub(crate) fn item_layout_refs(item: &Item, out: &mut HashSet) { + match item { + Item::Fn(item) => { + for param in &item.params { + collect_layout_type_refs(¶m.ty, out); + } + collect_layout_type_refs(&item.return_type, out); + } + Item::Callback(item) => { + for param in &item.params { + collect_layout_type_refs(¶m.ty, out); + } + collect_layout_type_refs(&item.return_type, out); + } + Item::Interface(item) => { + if let Some(base) = &item.base { + collect_layout_type_refs(base, out); + } + for method in &item.methods { + for param in &method.params { + collect_layout_type_refs(¶m.ty, out); + } + collect_layout_type_refs(&method.return_type, out); + } + } + Item::Struct(item) => collect_layout_field_refs(&item.fields, out), + // A typedef names a type but does not itself use that type by value. + Item::Typedef(_) => {} + Item::Const(item) => { + if let Some(ty) = &item.ty { + collect_layout_type_refs(ty, out); + } + } + Item::PropertyKeyConst(item) => { + out.insert(item.ty.clone()); + } + Item::Enum(_) | Item::GuidConst(_) => {} + } +} + /// Remove out-of-scope declarations not reachable from an in-scope declaration. pub(crate) fn sweep_unreferenced( collectors: &mut BTreeMap, diff --git a/crates/libs/clang/src/struct.rs b/crates/libs/clang/src/struct.rs index d6ac1acdabf..4ab6a81b310 100644 --- a/crates/libs/clang/src/struct.rs +++ b/crates/libs/clang/src/struct.rs @@ -30,6 +30,7 @@ pub struct Struct { pub name: String, pub fields: Vec, pub is_union: bool, + pub is_opaque: bool, /// Non-zero packing size in bytes, or `None` for natural alignment. pub packing: Option, /// Forced over-alignment in bytes; mutually exclusive with `packing`. @@ -43,6 +44,7 @@ impl Struct { name: name.to_string(), fields: vec![], is_union: false, + is_opaque: true, packing: None, alignment: None, } @@ -242,6 +244,7 @@ impl Struct { name, fields, is_union, + is_opaque: false, packing, alignment, }) diff --git a/crates/libs/clang/src/typedef.rs b/crates/libs/clang/src/typedef.rs index befebf99625..83fcd6094fb 100644 --- a/crates/libs/clang/src/typedef.rs +++ b/crates/libs/clang/src/typedef.rs @@ -103,7 +103,7 @@ impl Typedef { .get(&tag) .cloned() .unwrap_or_else(|| tag.clone()); - if parser.header_root.is_none() && inner_kind == CXType_Record { + if parser.header_root.is_none() && matches!(inner_kind, CXType_Record | CXType_Enum) { // Pull the backing definition into namespaced output before deciding whether this // typedef is the public name or a secondary alias. inner.to_type(parser); diff --git a/crates/libs/default/Windows.Win32.winmd b/crates/libs/default/Windows.Win32.winmd index 8e855d87a19..1b2712bf2d9 100644 Binary files a/crates/libs/default/Windows.Win32.winmd and b/crates/libs/default/Windows.Win32.winmd differ diff --git a/crates/libs/sys/src/Windows/Win32/d3dkmdt/mod.rs b/crates/libs/sys/src/Windows/Win32/d3dkmdt/mod.rs index 2e4e3d52fd3..89be17d8ee7 100644 --- a/crates/libs/sys/src/Windows/Win32/d3dkmdt/mod.rs +++ b/crates/libs/sys/src/Windows/Win32/d3dkmdt/mod.rs @@ -1523,7 +1523,7 @@ impl Default for DXGK_DISPLAYMUX_SET_INTERNAL_PANEL_INFO { } } pub type DXGK_DISPLAYMUX_SUPPORT_LEVEL = i32; -pub type DXGK_DISPLAY_DESCRIPTOR_TYPE = i32; +pub type DXGK_DISPLAY_DESCRIPTOR_TYPE = u8; #[repr(C)] #[cfg(all(feature = "d3dukmdt", feature = "usb"))] #[derive(Clone, Copy, Default)] @@ -1536,8 +1536,8 @@ pub struct DXGK_DISPLAY_INFORMATION { pub TargetId: super::D3DDDI_VIDEO_PRESENT_TARGET_ID, pub AcpiId: u32, } -pub type DXGK_DISPLAY_TECHNOLOGY = i32; -pub type DXGK_DISPLAY_USAGE = i32; +pub type DXGK_DISPLAY_TECHNOLOGY = u8; +pub type DXGK_DISPLAY_USAGE = u8; pub const DXGK_DT_INVALID: DXGK_DISPLAY_TECHNOLOGY = 0; pub const DXGK_DT_LCD: DXGK_DISPLAY_TECHNOLOGY = 2; pub const DXGK_DT_MAX: DXGK_DISPLAY_TECHNOLOGY = 5; diff --git a/crates/libs/sys/src/Windows/Win32/dwrite/mod.rs b/crates/libs/sys/src/Windows/Win32/dwrite/mod.rs index f66cd8bb9d1..af171ad3006 100644 --- a/crates/libs/sys/src/Windows/Win32/dwrite/mod.rs +++ b/crates/libs/sys/src/Windows/Win32/dwrite/mod.rs @@ -119,7 +119,7 @@ pub struct DWRITE_FONT_AXIS_RANGE { pub minValue: f32, pub maxValue: f32, } -pub type DWRITE_FONT_AXIS_TAG = i32; +pub type DWRITE_FONT_AXIS_TAG = u32; pub const DWRITE_FONT_AXIS_TAG_ITALIC: DWRITE_FONT_AXIS_TAG = 1818326121; pub const DWRITE_FONT_AXIS_TAG_OPTICAL_SIZE: DWRITE_FONT_AXIS_TAG = 2054385775; pub const DWRITE_FONT_AXIS_TAG_SLANT: DWRITE_FONT_AXIS_TAG = 1953393779; diff --git a/crates/libs/sys/src/Windows/Win32/dxcore_interface/mod.rs b/crates/libs/sys/src/Windows/Win32/dxcore_interface/mod.rs index c8bfd999b9a..499dcf5a598 100644 --- a/crates/libs/sys/src/Windows/Win32/dxcore_interface/mod.rs +++ b/crates/libs/sys/src/Windows/Win32/dxcore_interface/mod.rs @@ -34,7 +34,7 @@ impl Default for DXCoreAdapterMemoryBudgetNodeSegmentGroup { } #[repr(transparent)] #[derive(Clone, Copy)] -pub struct DXCoreAdapterPreference(pub i32); +pub struct DXCoreAdapterPreference(pub u32); impl DXCoreAdapterPreference { pub const Hardware: Self = Self(0); pub const MinimumPower: Self = Self(1); @@ -54,7 +54,7 @@ pub struct DXCoreAdapterProcessSetQueryOutput { } #[repr(transparent)] #[derive(Clone, Copy)] -pub struct DXCoreAdapterProperty(pub i32); +pub struct DXCoreAdapterProperty(pub u32); impl DXCoreAdapterProperty { pub const InstanceLuid: Self = Self(0); pub const DriverVersion: Self = Self(1); @@ -77,7 +77,7 @@ impl DXCoreAdapterProperty { } #[repr(transparent)] #[derive(Clone, Copy)] -pub struct DXCoreAdapterState(pub i32); +pub struct DXCoreAdapterState(pub u32); impl DXCoreAdapterState { pub const IsDriverUpdateInProgress: Self = Self(0); pub const AdapterMemoryBudget: Self = Self(1); @@ -163,7 +163,7 @@ impl Default for DXCoreMemoryQueryInput { } #[repr(transparent)] #[derive(Clone, Copy)] -pub struct DXCoreMemoryType(pub i32); +pub struct DXCoreMemoryType(pub u32); impl DXCoreMemoryType { pub const Dedicated: Self = Self(0); pub const Shared: Self = Self(1); @@ -176,7 +176,7 @@ pub struct DXCoreMemoryUsage { } #[repr(transparent)] #[derive(Clone, Copy)] -pub struct DXCoreNotificationType(pub i32); +pub struct DXCoreNotificationType(pub u32); impl DXCoreNotificationType { pub const AdapterListStale: Self = Self(0); pub const AdapterNoLongerValid: Self = Self(1); @@ -211,14 +211,14 @@ impl DXCoreRuntimeFilterFlags { } #[repr(transparent)] #[derive(Clone, Copy)] -pub struct DXCoreSegmentGroup(pub i32); +pub struct DXCoreSegmentGroup(pub u32); impl DXCoreSegmentGroup { pub const Local: Self = Self(0); pub const NonLocal: Self = Self(1); } #[repr(transparent)] #[derive(Clone, Copy)] -pub struct DXCoreSingleAdapterHybridMode(pub i32); +pub struct DXCoreSingleAdapterHybridMode(pub u32); impl DXCoreSingleAdapterHybridMode { pub const Unspecified: Self = Self(0); pub const MinimumPower: Self = Self(1); @@ -226,7 +226,7 @@ impl DXCoreSingleAdapterHybridMode { } #[repr(transparent)] #[derive(Clone, Copy)] -pub struct DXCoreWorkload(pub i32); +pub struct DXCoreWorkload(pub u32); impl DXCoreWorkload { pub const Graphics: Self = Self(0); pub const Compute: Self = Self(1); diff --git a/crates/libs/sys/src/Windows/Win32/wincrypt/mod.rs b/crates/libs/sys/src/Windows/Win32/wincrypt/mod.rs index 0fa16a32da4..d04ab1e8b10 100644 --- a/crates/libs/sys/src/Windows/Win32/wincrypt/mod.rs +++ b/crates/libs/sys/src/Windows/Win32/wincrypt/mod.rs @@ -4658,7 +4658,7 @@ pub struct CT_EXTRA_CERT_CHAIN_POLICY_STATUS { pub cValidated: u32, } pub const CUR_BLOB_VERSION: i32 = 2; -pub type CertKeyType = i32; +pub type CertKeyType = u32; pub type DATA_BLOB = CRYPT_INTEGER_BLOB; #[repr(C)] #[derive(Clone, Copy, Default)] diff --git a/crates/libs/windows/src/Windows/Win32/d3dkmdt/mod.rs b/crates/libs/windows/src/Windows/Win32/d3dkmdt/mod.rs index 7ecf50b04e1..d19b88dea57 100644 --- a/crates/libs/windows/src/Windows/Win32/d3dkmdt/mod.rs +++ b/crates/libs/windows/src/Windows/Win32/d3dkmdt/mod.rs @@ -2145,7 +2145,7 @@ impl Default for DXGK_DISPLAYMUX_SET_INTERNAL_PANEL_INFO { } } pub type DXGK_DISPLAYMUX_SUPPORT_LEVEL = i32; -pub type DXGK_DISPLAY_DESCRIPTOR_TYPE = i32; +pub type DXGK_DISPLAY_DESCRIPTOR_TYPE = u8; #[repr(C)] #[cfg(all(feature = "d3dukmdt", feature = "usb"))] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] @@ -2158,8 +2158,8 @@ pub struct DXGK_DISPLAY_INFORMATION { pub TargetId: super::D3DDDI_VIDEO_PRESENT_TARGET_ID, pub AcpiId: u32, } -pub type DXGK_DISPLAY_TECHNOLOGY = i32; -pub type DXGK_DISPLAY_USAGE = i32; +pub type DXGK_DISPLAY_TECHNOLOGY = u8; +pub type DXGK_DISPLAY_USAGE = u8; pub const DXGK_DT_INVALID: DXGK_DISPLAY_TECHNOLOGY = 0; pub const DXGK_DT_LCD: DXGK_DISPLAY_TECHNOLOGY = 2; pub const DXGK_DT_MAX: DXGK_DISPLAY_TECHNOLOGY = 5; diff --git a/crates/libs/windows/src/Windows/Win32/dwrite/mod.rs b/crates/libs/windows/src/Windows/Win32/dwrite/mod.rs index ae2fe7dfea2..8fce0442ac8 100644 --- a/crates/libs/windows/src/Windows/Win32/dwrite/mod.rs +++ b/crates/libs/windows/src/Windows/Win32/dwrite/mod.rs @@ -165,7 +165,7 @@ pub struct DWRITE_FONT_AXIS_RANGE { pub minValue: f32, pub maxValue: f32, } -pub type DWRITE_FONT_AXIS_TAG = i32; +pub type DWRITE_FONT_AXIS_TAG = u32; pub const DWRITE_FONT_AXIS_TAG_ITALIC: DWRITE_FONT_AXIS_TAG = 1818326121; pub const DWRITE_FONT_AXIS_TAG_OPTICAL_SIZE: DWRITE_FONT_AXIS_TAG = 2054385775; pub const DWRITE_FONT_AXIS_TAG_SLANT: DWRITE_FONT_AXIS_TAG = 1953393779; diff --git a/crates/libs/windows/src/Windows/Win32/dxcore_interface/mod.rs b/crates/libs/windows/src/Windows/Win32/dxcore_interface/mod.rs index b8c420000d7..f699c8e0810 100644 --- a/crates/libs/windows/src/Windows/Win32/dxcore_interface/mod.rs +++ b/crates/libs/windows/src/Windows/Win32/dxcore_interface/mod.rs @@ -29,7 +29,7 @@ pub struct DXCoreAdapterMemoryBudgetNodeSegmentGroup { } #[repr(transparent)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub struct DXCoreAdapterPreference(pub i32); +pub struct DXCoreAdapterPreference(pub u32); impl DXCoreAdapterPreference { pub const Hardware: Self = Self(0); pub const MinimumPower: Self = Self(1); @@ -49,7 +49,7 @@ pub struct DXCoreAdapterProcessSetQueryOutput { } #[repr(transparent)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub struct DXCoreAdapterProperty(pub i32); +pub struct DXCoreAdapterProperty(pub u32); impl DXCoreAdapterProperty { pub const InstanceLuid: Self = Self(0); pub const DriverVersion: Self = Self(1); @@ -72,7 +72,7 @@ impl DXCoreAdapterProperty { } #[repr(transparent)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub struct DXCoreAdapterState(pub i32); +pub struct DXCoreAdapterState(pub u32); impl DXCoreAdapterState { pub const IsDriverUpdateInProgress: Self = Self(0); pub const AdapterMemoryBudget: Self = Self(1); @@ -186,7 +186,7 @@ pub struct DXCoreMemoryQueryInput { } #[repr(transparent)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub struct DXCoreMemoryType(pub i32); +pub struct DXCoreMemoryType(pub u32); impl DXCoreMemoryType { pub const Dedicated: Self = Self(0); pub const Shared: Self = Self(1); @@ -199,7 +199,7 @@ pub struct DXCoreMemoryUsage { } #[repr(transparent)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub struct DXCoreNotificationType(pub i32); +pub struct DXCoreNotificationType(pub u32); impl DXCoreNotificationType { pub const AdapterListStale: Self = Self(0); pub const AdapterNoLongerValid: Self = Self(1); @@ -262,14 +262,14 @@ impl core::ops::Not for DXCoreRuntimeFilterFlags { } #[repr(transparent)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub struct DXCoreSegmentGroup(pub i32); +pub struct DXCoreSegmentGroup(pub u32); impl DXCoreSegmentGroup { pub const Local: Self = Self(0); pub const NonLocal: Self = Self(1); } #[repr(transparent)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub struct DXCoreSingleAdapterHybridMode(pub i32); +pub struct DXCoreSingleAdapterHybridMode(pub u32); impl DXCoreSingleAdapterHybridMode { pub const Unspecified: Self = Self(0); pub const MinimumPower: Self = Self(1); @@ -277,7 +277,7 @@ impl DXCoreSingleAdapterHybridMode { } #[repr(transparent)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub struct DXCoreWorkload(pub i32); +pub struct DXCoreWorkload(pub u32); impl DXCoreWorkload { pub const Graphics: Self = Self(0); pub const Compute: Self = Self(1); diff --git a/crates/libs/windows/src/Windows/Win32/wincrypt/mod.rs b/crates/libs/windows/src/Windows/Win32/wincrypt/mod.rs index d92d5fdab13..3d3b415cf4f 100644 --- a/crates/libs/windows/src/Windows/Win32/wincrypt/mod.rs +++ b/crates/libs/windows/src/Windows/Win32/wincrypt/mod.rs @@ -5943,7 +5943,7 @@ pub struct CT_EXTRA_CERT_CHAIN_POLICY_STATUS { pub cValidated: u32, } pub const CUR_BLOB_VERSION: i32 = 2; -pub type CertKeyType = i32; +pub type CertKeyType = u32; pub type DATA_BLOB = CRYPT_INTEGER_BLOB; #[repr(C)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] diff --git a/crates/tests/libs/clang/expected/midl_proxy_stub.rdl b/crates/tests/libs/clang/expected/midl_proxy_stub.rdl index c04f223c88f..8f3b71cb440 100644 --- a/crates/tests/libs/clang/expected/midl_proxy_stub.rdl +++ b/crates/tests/libs/clang/expected/midl_proxy_stub.rdl @@ -1,6 +1,5 @@ #[win32] mod Test { - struct IFoo {} #[library("")] extern fn Register_Stub(id: i32) -> i32; } diff --git a/crates/tests/libs/clang/input/const_dependency.hpp b/crates/tests/libs/clang/input/const_dependency.hpp new file mode 100644 index 00000000000..15471eccb8a --- /dev/null +++ b/crates/tests/libs/clang/input/const_dependency.hpp @@ -0,0 +1,8 @@ +enum class ForwardStatus : unsigned short; + +#include "const_dependency_inc.inl" + +#define STATUS_LITERAL ((IncludedStatus)7) +#define STATUS_CHAINED ((ChainedStatus)9) +#define STATUS_EVALUATED ((IncludedStatus)(1 + 2)) +#define STATUS_FORWARD ((ForwardStatus)11) diff --git a/crates/tests/libs/clang/input/const_dependency_inc.inl b/crates/tests/libs/clang/input/const_dependency_inc.inl new file mode 100644 index 00000000000..4da372025cd --- /dev/null +++ b/crates/tests/libs/clang/input/const_dependency_inc.inl @@ -0,0 +1,2 @@ +typedef unsigned short IncludedStatus; +typedef IncludedStatus ChainedStatus; diff --git a/crates/tests/libs/clang/input/enum_dependency.hpp b/crates/tests/libs/clang/input/enum_dependency.hpp new file mode 100644 index 00000000000..3d1b83824a1 --- /dev/null +++ b/crates/tests/libs/clang/input/enum_dependency.hpp @@ -0,0 +1,9 @@ +enum class IncludedForward : unsigned short; +enum class ForwardOnly : unsigned short; + +#include "enum_dependency_inc.inl" + +IncludedForward ReturnIncludedForward(void); +IncludedEnum ReturnIncludedEnum(void); +TypedefBacked ReturnTypedefBacked(void); +ForwardOnly ReturnForwardOnly(void); diff --git a/crates/tests/libs/clang/input/enum_dependency_inc.inl b/crates/tests/libs/clang/input/enum_dependency_inc.inl new file mode 100644 index 00000000000..65941a02c66 --- /dev/null +++ b/crates/tests/libs/clang/input/enum_dependency_inc.inl @@ -0,0 +1,13 @@ +enum class IncludedForward : unsigned short { + IncludedForwardOne = 1, +}; + +typedef enum IncludedEnum : unsigned int { + IncludedEnumOne = 1, +} IncludedEnum; + +typedef unsigned short EnumStorage; + +enum class TypedefBacked : EnumStorage { + TypedefBackedOne = 1, +}; diff --git a/crates/tests/libs/clang/input/incomplete_class_by_value.hpp b/crates/tests/libs/clang/input/incomplete_class_by_value.hpp new file mode 100644 index 00000000000..4f01e289f54 --- /dev/null +++ b/crates/tests/libs/clang/input/incomplete_class_by_value.hpp @@ -0,0 +1,3 @@ +class IncompleteClass; + +IncompleteClass ReturnIncompleteClass(void); diff --git a/crates/tests/libs/clang/input/incomplete_record_by_value.hpp b/crates/tests/libs/clang/input/incomplete_record_by_value.hpp new file mode 100644 index 00000000000..e9b0b51ef58 --- /dev/null +++ b/crates/tests/libs/clang/input/incomplete_record_by_value.hpp @@ -0,0 +1,3 @@ +struct IncompleteValue; + +IncompleteValue ReturnIncompleteValue(void); diff --git a/crates/tests/libs/clang/input/incomplete_record_dependency.hpp b/crates/tests/libs/clang/input/incomplete_record_dependency.hpp new file mode 100644 index 00000000000..8dc496280a3 --- /dev/null +++ b/crates/tests/libs/clang/input/incomplete_record_dependency.hpp @@ -0,0 +1,6 @@ +#include "incomplete_record_dependency_inc.inl" + +DirectOpaque* ReturnDirectOpaque(void); +const DirectOpaque* ReturnConstDirectOpaque(void); +AliasOpaque* ReturnAliasOpaque(void); +AliasOpaqueChain* ReturnAliasOpaqueChain(void); diff --git a/crates/tests/libs/clang/input/incomplete_record_dependency_inc.inl b/crates/tests/libs/clang/input/incomplete_record_dependency_inc.inl new file mode 100644 index 00000000000..96373b6793e --- /dev/null +++ b/crates/tests/libs/clang/input/incomplete_record_dependency_inc.inl @@ -0,0 +1,3 @@ +struct DirectOpaque; +typedef struct _AliasOpaque AliasOpaque; +typedef AliasOpaque AliasOpaqueChain; diff --git a/crates/tests/libs/clang/input/multi_tu_complete.hpp b/crates/tests/libs/clang/input/multi_tu_complete.hpp new file mode 100644 index 00000000000..530df2d5e15 --- /dev/null +++ b/crates/tests/libs/clang/input/multi_tu_complete.hpp @@ -0,0 +1,3 @@ +typedef struct _MultiValue { + int value; +} MultiValue; diff --git a/crates/tests/libs/clang/input/multi_tu_forward.hpp b/crates/tests/libs/clang/input/multi_tu_forward.hpp new file mode 100644 index 00000000000..86bd225f1a6 --- /dev/null +++ b/crates/tests/libs/clang/input/multi_tu_forward.hpp @@ -0,0 +1,7 @@ +typedef struct _MultiValue MultiValue; + +typedef MultiValue MultiValueAliasInner; +typedef MultiValueAliasInner MultiValueAlias; + +MultiValue* GetMultiValuePointer(void); +MultiValueAlias ReturnMultiValue(void); diff --git a/crates/tests/libs/clang/tests/clang.rs b/crates/tests/libs/clang/tests/clang.rs index 7fb766a0ac6..6a0e6679030 100644 --- a/crates/tests/libs/clang/tests/clang.rs +++ b/crates/tests/libs/clang/tests/clang.rs @@ -530,6 +530,173 @@ fn namespaced_record_dependencies_preserve_layout() { .unwrap(); } +#[test] +fn namespaced_incomplete_pointer_records_are_opaque() { + let scratch = std::path::Path::new(env!("OUT_DIR")).join("incomplete_record_dependency"); + std::fs::create_dir_all(&scratch).unwrap(); + let rdl = scratch.join("out.rdl"); + + { + let _guard = test_clang::libclang_guard(); + windows_clang::clang() + .input("input/incomplete_record_dependency.hpp") + .output(&rdl) + .namespace("IncompleteRecordDependency") + .library("test.dll") + .write() + .unwrap(); + } + + let contents = std::fs::read_to_string(&rdl).unwrap(); + assert!(contents.contains("struct DirectOpaque")); + assert!(contents.contains("struct AliasOpaque")); + assert!(contents.contains("type AliasOpaqueChain = AliasOpaque")); + assert!(contents.contains("fn ReturnConstDirectOpaque() -> *const DirectOpaque")); + windows_rdl::reader() + .input(&rdl) + .output(scratch.join("out.winmd")) + .write() + .unwrap(); +} + +#[test] +fn namespaced_incomplete_records_cannot_be_used_by_value() { + let scratch = std::path::Path::new(env!("OUT_DIR")).join("incomplete_record_by_value"); + std::fs::create_dir_all(&scratch).unwrap(); + + let error = { + let _guard = test_clang::libclang_guard(); + windows_clang::clang() + .input("input/incomplete_record_by_value.hpp") + .output(scratch.join("out.rdl")) + .namespace("IncompleteRecordByValue") + .library("test.dll") + .write() + .unwrap_err() + }; + assert!( + error + .to_string() + .contains("incomplete record used by value") + ); +} + +#[test] +fn namespaced_complete_record_wins_across_translation_units() { + let scratch = std::path::Path::new(env!("OUT_DIR")).join("multi_tu_record"); + std::fs::create_dir_all(&scratch).unwrap(); + let rdl = scratch.join("out.rdl"); + + { + let _guard = test_clang::libclang_guard(); + windows_clang::clang() + .input("input/multi_tu_complete.hpp") + .input("input/multi_tu_forward.hpp") + .output(&rdl) + .namespace("MultiTuRecord") + .library("test.dll") + .symbol("GetMultiValuePointer") + .symbol("ReturnMultiValue") + .write() + .unwrap(); + } + + let contents = std::fs::read_to_string(&rdl).unwrap(); + assert!(contents.contains("struct MultiValue")); + assert!(contents.contains("value: i32")); + assert!(contents.contains("type MultiValueAliasInner = MultiValue")); + assert!(contents.contains("type MultiValueAlias = MultiValueAliasInner")); + assert!(contents.contains("fn GetMultiValuePointer() -> *mut MultiValue")); + assert!(contents.contains("fn ReturnMultiValue() -> MultiValueAlias")); +} + +#[test] +fn namespaced_incomplete_classes_cannot_be_used_by_value() { + let scratch = std::path::Path::new(env!("OUT_DIR")).join("incomplete_class_by_value"); + std::fs::create_dir_all(&scratch).unwrap(); + + let error = { + let _guard = test_clang::libclang_guard(); + windows_clang::clang() + .input("input/incomplete_class_by_value.hpp") + .output(scratch.join("out.rdl")) + .namespace("IncompleteClassByValue") + .library("test.dll") + .write() + .unwrap_err() + }; + assert!( + error + .to_string() + .contains("incomplete record used by value") + ); +} + +#[test] +fn namespaced_enum_dependencies_preserve_definitions() { + let scratch = std::path::Path::new(env!("OUT_DIR")).join("enum_dependency"); + std::fs::create_dir_all(&scratch).unwrap(); + let rdl = scratch.join("out.rdl"); + + { + let _guard = test_clang::libclang_guard(); + windows_clang::clang() + .input("input/enum_dependency.hpp") + .output(&rdl) + .namespace("EnumDependency") + .library("test.dll") + .write() + .unwrap(); + } + + let contents = std::fs::read_to_string(&rdl).unwrap(); + assert!(contents.contains("#[repr(u16)]")); + assert!(contents.contains("enum IncludedForward")); + assert!(contents.contains("IncludedForwardOne = 1")); + assert!(contents.contains("#[repr(u32)]")); + assert!(contents.contains("enum IncludedEnum")); + assert!(contents.contains("IncludedEnumOne = 1")); + assert!(contents.contains("enum TypedefBacked")); + assert!(contents.contains("TypedefBackedOne = 1")); + assert!(contents.contains("type ForwardOnly = u16")); + windows_rdl::reader() + .input(&rdl) + .output(scratch.join("out.winmd")) + .write() + .unwrap(); +} + +#[test] +fn namespaced_constants_retain_type_dependencies() { + let scratch = std::path::Path::new(env!("OUT_DIR")).join("const_dependency"); + std::fs::create_dir_all(&scratch).unwrap(); + let rdl = scratch.join("out.rdl"); + + { + let _guard = test_clang::libclang_guard(); + windows_clang::clang() + .input("input/const_dependency.hpp") + .output(&rdl) + .namespace("ConstDependency") + .write() + .unwrap(); + } + + let contents = std::fs::read_to_string(&rdl).unwrap(); + assert!(contents.contains("type IncludedStatus = u16")); + assert!(contents.contains("type ChainedStatus = u16")); + assert!(contents.contains("type ForwardStatus = u16")); + assert!(contents.contains("STATUS_LITERAL: IncludedStatus = 7")); + assert!(contents.contains("STATUS_CHAINED: ChainedStatus = 9")); + assert!(contents.contains("STATUS_EVALUATED: u32 = 3")); + assert!(contents.contains("STATUS_FORWARD: ForwardStatus = 11")); + windows_rdl::reader() + .input(&rdl) + .output(scratch.join("out.winmd")) + .write() + .unwrap(); +} + fn run(name: &str) { let input_path = format!("input/{name}.h"); let expected_path = format!("expected/{name}.rdl"); diff --git a/docs/crates/windows-clang.md b/docs/crates/windows-clang.md index 261011526e4..40328d38df1 100644 --- a/docs/crates/windows-clang.md +++ b/docs/crates/windows-clang.md @@ -156,10 +156,10 @@ The scraper preserves: - `DEFINE_ENUM_FLAG_OPERATORS` as a flags-enum signal; - symbol-to-DLL mappings recovered from import libraries. -Namespaced scrapes follow referenced record definitions from included headers. Available layouts -are emitted for both by-value and pointer dependencies; they are not replaced with opaque records. -Per-header scrapes discover the same definitions globally and retain them in their owning header -partition. +Namespaced scrapes follow referenced typedef, record, and enum declarations from included headers, +including types referenced only by constants. Available layouts and enum definitions retain their +source representation. Genuinely incomplete pointer-only records are emitted as opaque records; +using one by value is an error. Per-header scrapes retain definitions in their owning partition. Some C portability spellings are canonicalized for metadata consumers. Examples include fixed-width integer typedefs, pointer-sized integer typedefs, Windows string wrappers, COM interface aliases, diff --git a/metadata/win32/d3dkmdt.rdl b/metadata/win32/d3dkmdt.rdl index 7d2fb63c00b..aad0fbdc669 100644 --- a/metadata/win32/d3dkmdt.rdl +++ b/metadata/win32/d3dkmdt.rdl @@ -1183,7 +1183,7 @@ mod Windows { DXGK_DISPLAYMUX_DRIVER_SUPPORT_LEVEL_EXPERIMENTAL = 3, DXGK_DISPLAYMUX_DRIVER_SUPPORT_LEVEL_FULL = 4, } - #[repr(i32)] + #[repr(u8)] enum DXGK_DISPLAY_DESCRIPTOR_TYPE { DXGK_DDT_INVALID = 0, DXGK_DDT_EDID = 1, @@ -1198,7 +1198,7 @@ mod Windows { TargetId: D3DDDI_VIDEO_PRESENT_TARGET_ID, AcpiId: u32, } - #[repr(i32)] + #[repr(u8)] enum DXGK_DISPLAY_TECHNOLOGY { DXGK_DT_INVALID = 0, DXGK_DT_OTHER = 1, @@ -1207,7 +1207,7 @@ mod Windows { DXGK_DT_PROJECTOR = 4, DXGK_DT_MAX = 5, } - #[repr(i32)] + #[repr(u8)] enum DXGK_DISPLAY_USAGE { DXGK_DU_INVALID = 0, DXGK_DU_GENERIC = 1, diff --git a/metadata/win32/dwrite_3.rdl b/metadata/win32/dwrite_3.rdl index 74a9a7de500..a7948b2aea8 100644 --- a/metadata/win32/dwrite_3.rdl +++ b/metadata/win32/dwrite_3.rdl @@ -70,7 +70,7 @@ mod Windows { minValue: f32, maxValue: f32, } - #[repr(i32)] + #[repr(u32)] enum DWRITE_FONT_AXIS_TAG { DWRITE_FONT_AXIS_TAG_WEIGHT = 1952999287, DWRITE_FONT_AXIS_TAG_WIDTH = 1752458359, diff --git a/metadata/win32/dxcore_interface.rdl b/metadata/win32/dxcore_interface.rdl index 7513c491fc7..129b690ec25 100644 --- a/metadata/win32/dxcore_interface.rdl +++ b/metadata/win32/dxcore_interface.rdl @@ -24,7 +24,7 @@ mod Windows { nodeIndex: u32, segmentGroup: DXCoreSegmentGroup, } - #[repr(i32)] + #[repr(u32)] #[scoped] enum DXCoreAdapterPreference { Hardware = 0, @@ -39,7 +39,7 @@ mod Windows { processesWritten: u32, processesTotal: u32, } - #[repr(i32)] + #[repr(u32)] #[scoped] enum DXCoreAdapterProperty { InstanceLuid = 0, @@ -61,7 +61,7 @@ mod Windows { AdapterEngineCount = 16, AdapterEngineName = 17, } - #[repr(i32)] + #[repr(u32)] #[scoped] enum DXCoreAdapterState { IsDriverUpdateInProgress = 0, @@ -125,7 +125,7 @@ mod Windows { physicalAdapterIndex: u32, memoryType: DXCoreMemoryType, } - #[repr(i32)] + #[repr(u32)] #[scoped] enum DXCoreMemoryType { Dedicated = 0, @@ -135,7 +135,7 @@ mod Windows { committed: u64, resident: u64, } - #[repr(i32)] + #[repr(u32)] #[scoped] enum DXCoreNotificationType { AdapterListStale = 0, @@ -160,20 +160,20 @@ mod Windows { D3D11 = 1, D3D12 = 2, } - #[repr(i32)] + #[repr(u32)] #[scoped] enum DXCoreSegmentGroup { Local = 0, NonLocal = 1, } - #[repr(i32)] + #[repr(u32)] #[scoped] enum DXCoreSingleAdapterHybridMode { Unspecified = 0, MinimumPower = 1, HighPerformance = 2, } - #[repr(i32)] + #[repr(u32)] #[scoped] enum DXCoreWorkload { Graphics = 0, diff --git a/metadata/win32/wincrypt.rdl b/metadata/win32/wincrypt.rdl index 2c680113099..59ea06c8e93 100644 --- a/metadata/win32/wincrypt.rdl +++ b/metadata/win32/wincrypt.rdl @@ -3619,7 +3619,7 @@ mod Windows { extern fn CertIsValidCRLForCertificate(pCert: *const CERT_CONTEXT, pCrl: *const CRL_CONTEXT, dwFlags: u32, #[reserved] pvReserved: *const void) -> BOOL; #[library("CRYPT32.dll")] extern fn CertIsWeakHash(dwHashUseType: u32, pwszCNGHashAlgid: PCWSTR, dwChainFlags: u32, #[opt] pSignerChainContext: *const CERT_CHAIN_CONTEXT, #[opt] pTimeStamp: *const FILETIME, #[opt] pwszFileName: PCWSTR) -> BOOL; - #[repr(i32)] + #[repr(u32)] enum CertKeyType { KeyTypeOther = 0, KeyTypeVirtualSmartCard = 1,