diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs index 5b7828a2699..83286cf6b77 100644 --- a/crates/base-db/src/fixture.rs +++ b/crates/base-db/src/fixture.rs @@ -162,6 +162,7 @@ impl ChangeFixture { Ok(Vec::new()), false, origin, + meta.target_data_layout.as_deref().map(Arc::from), ); let prev = crates.insert(crate_name.clone(), crate_id); assert!(prev.is_none()); @@ -197,6 +198,7 @@ impl ChangeFixture { Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, + None, ); } else { for (from, to, prelude) in crate_deps { @@ -210,6 +212,8 @@ impl ChangeFixture { .unwrap(); } } + let target_layout = + crate_graph.iter().next().and_then(|it| crate_graph[it].target_layout.clone()); if let Some(mini_core) = mini_core { let core_file = file_id; @@ -234,6 +238,7 @@ impl ChangeFixture { Ok(Vec::new()), false, CrateOrigin::Lang(LangCrateOrigin::Core), + target_layout.clone(), ); for krate in all_crates { @@ -271,6 +276,7 @@ impl ChangeFixture { Ok(proc_macro), true, CrateOrigin::CratesIo { repo: None, name: None }, + target_layout, ); for krate in all_crates { @@ -391,6 +397,7 @@ struct FileMeta { edition: Edition, env: Env, introduce_new_source_root: Option, + target_data_layout: Option, } fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option) { @@ -434,6 +441,7 @@ impl From for FileMeta { "library" => SourceRootKind::Library, invalid => panic!("invalid source root kind '{}'", invalid), }), + target_data_layout: f.target_data_layout, } } } diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index e7f0c4ec29b..7aff9b1ae84 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -270,6 +270,7 @@ pub struct CrateData { pub display_name: Option, pub cfg_options: CfgOptions, pub potential_cfg_options: CfgOptions, + pub target_layout: Option>, pub env: Env, pub dependencies: Vec, pub proc_macro: ProcMacroLoadResult, @@ -328,6 +329,7 @@ impl CrateGraph { proc_macro: ProcMacroLoadResult, is_proc_macro: bool, origin: CrateOrigin, + target_layout: Option>, ) -> CrateId { let data = CrateData { root_file_id, @@ -340,6 +342,7 @@ impl CrateGraph { proc_macro, dependencies: Vec::new(), origin, + target_layout, is_proc_macro, }; let crate_id = CrateId(self.arena.len() as u32); @@ -649,6 +652,7 @@ mod tests { Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, + None, ); let crate2 = graph.add_crate_root( FileId(2u32), @@ -661,6 +665,7 @@ mod tests { Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, + None, ); let crate3 = graph.add_crate_root( FileId(3u32), @@ -673,6 +678,7 @@ mod tests { Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, + None, ); assert!(graph .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) @@ -699,6 +705,7 @@ mod tests { Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, + None, ); let crate2 = graph.add_crate_root( FileId(2u32), @@ -711,6 +718,7 @@ mod tests { Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, + None, ); assert!(graph .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) @@ -734,6 +742,7 @@ mod tests { Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, + None, ); let crate2 = graph.add_crate_root( FileId(2u32), @@ -746,6 +755,7 @@ mod tests { Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, + None, ); let crate3 = graph.add_crate_root( FileId(3u32), @@ -758,6 +768,7 @@ mod tests { Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, + None, ); assert!(graph .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) @@ -781,6 +792,7 @@ mod tests { Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, + None, ); let crate2 = graph.add_crate_root( FileId(2u32), @@ -793,6 +805,7 @@ mod tests { Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, + None, ); assert!(graph .add_dep( diff --git a/crates/hir-def/src/layout.rs b/crates/hir-def/src/layout.rs index a427c464bc0..6bb4cd94f8a 100644 --- a/crates/hir-def/src/layout.rs +++ b/crates/hir-def/src/layout.rs @@ -6,7 +6,7 @@ use la_arena::{Idx, RawIdx}; pub use rustc_abi::{ Abi, AbiAndPrefAlign, AddressSpace, Align, Endian, FieldsShape, Integer, IntegerType, LayoutCalculator, Niche, Primitive, ReprFlags, ReprOptions, Scalar, Size, StructKind, - TargetDataLayout, WrappingRange, + TargetDataLayout, TargetDataLayoutErrors, WrappingRange, }; use crate::LocalEnumVariantId; diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 23ad335aac7..54b244620fb 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -64,8 +64,8 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::cycle(crate::layout::layout_of_adt_recover)] fn layout_of_adt(&self, def: AdtId, subst: Substitution) -> Result; - #[salsa::invoke(crate::layout::current_target_data_layout_query)] - fn current_target_data_layout(&self) -> Arc; + #[salsa::invoke(crate::layout::target_data_layout_query)] + fn target_data_layout(&self, krate: CrateId) -> Arc; #[salsa::invoke(crate::lower::callable_item_sig)] fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig; diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index f4f3c723ab7..209072176c4 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -2,6 +2,7 @@ use std::sync::Arc; +use base_db::CrateId; use chalk_ir::{AdtId, TyKind}; use hir_def::{ layout::{ @@ -17,7 +18,7 @@ use crate::{db::HirDatabase, Interner, Substitution, Ty}; use self::adt::struct_variant_idx; pub use self::{ adt::{layout_of_adt_query, layout_of_adt_recover}, - target::current_target_data_layout_query, + target::target_data_layout_query, }; macro_rules! user_error { @@ -31,6 +32,7 @@ mod target; struct LayoutCx<'a> { db: &'a dyn HirDatabase, + krate: CrateId, } impl LayoutCalculator for LayoutCx<'_> { @@ -41,7 +43,7 @@ impl LayoutCalculator for LayoutCx<'_> { } fn current_data_layout(&self) -> Arc { - self.db.current_target_data_layout() + self.db.target_data_layout(self.krate) } } @@ -53,9 +55,9 @@ fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout { Layout::scalar(dl, scalar_unit(dl, value)) } -pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty) -> Result { - let dl = &*db.current_target_data_layout(); - let cx = LayoutCx { db }; +pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result { + let cx = LayoutCx { db, krate }; + let dl = &*cx.current_data_layout(); Ok(match ty.kind(Interner) { TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?, TyKind::Scalar(s) => match s { @@ -84,7 +86,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty) -> Result Integer::I64, chalk_ir::IntTy::I128 => Integer::I128, }, - false, + true, ), ), chalk_ir::Scalar::Uint(i) => scalar( @@ -98,7 +100,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty) -> Result Integer::I64, chalk_ir::UintTy::U128 => Integer::I128, }, - true, + false, ), ), chalk_ir::Scalar::Float(f) => scalar( @@ -114,7 +116,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty) -> Result, _>>()?; let fields = fields.iter().collect::>(); let fields = fields.iter().collect::>(); @@ -132,7 +134,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty) -> Result return Err(LayoutError::HasPlaceholder), }; - let element = layout_of_ty(db, element)?; + let element = layout_of_ty(db, element, krate)?; let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?; let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) { @@ -153,7 +155,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty) -> Result { - let element = layout_of_ty(db, element)?; + let element = layout_of_ty(db, element, krate)?; Layout { variants: Variants::Single { index: struct_variant_idx() }, fields: FieldsShape::Array { stride: element.size, count: 0 }, diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index e9382955356..23166a5a522 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -5,7 +5,7 @@ use std::ops::Bound; use hir_def::{ adt::VariantData, layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx}, - AdtId, EnumVariantId, LocalEnumVariantId, VariantId, + AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId, }; use la_arena::RawIdx; use smallvec::SmallVec; @@ -23,12 +23,12 @@ pub fn layout_of_adt_query( def: AdtId, subst: Substitution, ) -> Result { - let dl = db.current_target_data_layout(); - let cx = LayoutCx { db }; + let cx = LayoutCx { db, krate: def.module(db.upcast()).krate() }; + let dl = cx.current_data_layout(); let handle_variant = |def: VariantId, var: &VariantData| { var.fields() .iter() - .map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst))) + .map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst), cx.krate)) .collect::, _>>() }; let (variants, is_enum, is_union, repr) = match def { diff --git a/crates/hir-ty/src/layout/target.rs b/crates/hir-ty/src/layout/target.rs index b248031f152..54fa0fd9ea5 100644 --- a/crates/hir-ty/src/layout/target.rs +++ b/crates/hir-ty/src/layout/target.rs @@ -2,45 +2,130 @@ use std::sync::Arc; -use hir_def::layout::TargetDataLayout; +use base_db::CrateId; +use hir_def::layout::{TargetDataLayout, TargetDataLayoutErrors}; use crate::db::HirDatabase; -use hir_def::layout::{AbiAndPrefAlign, AddressSpace, Align, Endian, Integer, Size}; +use hir_def::layout::{AbiAndPrefAlign, AddressSpace, Align, Endian, Size}; -pub fn current_target_data_layout_query(db: &dyn HirDatabase) -> Arc { +pub fn target_data_layout_query(db: &dyn HirDatabase, krate: CrateId) -> Arc { let crate_graph = db.crate_graph(); - let cfg_options = &crate_graph[crate_graph.iter().next().unwrap()].cfg_options; - let endian = match cfg_options.get_cfg_values("target_endian").next() { - Some(x) if x.as_str() == "big" => Endian::Big, - _ => Endian::Little, - }; - let pointer_size = - Size::from_bytes(match cfg_options.get_cfg_values("target_pointer_width").next() { - Some(x) => match x.as_str() { - "16" => 2, - "32" => 4, - _ => 8, - }, - _ => 8, - }); - // FIXME: These values are incorrect for many architectures, at least for aarch64 and riscv64, - // use `rustc +nightly -Z unstable-options --print target-spec-json` or something similar instead. - Arc::new(TargetDataLayout { - endian, - i1_align: AbiAndPrefAlign::new(Align::from_bytes(1).unwrap()), - i8_align: AbiAndPrefAlign::new(Align::from_bytes(1).unwrap()), - i16_align: AbiAndPrefAlign::new(Align::from_bytes(2).unwrap()), - i32_align: AbiAndPrefAlign::new(Align::from_bytes(4).unwrap()), - i64_align: AbiAndPrefAlign::new(Align::from_bytes(8).unwrap()), - i128_align: AbiAndPrefAlign::new(Align::from_bytes(8).unwrap()), - f32_align: AbiAndPrefAlign::new(Align::from_bytes(4).unwrap()), - f64_align: AbiAndPrefAlign::new(Align::from_bytes(8).unwrap()), - pointer_size, - pointer_align: AbiAndPrefAlign::new(Align::from_bytes(pointer_size.bytes()).unwrap()), - aggregate_align: AbiAndPrefAlign::new(Align::from_bytes(1).unwrap()), - vector_align: vec![], - instruction_address_space: AddressSpace(0), - c_enum_min_size: Integer::I32, - }) + let target_layout = &crate_graph[krate].target_layout; + let cfg_options = &crate_graph[krate].cfg_options; + Arc::new( + target_layout + .as_ref() + .and_then(|it| parse_from_llvm_datalayout_string(it).ok()) + .unwrap_or_else(|| { + let endian = match cfg_options.get_cfg_values("target_endian").next() { + Some(x) if x.as_str() == "big" => Endian::Big, + _ => Endian::Little, + }; + let pointer_size = Size::from_bytes( + match cfg_options.get_cfg_values("target_pointer_width").next() { + Some(x) => match x.as_str() { + "16" => 2, + "32" => 4, + _ => 8, + }, + _ => 8, + }, + ); + TargetDataLayout { endian, pointer_size, ..TargetDataLayout::default() } + }), + ) +} + +/// copied from rustc as it is not exposed yet +fn parse_from_llvm_datalayout_string<'a>( + input: &'a str, +) -> Result> { + // Parse an address space index from a string. + let parse_address_space = |s: &'a str, cause: &'a str| { + s.parse::().map(AddressSpace).map_err(|err| { + TargetDataLayoutErrors::InvalidAddressSpace { addr_space: s, cause, err } + }) + }; + + // Parse a bit count from a string. + let parse_bits = |s: &'a str, kind: &'a str, cause: &'a str| { + s.parse::().map_err(|err| TargetDataLayoutErrors::InvalidBits { + kind, + bit: s, + cause, + err, + }) + }; + + // Parse a size string. + let size = |s: &'a str, cause: &'a str| parse_bits(s, "size", cause).map(Size::from_bits); + + // Parse an alignment string. + let align = |s: &[&'a str], cause: &'a str| { + if s.is_empty() { + return Err(TargetDataLayoutErrors::MissingAlignment { cause }); + } + let align_from_bits = |bits| { + Align::from_bits(bits) + .map_err(|err| TargetDataLayoutErrors::InvalidAlignment { cause, err }) + }; + let abi = parse_bits(s[0], "alignment", cause)?; + let pref = s.get(1).map_or(Ok(abi), |pref| parse_bits(pref, "alignment", cause))?; + Ok(AbiAndPrefAlign { abi: align_from_bits(abi)?, pref: align_from_bits(pref)? }) + }; + + let mut dl = TargetDataLayout::default(); + let mut i128_align_src = 64; + for spec in input.split('-') { + let spec_parts = spec.split(':').collect::>(); + + match &*spec_parts { + ["e"] => dl.endian = Endian::Little, + ["E"] => dl.endian = Endian::Big, + [p] if p.starts_with('P') => { + dl.instruction_address_space = parse_address_space(&p[1..], "P")? + } + ["a", ref a @ ..] => dl.aggregate_align = align(a, "a")?, + ["f32", ref a @ ..] => dl.f32_align = align(a, "f32")?, + ["f64", ref a @ ..] => dl.f64_align = align(a, "f64")?, + [p @ "p", s, ref a @ ..] | [p @ "p0", s, ref a @ ..] => { + dl.pointer_size = size(s, p)?; + dl.pointer_align = align(a, p)?; + } + [s, ref a @ ..] if s.starts_with('i') => { + let Ok(bits) = s[1..].parse::() else { + size(&s[1..], "i")?; // For the user error. + continue; + }; + let a = align(a, s)?; + match bits { + 1 => dl.i1_align = a, + 8 => dl.i8_align = a, + 16 => dl.i16_align = a, + 32 => dl.i32_align = a, + 64 => dl.i64_align = a, + _ => {} + } + if bits >= i128_align_src && bits <= 128 { + // Default alignment for i128 is decided by taking the alignment of + // largest-sized i{64..=128}. + i128_align_src = bits; + dl.i128_align = a; + } + } + [s, ref a @ ..] if s.starts_with('v') => { + let v_size = size(&s[1..], "v")?; + let a = align(a, s)?; + if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) { + v.1 = a; + continue; + } + // No existing entry, add a new one. + dl.vector_align.push((v_size, a)); + } + _ => {} // Ignore everything else. + } + } + Ok(dl) } diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index 5d97a69501f..ba821235f3f 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -9,8 +9,22 @@ use crate::{test_db::TestDB, Interner, Substitution}; use super::layout_of_ty; -fn eval_goal(ra_fixture: &str) -> Result { - let (db, file_id) = TestDB::with_single_file(ra_fixture); +fn eval_goal(ra_fixture: &str, minicore: &str) -> Result { + // using unstable cargo features failed, fall back to using plain rustc + let mut cmd = std::process::Command::new("rustc"); + cmd.args(&["-Z", "unstable-options", "--print", "target-spec-json"]) + .env("RUSTC_BOOTSTRAP", "1"); + let output = cmd.output().unwrap(); + assert!(output.status.success(), "{}", output.status); + let stdout = String::from_utf8(output.stdout).unwrap(); + let target_data_layout = + stdout.split_once(r#""data-layout": ""#).unwrap().1.split_once('"').unwrap().0.to_owned(); + + let ra_fixture = format!( + "{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\n{ra_fixture}", + ); + + let (db, file_id) = TestDB::with_single_file(&ra_fixture); let module_id = db.module_for_file(file_id); let def_map = module_id.def_map(&db); let scope = &def_map[module_id.local_id].scope; @@ -20,31 +34,29 @@ fn eval_goal(ra_fixture: &str) -> Result { .find_map(|x| match x { hir_def::ModuleDefId::AdtId(x) => { let name = match x { - hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_string(), - hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_string(), - hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_string(), + hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_smol_str(), + hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_smol_str(), + hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_smol_str(), }; - if name == "Goal" { - Some(x) - } else { - None - } + (name == "Goal").then(|| x) } _ => None, }) .unwrap(); let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner); - layout_of_ty(&db, &goal_ty) + layout_of_ty(&db, &goal_ty, module_id.krate()) } -fn check_size_and_align(ra_fixture: &str, size: u64, align: u64) { - let l = eval_goal(ra_fixture).unwrap(); +#[track_caller] +fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64) { + let l = eval_goal(ra_fixture, minicore).unwrap(); assert_eq!(l.size.bytes(), size); assert_eq!(l.align.abi.bytes(), align); } +#[track_caller] fn check_fail(ra_fixture: &str, e: LayoutError) { - let r = eval_goal(ra_fixture); + let r = eval_goal(ra_fixture, ""); assert_eq!(r, Err(e)); } @@ -54,7 +66,8 @@ macro_rules! size_and_align { #[allow(dead_code)] $($t)* check_size_and_align( - &format!("//- minicore: {}\n{}", stringify!($($x),*), stringify!($($t)*)), + stringify!($($t)*), + &format!("//- minicore: {}\n", stringify!($($x),*)), ::std::mem::size_of::() as u64, ::std::mem::align_of::() as u64, ); @@ -66,6 +79,7 @@ macro_rules! size_and_align { $($t)* check_size_and_align( stringify!($($t)*), + "", ::std::mem::size_of::() as u64, ::std::mem::align_of::() as u64, ); diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index b841e580b6c..cc983ee0142 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -866,7 +866,7 @@ impl Field { } pub fn layout(&self, db: &dyn HirDatabase) -> Result { - layout_of_ty(db, &self.ty(db).ty) + layout_of_ty(db, &self.ty(db).ty, self.parent.module(db).krate().into()) } pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 7402e86f36f..6698bf766a0 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -236,6 +236,7 @@ impl Analysis { Ok(Vec::new()), false, CrateOrigin::CratesIo { repo: None, name: None }, + None, ); change.change_file(file_id, Some(Arc::new(text))); change.set_crate_graph(crate_graph); diff --git a/crates/ide/src/shuffle_crate_graph.rs b/crates/ide/src/shuffle_crate_graph.rs index 2d86627643d..ae539a5d397 100644 --- a/crates/ide/src/shuffle_crate_graph.rs +++ b/crates/ide/src/shuffle_crate_graph.rs @@ -36,6 +36,7 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { data.proc_macro.clone(), data.is_proc_macro, data.origin.clone(), + data.target_layout.clone(), ); map.insert(old_id, new_id); } diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index 575581fa543..835f2b3dd03 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -25,6 +25,7 @@ mod sysroot; mod workspace; mod rustc_cfg; mod build_scripts; +mod target_data_layout; #[cfg(test)] mod tests; diff --git a/crates/project-model/src/rustc_cfg.rs b/crates/project-model/src/rustc_cfg.rs index 32313618366..12c8cf70dc0 100644 --- a/crates/project-model/src/rustc_cfg.rs +++ b/crates/project-model/src/rustc_cfg.rs @@ -50,7 +50,7 @@ fn get_rust_cfgs( cargo_config.envs(extra_env); cargo_config .current_dir(cargo_toml.parent()) - .args(&["-Z", "unstable-options", "rustc", "--print", "cfg"]) + .args(&["rustc", "-Z", "unstable-options", "--print", "cfg"]) .env("RUSTC_BOOTSTRAP", "1"); if let Some(target) = target { cargo_config.args(&["--target", target]); diff --git a/crates/project-model/src/target_data_layout.rs b/crates/project-model/src/target_data_layout.rs new file mode 100644 index 00000000000..b9d7d2338c3 --- /dev/null +++ b/crates/project-model/src/target_data_layout.rs @@ -0,0 +1,40 @@ +//! Runs `rustc --print target-spec-json` to get the target_data_layout. +use std::process::Command; + +use rustc_hash::FxHashMap; + +use crate::{utf8_stdout, ManifestPath}; + +pub(super) fn get( + cargo_toml: Option<&ManifestPath>, + target: Option<&str>, + extra_env: &FxHashMap, +) -> Option { + let output = (|| { + if let Some(cargo_toml) = cargo_toml { + let mut cmd = Command::new(toolchain::rustc()); + cmd.envs(extra_env); + cmd.current_dir(cargo_toml.parent()) + .args(&["-Z", "unstable-options", "rustc", "--print", "target-spec-json"]) + .env("RUSTC_BOOTSTRAP", "1"); + if let Some(target) = target { + cmd.args(&["--target", target]); + } + match utf8_stdout(cmd) { + Ok(it) => return Ok(it), + Err(e) => tracing::debug!("{e:?}: falling back to querying rustc for cfgs"), + } + } + // using unstable cargo features failed, fall back to using plain rustc + let mut cmd = Command::new(toolchain::rustc()); + cmd.envs(extra_env) + .args(&["-Z", "unstable-options", "rustc", "--print", "target-spec-json"]) + .env("RUSTC_BOOTSTRAP", "1"); + if let Some(target) = target { + cmd.args(&["--target", target]); + } + utf8_stdout(cmd) + })() + .ok()?; + Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()) +} diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index a1cb438bddc..adb106e9793 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -29,6 +29,7 @@ fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> CrateGr rustc_cfg: Vec::new(), cfg_overrides, toolchain: None, + target_layout: None, }; to_crate_graph(project_workspace) } @@ -150,6 +151,7 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { "debug_assertions", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -219,6 +221,7 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { "debug_assertions", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -297,6 +300,7 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { "debug_assertions", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -375,6 +379,7 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { "debug_assertions", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -462,6 +467,7 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { "feature=use_std", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -547,6 +553,7 @@ fn cargo_hello_world_project_model_with_selective_overrides() { "test", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -618,6 +625,7 @@ fn cargo_hello_world_project_model_with_selective_overrides() { "test", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -698,6 +706,7 @@ fn cargo_hello_world_project_model_with_selective_overrides() { "test", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -778,6 +787,7 @@ fn cargo_hello_world_project_model_with_selective_overrides() { "test", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -865,6 +875,7 @@ fn cargo_hello_world_project_model_with_selective_overrides() { "feature=use_std", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -941,6 +952,7 @@ fn cargo_hello_world_project_model() { "test", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -1012,6 +1024,7 @@ fn cargo_hello_world_project_model() { "test", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -1092,6 +1105,7 @@ fn cargo_hello_world_project_model() { "test", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -1172,6 +1186,7 @@ fn cargo_hello_world_project_model() { "test", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -1259,6 +1274,7 @@ fn cargo_hello_world_project_model() { "feature=use_std", ], ), + target_layout: None, env: Env { entries: { "CARGO_PKG_LICENSE": "", @@ -1327,6 +1343,7 @@ fn rust_project_hello_world_project_model() { potential_cfg_options: CfgOptions( [], ), + target_layout: None, env: Env { entries: {}, }, @@ -1371,6 +1388,7 @@ fn rust_project_hello_world_project_model() { potential_cfg_options: CfgOptions( [], ), + target_layout: None, env: Env { entries: {}, }, @@ -1405,6 +1423,7 @@ fn rust_project_hello_world_project_model() { potential_cfg_options: CfgOptions( [], ), + target_layout: None, env: Env { entries: {}, }, @@ -1439,6 +1458,7 @@ fn rust_project_hello_world_project_model() { potential_cfg_options: CfgOptions( [], ), + target_layout: None, env: Env { entries: {}, }, @@ -1473,6 +1493,7 @@ fn rust_project_hello_world_project_model() { potential_cfg_options: CfgOptions( [], ), + target_layout: None, env: Env { entries: {}, }, @@ -1517,6 +1538,7 @@ fn rust_project_hello_world_project_model() { potential_cfg_options: CfgOptions( [], ), + target_layout: None, env: Env { entries: {}, }, @@ -1551,6 +1573,7 @@ fn rust_project_hello_world_project_model() { potential_cfg_options: CfgOptions( [], ), + target_layout: None, env: Env { entries: {}, }, @@ -1658,6 +1681,7 @@ fn rust_project_hello_world_project_model() { potential_cfg_options: CfgOptions( [], ), + target_layout: None, env: Env { entries: {}, }, @@ -1692,6 +1716,7 @@ fn rust_project_hello_world_project_model() { potential_cfg_options: CfgOptions( [], ), + target_layout: None, env: Env { entries: {}, }, @@ -1726,6 +1751,7 @@ fn rust_project_hello_world_project_model() { potential_cfg_options: CfgOptions( [], ), + target_layout: None, env: Env { entries: {}, }, @@ -1760,6 +1786,7 @@ fn rust_project_hello_world_project_model() { potential_cfg_options: CfgOptions( [], ), + target_layout: None, env: Env { entries: {}, }, diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index aa54342e39d..52ac3b6dc02 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -21,8 +21,8 @@ use crate::{ cfg_flag::CfgFlag, rustc_cfg, sysroot::SysrootCrate, - utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath, Package, - ProjectJson, ProjectManifest, Sysroot, TargetKind, WorkspaceBuildScripts, + target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath, + Package, ProjectJson, ProjectManifest, Sysroot, TargetKind, WorkspaceBuildScripts, }; /// A set of cfg-overrides per crate. @@ -79,6 +79,7 @@ pub enum ProjectWorkspace { rustc_cfg: Vec, cfg_overrides: CfgOverrides, toolchain: Option, + target_layout: Option, }, /// Project workspace was manually specified using a `rust-project.json` file. Json { project: ProjectJson, sysroot: Option, rustc_cfg: Vec }, @@ -108,6 +109,7 @@ impl fmt::Debug for ProjectWorkspace { rustc_cfg, cfg_overrides, toolchain, + target_layout: data_layout, } => f .debug_struct("Cargo") .field("root", &cargo.workspace_root().file_name()) @@ -120,6 +122,7 @@ impl fmt::Debug for ProjectWorkspace { .field("n_rustc_cfg", &rustc_cfg.len()) .field("n_cfg_overrides", &cfg_overrides.len()) .field("toolchain", &toolchain) + .field("data_layout", &data_layout) .finish(), ProjectWorkspace::Json { project, sysroot, rustc_cfg } => { let mut debug_struct = f.debug_struct("Json"); @@ -241,6 +244,11 @@ impl ProjectWorkspace { rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env); let cfg_overrides = config.cfg_overrides(); + let data_layout = target_data_layout::get( + Some(&cargo_toml), + config.target.as_deref(), + &config.extra_env, + ); ProjectWorkspace::Cargo { cargo, build_scripts: WorkspaceBuildScripts::default(), @@ -249,6 +257,7 @@ impl ProjectWorkspace { rustc_cfg, cfg_overrides, toolchain, + target_layout: data_layout, } } }; @@ -435,6 +444,7 @@ impl ProjectWorkspace { cfg_overrides: _, build_scripts, toolchain: _, + target_layout: _, } => { cargo .packages() @@ -530,6 +540,7 @@ impl ProjectWorkspace { project, sysroot, extra_env, + None, ), ProjectWorkspace::Cargo { cargo, @@ -539,6 +550,7 @@ impl ProjectWorkspace { cfg_overrides, build_scripts, toolchain: _, + target_layout, } => cargo_to_crate_graph( load_proc_macro, load, @@ -548,9 +560,10 @@ impl ProjectWorkspace { rustc_cfg.clone(), cfg_overrides, build_scripts, + target_layout.as_deref().map(Arc::from), ), ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => { - detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot) + detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot, None) } }; if crate_graph.patch_cfg_if() { @@ -569,11 +582,18 @@ fn project_json_to_crate_graph( project: &ProjectJson, sysroot: &Option, extra_env: &FxHashMap, + target_layout: Option>, ) -> CrateGraph { let mut crate_graph = CrateGraph::default(); - let sysroot_deps = sysroot - .as_ref() - .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load)); + let sysroot_deps = sysroot.as_ref().map(|sysroot| { + sysroot_to_crate_graph( + &mut crate_graph, + sysroot, + rustc_cfg.clone(), + target_layout.clone(), + load, + ) + }); let mut cfg_cache: FxHashMap<&str, Vec> = FxHashMap::default(); let crates: NoHashHashMap = project @@ -625,6 +645,7 @@ fn project_json_to_crate_graph( } else { CrateOrigin::CratesIo { repo: None, name: None } }, + target_layout.clone(), ), ) }) @@ -665,11 +686,18 @@ fn cargo_to_crate_graph( rustc_cfg: Vec, override_cfg: &CfgOverrides, build_scripts: &WorkspaceBuildScripts, + target_layout: Option>, ) -> CrateGraph { let _p = profile::span("cargo_to_crate_graph"); let mut crate_graph = CrateGraph::default(); let (public_deps, libproc_macro) = match sysroot { - Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load), + Some(sysroot) => sysroot_to_crate_graph( + &mut crate_graph, + sysroot, + rustc_cfg.clone(), + target_layout.clone(), + load, + ), None => (SysrootPublicDeps::default(), None), }; @@ -732,6 +760,7 @@ fn cargo_to_crate_graph( file_id, &cargo[tgt].name, cargo[tgt].is_proc_macro, + target_layout.clone(), ); if cargo[tgt].kind == TargetKind::Lib { lib_tgt = Some((crate_id, cargo[tgt].name.clone())); @@ -811,6 +840,7 @@ fn cargo_to_crate_graph( &cfg_options, override_cfg, build_scripts, + target_layout, ); } } @@ -822,11 +852,18 @@ fn detached_files_to_crate_graph( load: &mut dyn FnMut(&AbsPath) -> Option, detached_files: &[AbsPathBuf], sysroot: &Option, + target_layout: Option>, ) -> CrateGraph { let _p = profile::span("detached_files_to_crate_graph"); let mut crate_graph = CrateGraph::default(); let (public_deps, _libproc_macro) = match sysroot { - Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load), + Some(sysroot) => sysroot_to_crate_graph( + &mut crate_graph, + sysroot, + rustc_cfg.clone(), + target_layout.clone(), + load, + ), None => (SysrootPublicDeps::default(), None), }; @@ -859,6 +896,7 @@ fn detached_files_to_crate_graph( repo: None, name: display_name.map(|n| n.canonical_name().to_string()), }, + target_layout.clone(), ); public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate); @@ -879,6 +917,7 @@ fn handle_rustc_crates( cfg_options: &CfgOptions, override_cfg: &CfgOverrides, build_scripts: &WorkspaceBuildScripts, + target_layout: Option>, ) { let mut rustc_pkg_crates = FxHashMap::default(); // The root package of the rustc-dev component is rustc_driver, so we match that @@ -935,6 +974,7 @@ fn handle_rustc_crates( file_id, &rustc_workspace[tgt].name, rustc_workspace[tgt].is_proc_macro, + target_layout.clone(), ); pkg_to_lib_crate.insert(pkg, crate_id); // Add dependencies on core / std / alloc for this crate @@ -999,6 +1039,7 @@ fn add_target_crate_root( file_id: FileId, cargo_name: &str, is_proc_macro: bool, + target_layout: Option>, ) -> CrateId { let edition = pkg.edition; let mut potential_cfg_options = cfg_options.clone(); @@ -1045,6 +1086,7 @@ fn add_target_crate_root( proc_macro, is_proc_macro, CrateOrigin::CratesIo { repo: pkg.repository.clone(), name: Some(pkg.name.clone()) }, + target_layout, ) } @@ -1066,6 +1108,7 @@ fn sysroot_to_crate_graph( crate_graph: &mut CrateGraph, sysroot: &Sysroot, rustc_cfg: Vec, + target_layout: Option>, load: &mut dyn FnMut(&AbsPath) -> Option, ) -> (SysrootPublicDeps, Option) { let _p = profile::span("sysroot_to_crate_graph"); @@ -1089,6 +1132,7 @@ fn sysroot_to_crate_graph( Err("no proc macro loaded for sysroot crate".into()), false, CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)), + target_layout.clone(), ); Some((krate, crate_id)) }) diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 3e7664d7d3e..faf7a2c2131 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -226,6 +226,7 @@ impl GlobalState { build_scripts: _, toolchain: _, + target_layout: _, } => Some((cargo, sysroot, rustc, rustc_cfg, cfg_overrides)), _ => None, }; diff --git a/crates/test-utils/src/fixture.rs b/crates/test-utils/src/fixture.rs index c824f5af725..73e72c18809 100644 --- a/crates/test-utils/src/fixture.rs +++ b/crates/test-utils/src/fixture.rs @@ -78,6 +78,7 @@ pub struct Fixture { pub edition: Option, pub env: FxHashMap, pub introduce_new_source_root: Option, + pub target_data_layout: Option, } pub struct MiniCore { @@ -181,6 +182,7 @@ impl Fixture { let mut cfg_key_values = Vec::new(); let mut env = FxHashMap::default(); let mut introduce_new_source_root = None; + let mut target_data_layout = None; for component in components[1..].iter() { let (key, value) = component .split_once(':') @@ -213,6 +215,7 @@ impl Fixture { } } "new_source_root" => introduce_new_source_root = Some(value.to_string()), + "target_data_layout" => target_data_layout = Some(value.to_string()), _ => panic!("bad component: {:?}", component), } } @@ -237,6 +240,7 @@ impl Fixture { edition, env, introduce_new_source_root, + target_data_layout, } } }