From f1070825bb40587d482b8916a031b48a6c6a763e Mon Sep 17 00:00:00 2001 From: Lin Yihai Date: Mon, 17 Jun 2024 19:20:53 +0800 Subject: [PATCH 1/6] Added more scenarios where commas need to be removed --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 30 ++++++++++--- .../argument-suggestions/issue-109425.fixed | 4 ++ tests/ui/argument-suggestions/issue-109425.rs | 4 ++ .../argument-suggestions/issue-109425.stderr | 42 ++++++++++++++++++- .../argument-suggestions/issue-112507.stderr | 5 ++- 5 files changed, 76 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index bf8cc462189..f29d6199a87 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1097,6 +1097,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut only_extras_so_far = errors .peek() .is_some_and(|first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); + let mut prev_extra_idx = None; let mut suggestions = vec![]; while let Some(error) = errors.next() { only_extras_so_far &= matches!(error, Error::Extra(_)); @@ -1165,11 +1166,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // fn f() {} // - f(0, 1,) // + f() - if only_extras_so_far - && !errors - .peek() - .is_some_and(|next_error| matches!(next_error, Error::Extra(_))) - { + let trim_next_comma = match errors.peek() { + Some(Error::Extra(provided_idx)) + if only_extras_so_far + && provided_idx.index() > arg_idx.index() + 1 => + // If the next Error::Extra ("next") doesn't next to current ("current"), + // fn foo(_: (), _: u32) {} + // - foo("current", (), 1u32, "next") + // + foo((), 1u32) + // If the previous error is not a `Error::Extra`, then do not trim the next comma + // - foo((), "current", 42u32, "next") + // + foo((), 42u32) + { + prev_extra_idx.map_or(true, |prev_extra_idx| { + prev_extra_idx + 1 == arg_idx.index() + }) + } + // If no error left, we need to delete the next comma + None if only_extras_so_far => true, + // Not sure if other error type need to be handled as well + _ => false, + }; + + if trim_next_comma { let next = provided_arg_tys .get(arg_idx + 1) .map(|&(_, sp)| sp) @@ -1192,6 +1211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { SuggestionText::Remove(_) => SuggestionText::Remove(true), _ => SuggestionText::DidYouMean, }; + prev_extra_idx = Some(arg_idx.index()) } } Error::Missing(expected_idx) => { diff --git a/tests/ui/argument-suggestions/issue-109425.fixed b/tests/ui/argument-suggestions/issue-109425.fixed index 5d96f457c88..4b3aaa46d86 100644 --- a/tests/ui/argument-suggestions/issue-109425.fixed +++ b/tests/ui/argument-suggestions/issue-109425.fixed @@ -15,6 +15,10 @@ fn main() { //~^ error: this function takes 1 argument but 3 arguments were supplied is(0, ""); // is(0, "") //~^ error: this function takes 2 arguments but 4 arguments were supplied + is(1, ""); + //~^ error: this function takes 2 arguments but 4 arguments were supplied + is(1, ""); + //~^ error: this function takes 2 arguments but 4 arguments were supplied s(""); // s("") //~^ error: this function takes 1 argument but 3 arguments were supplied } diff --git a/tests/ui/argument-suggestions/issue-109425.rs b/tests/ui/argument-suggestions/issue-109425.rs index bb9d37ee0ff..56816681337 100644 --- a/tests/ui/argument-suggestions/issue-109425.rs +++ b/tests/ui/argument-suggestions/issue-109425.rs @@ -15,6 +15,10 @@ fn main() { //~^ error: this function takes 1 argument but 3 arguments were supplied is(0, 1, 2, ""); // is(0, "") //~^ error: this function takes 2 arguments but 4 arguments were supplied + is((), 1, "", ()); + //~^ error: this function takes 2 arguments but 4 arguments were supplied + is(1, (), "", ()); + //~^ error: this function takes 2 arguments but 4 arguments were supplied s(0, 1, ""); // s("") //~^ error: this function takes 1 argument but 3 arguments were supplied } diff --git a/tests/ui/argument-suggestions/issue-109425.stderr b/tests/ui/argument-suggestions/issue-109425.stderr index bfe007793d4..2cd53ed528e 100644 --- a/tests/ui/argument-suggestions/issue-109425.stderr +++ b/tests/ui/argument-suggestions/issue-109425.stderr @@ -74,9 +74,47 @@ LL - is(0, 1, 2, ""); // is(0, "") LL + is(0, ""); // is(0, "") | -error[E0061]: this function takes 1 argument but 3 arguments were supplied +error[E0061]: this function takes 2 arguments but 4 arguments were supplied --> $DIR/issue-109425.rs:18:5 | +LL | is((), 1, "", ()); + | ^^ -- -- unexpected argument #4 of type `()` + | | + | unexpected argument #1 of type `()` + | +note: function defined here + --> $DIR/issue-109425.rs:5:4 + | +LL | fn is(_: u32, _: &str) {} + | ^^ ------ ------- +help: remove the extra arguments + | +LL - is((), 1, "", ()); +LL + is(1, ""); + | + +error[E0061]: this function takes 2 arguments but 4 arguments were supplied + --> $DIR/issue-109425.rs:20:5 + | +LL | is(1, (), "", ()); + | ^^ -- -- unexpected argument #4 of type `()` + | | + | unexpected argument #2 of type `()` + | +note: function defined here + --> $DIR/issue-109425.rs:5:4 + | +LL | fn is(_: u32, _: &str) {} + | ^^ ------ ------- +help: remove the extra arguments + | +LL - is(1, (), "", ()); +LL + is(1, ""); + | + +error[E0061]: this function takes 1 argument but 3 arguments were supplied + --> $DIR/issue-109425.rs:22:5 + | LL | s(0, 1, ""); // s("") | ^ - - unexpected argument #2 of type `{integer}` | | @@ -93,6 +131,6 @@ LL - s(0, 1, ""); // s("") LL + s(""); // s("") | -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0061`. diff --git a/tests/ui/argument-suggestions/issue-112507.stderr b/tests/ui/argument-suggestions/issue-112507.stderr index 908ed35c669..e2ea9af7dc2 100644 --- a/tests/ui/argument-suggestions/issue-112507.stderr +++ b/tests/ui/argument-suggestions/issue-112507.stderr @@ -18,8 +18,9 @@ LL | Float(Option), | ^^^^^ help: remove the extra arguments | -LL ~ , -LL ~ None); +LL - 0, +LL - None, +LL + None); | error: aborting due to 1 previous error From 6f6f91ab828abf38244715183ef3fd19b3b2b32f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 20 Oct 2024 13:39:52 +0000 Subject: [PATCH 2/6] Rip out old effects var handling code from traits --- .../src/check_consts/qualifs.rs | 53 +---- compiler/rustc_hir_typeck/src/callee.rs | 35 +--- compiler/rustc_middle/src/traits/select.rs | 7 +- compiler/rustc_middle/src/ty/util.rs | 18 -- .../rustc_mir_build/src/build/matches/test.rs | 7 +- .../src/thir/pattern/const_to_pat.rs | 12 +- .../src/error_reporting/traits/suggestions.rs | 4 +- .../src/traits/project.rs | 36 +--- .../src/traits/select/candidate_assembly.rs | 112 +--------- .../src/traits/select/confirmation.rs | 191 +----------------- .../src/traits/select/mod.rs | 15 +- .../rustc_trait_selection/src/traits/util.rs | 11 +- .../src/implied_bounds_in_impls.rs | 9 - src/tools/clippy/clippy_utils/src/ty.rs | 15 +- 14 files changed, 34 insertions(+), 491 deletions(-) diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 7358a6c6d22..547030a1854 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -6,13 +6,10 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::LangItem; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::*; -use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty}; use rustc_middle::{bug, mir}; -use rustc_trait_selection::traits::{ - ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext, -}; -use tracing::{instrument, trace}; +use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; +use tracing::instrument; use super::ConstCx; @@ -195,50 +192,8 @@ impl Qualif for NeedsNonConstDrop { return false; } - // FIXME(effects): If `destruct` is not a `const_trait`, - // or effects are disabled in this crate, then give up. - let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span)); - if !cx.tcx.has_host_param(destruct_def_id) || !cx.tcx.features().effects { - return NeedsDrop::in_any_value_of_ty(cx, ty); - } - - let obligation = Obligation::new( - cx.tcx, - ObligationCause::dummy_with_span(cx.body.span), - cx.param_env, - ty::TraitRef::new(cx.tcx, destruct_def_id, [ - ty::GenericArg::from(ty), - ty::GenericArg::from(cx.tcx.expected_host_effect_param_for_body(cx.def_id())), - ]), - ); - - let infcx = cx.tcx.infer_ctxt().build(); - let mut selcx = SelectionContext::new(&infcx); - let Some(impl_src) = selcx.select(&obligation).ok().flatten() else { - // If we couldn't select a const destruct candidate, then it's bad - return true; - }; - - trace!(?impl_src); - - if !matches!( - impl_src, - ImplSource::Builtin(BuiltinImplSource::Misc, _) | ImplSource::Param(_) - ) { - // If our const destruct candidate is not ConstDestruct or implied by the param env, - // then it's bad - return true; - } - - if impl_src.borrow_nested_obligations().is_empty() { - return false; - } - - // If we had any errors, then it's bad - let ocx = ObligationCtxt::new(&infcx); - ocx.register_obligations(impl_src.nested_obligations()); - let errors = ocx.select_all_or_error(); - !errors.is_empty() + // FIXME(effects): Reimplement const drop checking. + NeedsDrop::in_any_value_of_ty(cx, ty) } fn in_adt_inherently<'tcx>( diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 13ba615d4c9..fa6a86c6911 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -537,40 +537,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // This check is here because there is currently no way to express a trait bound for `FnDef` types only. if let ty::FnDef(def_id, _args) = *arg_ty.kind() { - let fn_once_def_id = - self.tcx.require_lang_item(hir::LangItem::FnOnce, Some(span)); - let fn_once_output_def_id = - self.tcx.require_lang_item(hir::LangItem::FnOnceOutput, Some(span)); - if self.tcx.has_host_param(fn_once_def_id) { - let const_param: ty::GenericArg<'tcx> = - ([self.tcx.consts.false_, self.tcx.consts.true_])[idx].into(); - self.register_predicate(traits::Obligation::new( - self.tcx, - self.misc(span), - self.param_env, - ty::TraitRef::new(self.tcx, fn_once_def_id, [ - arg_ty.into(), - fn_sig.inputs()[0].into(), - const_param, - ]), - )); - - self.register_predicate(traits::Obligation::new( - self.tcx, - self.misc(span), - self.param_env, - ty::ProjectionPredicate { - projection_term: ty::AliasTerm::new( - self.tcx, - fn_once_output_def_id, - [arg_ty.into(), fn_sig.inputs()[0].into(), const_param], - ), - term: fn_sig.output().into(), - }, - )); - - self.select_obligations_where_possible(|_| {}); - } else if idx == 0 && !self.tcx.is_const_fn_raw(def_id) { + if idx == 0 && !self.tcx.is_const_fn_raw(def_id) { self.dcx().emit_err(errors::ConstSelectMustBeConst { span }); } } else { diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 66035464fa5..05556ae38a8 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -161,9 +161,7 @@ pub enum SelectionCandidate<'tcx> { /// Implementation of a `Fn`-family trait by one of the anonymous /// types generated for a fn pointer type (e.g., `fn(int) -> int`) - FnPointerCandidate { - fn_host_effect: ty::Const<'tcx>, - }, + FnPointerCandidate, TraitAliasCandidate, @@ -180,9 +178,6 @@ pub enum SelectionCandidate<'tcx> { BuiltinObjectCandidate, BuiltinUnsizeCandidate, - - /// Implementation of `const Destruct`, optionally from a custom `impl const Drop`. - ConstDestructCandidate(Option), } /// The result of trait evaluation. The order is important diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 0a917120b3b..9a3049f3be6 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -907,24 +907,6 @@ impl<'tcx> TyCtxt<'tcx> { } } - /// Constructs generic args for an item, optionally appending a const effect param type - pub fn with_opt_host_effect_param( - self, - caller_def_id: LocalDefId, - callee_def_id: DefId, - args: impl IntoIterator>>, - ) -> ty::GenericArgsRef<'tcx> { - let generics = self.generics_of(callee_def_id); - assert_eq!(generics.parent, None); - - let opt_const_param = generics - .host_effect_index - .is_some() - .then(|| ty::GenericArg::from(self.expected_host_effect_param_for_body(caller_def_id))); - - self.mk_args_from_iter(args.into_iter().map(|arg| arg.into()).chain(opt_const_param)) - } - /// Expand any [weak alias types][weak] contained within the given `value`. /// /// This should be used over other normalization routines in situations where diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 020c202f965..7170aebee7d 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -454,12 +454,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, Some(source_info.span)); - let method = trait_method( - self.tcx, - eq_def_id, - sym::eq, - self.tcx.with_opt_host_effect_param(self.def_id, eq_def_id, [compare_ty, compare_ty]), - ); + let method = trait_method(self.tcx, eq_def_id, sym::eq, [compare_ty, compare_ty]); let bool_ty = self.tcx.types.bool; let eq_result = self.temp(bool_ty, source_info.span); diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 2c3611afca9..0dfa9168f7c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -43,7 +43,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } struct ConstToPat<'tcx> { - id: hir::HirId, span: Span, param_env: ty::ParamEnv<'tcx>, @@ -62,7 +61,6 @@ impl<'tcx> ConstToPat<'tcx> { ) -> Self { trace!(?pat_ctxt.typeck_results.hir_owner); ConstToPat { - id, span, infcx, param_env: pat_ctxt.param_env, @@ -149,15 +147,7 @@ impl<'tcx> ConstToPat<'tcx> { tcx, ObligationCause::dummy(), self.param_env, - ty::TraitRef::new_from_args( - tcx, - partial_eq_trait_id, - tcx.with_opt_host_effect_param( - tcx.hir().enclosing_body_owner(self.id), - partial_eq_trait_id, - [ty, ty], - ), - ), + ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]), ); // This *could* accept a type that isn't actually `PartialEq`, because region bounds get diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 733baaa99e5..677905a3085 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3701,12 +3701,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } _ => None, }; - // Also add host param, if present - let host = self.tcx.generics_of(trait_pred.def_id()).host_effect_index.map(|idx| trait_pred.skip_binder().trait_ref.args[idx]); let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate { trait_ref: ty::TraitRef::new(self.tcx, trait_pred.def_id(), - [field_ty].into_iter().chain(trait_args).chain(host), + [field_ty].into_iter().chain(trait_args), ), ..*tr }); diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 7f7c9bced18..cc634b65a0b 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1642,24 +1642,9 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>( sig, ); - let host_effect_param = match *fn_type.kind() { - ty::FnDef(def_id, args) => tcx - .generics_of(def_id) - .host_effect_index - .map_or(tcx.consts.true_, |idx| args.const_at(idx)), - ty::FnPtr(..) => tcx.consts.true_, - _ => unreachable!("only expected FnPtr or FnDef in `confirm_fn_pointer_candidate`"), - }; - - confirm_callable_candidate( - selcx, - obligation, - sig, - util::TupleArgumentsFlag::Yes, - host_effect_param, - ) - .with_addl_obligations(nested) - .with_addl_obligations(obligations) + confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) } fn confirm_closure_candidate<'cx, 'tcx>( @@ -1739,16 +1724,9 @@ fn confirm_closure_candidate<'cx, 'tcx>( debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate"); - confirm_callable_candidate( - selcx, - obligation, - closure_sig, - util::TupleArgumentsFlag::No, - // FIXME(effects): This doesn't handle const closures correctly! - selcx.tcx().consts.true_, - ) - .with_addl_obligations(nested) - .with_addl_obligations(obligations) + confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) } fn confirm_callable_candidate<'cx, 'tcx>( @@ -1756,7 +1734,6 @@ fn confirm_callable_candidate<'cx, 'tcx>( obligation: &ProjectionTermObligation<'tcx>, fn_sig: ty::PolyFnSig<'tcx>, flag: util::TupleArgumentsFlag, - fn_host_effect: ty::Const<'tcx>, ) -> Progress<'tcx> { let tcx = selcx.tcx(); @@ -1771,7 +1748,6 @@ fn confirm_callable_candidate<'cx, 'tcx>( obligation.predicate.self_ty(), fn_sig, flag, - fn_host_effect, ) .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate { projection_term: ty::AliasTerm::new_from_args(tcx, fn_once_output_def_id, trait_ref.args), diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index aa313a526c1..ac38c9e24a2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -543,23 +543,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Provide an impl, but only for suitable `fn` pointers. ty::FnPtr(sig_tys, hdr) => { if sig_tys.with(hdr).is_fn_trait_compatible() { - candidates - .vec - .push(FnPointerCandidate { fn_host_effect: self.tcx().consts.true_ }); + candidates.vec.push(FnPointerCandidate); } } // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). - ty::FnDef(def_id, args) => { + ty::FnDef(def_id, _args) => { let tcx = self.tcx(); if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible() && tcx.codegen_fn_attrs(def_id).target_features.is_empty() { - candidates.vec.push(FnPointerCandidate { - fn_host_effect: tcx - .generics_of(def_id) - .host_effect_index - .map_or(tcx.consts.true_, |idx| args.const_at(idx)), - }); + candidates.vec.push(FnPointerCandidate); } } _ => {} @@ -1170,103 +1163,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn assemble_const_destruct_candidates( &mut self, - obligation: &PolyTraitObligation<'tcx>, + _obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - // If the predicate is `~const Destruct` in a non-const environment, we don't actually need - // to check anything. We'll short-circuit checking any obligations in confirmation, too. - let Some(host_effect_index) = - self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index - else { - candidates.vec.push(BuiltinCandidate { has_nested: false }); - return; - }; - // If the obligation has `host = true`, then the obligation is non-const and it's always - // trivially implemented. - if obligation.predicate.skip_binder().trait_ref.args.const_at(host_effect_index) - == self.tcx().consts.true_ - { - candidates.vec.push(BuiltinCandidate { has_nested: false }); - return; - } - - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); - match self_ty.kind() { - ty::Alias(..) - | ty::Dynamic(..) - | ty::Error(_) - | ty::Bound(..) - | ty::Param(_) - | ty::Placeholder(_) => { - // We don't know if these are `~const Destruct`, at least - // not structurally... so don't push a candidate. - } - - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::Str - | ty::RawPtr(_, _) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Never - | ty::Foreign(_) - | ty::Array(..) - | ty::Pat(..) - | ty::Slice(_) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::Tuple(_) - | ty::CoroutineWitness(..) => { - // These are built-in, and cannot have a custom `impl const Destruct`. - candidates.vec.push(ConstDestructCandidate(None)); - } - - ty::Adt(..) => { - let mut relevant_impl = None; - self.tcx().for_each_relevant_impl( - self.tcx().require_lang_item(LangItem::Drop, None), - obligation.predicate.skip_binder().trait_ref.self_ty(), - |impl_def_id| { - if let Some(old_impl_def_id) = relevant_impl { - self.tcx() - .dcx() - .struct_span_err( - self.tcx().def_span(impl_def_id), - "multiple drop impls found", - ) - .with_span_note( - self.tcx().def_span(old_impl_def_id), - "other impl here", - ) - .delay_as_bug(); - } - - relevant_impl = Some(impl_def_id); - }, - ); - - if let Some(impl_def_id) = relevant_impl { - // Check that `impl Drop` is actually const, if there is a custom impl - if self.tcx().constness(impl_def_id) == hir::Constness::Const { - candidates.vec.push(ConstDestructCandidate(Some(impl_def_id))); - } - } else { - // Otherwise check the ADT like a built-in type (structurally) - candidates.vec.push(ConstDestructCandidate(None)); - } - } - - ty::Infer(_) => { - candidates.ambiguous = true; - } - } + // FIXME(effects): Destruct is not const yet, and it is implemented + // by all types today in non-const setting. + candidates.vec.push(BuiltinCandidate { has_nested: false }); } fn assemble_candidate_for_tuple( diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 0ba3b4e6e55..1764777ee69 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -28,9 +28,9 @@ use super::{BuiltinImplConditions, PredicateObligations, SelectionContext}; use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::util::{self, closure_trait_ref_and_return_type}; use crate::traits::{ - ImplDerivedCause, ImplSource, ImplSourceUserDefinedData, Normalized, Obligation, - ObligationCause, PolyTraitObligation, PredicateObligation, Selection, SelectionError, - SignatureMismatch, TraitDynIncompatible, TraitObligation, Unimplemented, + ImplSource, ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, + PolyTraitObligation, PredicateObligation, Selection, SelectionError, SignatureMismatch, + TraitDynIncompatible, TraitObligation, Unimplemented, }; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { @@ -109,8 +109,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator) } - FnPointerCandidate { fn_host_effect } => { - let data = self.confirm_fn_pointer_candidate(obligation, fn_host_effect)?; + FnPointerCandidate => { + let data = self.confirm_fn_pointer_candidate(obligation)?; ImplSource::Builtin(BuiltinImplSource::Misc, data) } @@ -131,11 +131,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { TraitUpcastingUnsizeCandidate(idx) => { self.confirm_trait_upcasting_unsize_candidate(obligation, idx)? } - - ConstDestructCandidate(def_id) => { - let data = self.confirm_const_destruct_candidate(obligation, def_id)?; - ImplSource::Builtin(BuiltinImplSource::Misc, data) - } }; // The obligations returned by confirmation are recursively evaluated @@ -708,7 +703,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_fn_pointer_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, - fn_host_effect: ty::Const<'tcx>, ) -> Result, SelectionError<'tcx>> { debug!(?obligation, "confirm_fn_pointer_candidate"); let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); @@ -722,7 +716,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self_ty, sig, util::TupleArgumentsFlag::Yes, - fn_host_effect, ) .map_bound(|(trait_ref, _)| trait_ref); @@ -904,11 +897,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let self_ty: Ty<'_> = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let trait_ref = match *self_ty.kind() { - ty::Closure(..) => self.closure_trait_ref_unnormalized( - self_ty, - obligation.predicate.def_id(), - self.tcx().consts.true_, - ), + ty::Closure(..) => { + self.closure_trait_ref_unnormalized(self_ty, obligation.predicate.def_id()) + } ty::CoroutineClosure(_, args) => { args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| { ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [ @@ -1341,170 +1332,4 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => bug!("source: {source}, target: {target}"), }) } - - fn confirm_const_destruct_candidate( - &mut self, - obligation: &PolyTraitObligation<'tcx>, - impl_def_id: Option, - ) -> Result, SelectionError<'tcx>> { - let Some(host_effect_index) = - self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index - else { - bug!() - }; - let host_effect_param: ty::GenericArg<'tcx> = - obligation.predicate.skip_binder().trait_ref.args.const_at(host_effect_index).into(); - - let drop_trait = self.tcx().require_lang_item(LangItem::Drop, None); - - let tcx = self.tcx(); - let self_ty = obligation.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty)); - - let mut nested = PredicateObligations::new(); - let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived); - - // If we have a custom `impl const Drop`, then - // first check it like a regular impl candidate. - // This is copied from confirm_impl_candidate but remaps the predicate to `~const Drop` beforehand. - if let Some(impl_def_id) = impl_def_id { - let mut new_obligation = obligation.clone(); - new_obligation.predicate = new_obligation.predicate.map_bound(|mut trait_pred| { - trait_pred.trait_ref.def_id = drop_trait; - trait_pred - }); - let args = self.rematch_impl(impl_def_id, &new_obligation); - debug!(?args, "impl args"); - - let cause = obligation.derived_cause(|derived| { - ObligationCauseCode::ImplDerived(Box::new(ImplDerivedCause { - derived, - impl_or_alias_def_id: impl_def_id, - impl_def_predicate_index: None, - span: obligation.cause.span, - })) - }); - let obligations = ensure_sufficient_stack(|| { - self.vtable_impl( - impl_def_id, - args, - &cause, - new_obligation.recursion_depth + 1, - new_obligation.param_env, - obligation.predicate, - ) - }); - nested.extend(obligations.nested); - } - - // We want to confirm the ADT's fields if we have an ADT - let mut stack = match *self_ty.skip_binder().kind() { - ty::Adt(def, args) => def.all_fields().map(|f| f.ty(tcx, args)).collect(), - _ => vec![self_ty.skip_binder()], - }; - - while let Some(nested_ty) = stack.pop() { - match *nested_ty.kind() { - // We know these types are trivially drop - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::Str - | ty::RawPtr(_, _) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Never - | ty::Foreign(_) => {} - - // `ManuallyDrop` is trivially drop - ty::Adt(def, _) if def.is_manually_drop() => {} - - // These types are built-in, so we can fast-track by registering - // nested predicates for their constituent type(s) - ty::Array(ty, _) | ty::Slice(ty) | ty::Pat(ty, _) => { - stack.push(ty); - } - ty::Tuple(tys) => { - stack.extend(tys.iter()); - } - ty::Closure(_, args) => { - stack.push(args.as_closure().tupled_upvars_ty()); - } - ty::Coroutine(_, args) => { - let coroutine = args.as_coroutine(); - stack.extend([coroutine.tupled_upvars_ty(), coroutine.witness()]); - } - ty::CoroutineWitness(def_id, args) => { - let tcx = self.tcx(); - stack.extend(tcx.bound_coroutine_hidden_types(def_id).map(|bty| { - self.infcx.enter_forall_and_leak_universe(bty.instantiate(tcx, args)) - })) - } - - // If we have a projection type, make sure to normalize it so we replace it - // with a fresh infer variable - ty::Alias(ty::Projection | ty::Inherent, ..) => { - let predicate = normalize_with_depth_to( - self, - obligation.param_env, - cause.clone(), - obligation.recursion_depth + 1, - self_ty.rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef::new( - self.tcx(), - self.tcx().require_lang_item(LangItem::Destruct, Some(cause.span)), - [nested_ty.into(), host_effect_param], - ), - polarity: ty::PredicatePolarity::Positive, - }), - &mut nested, - ); - - nested.push(Obligation::with_depth( - tcx, - cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - predicate, - )); - } - - // If we have any other type (e.g. an ADT), just register a nested obligation - // since it's either not `const Drop` (and we raise an error during selection), - // or it's an ADT (and we need to check for a custom impl during selection) - ty::Error(_) - | ty::Dynamic(..) - | ty::CoroutineClosure(..) - | ty::Param(_) - | ty::Bound(..) - | ty::Adt(..) - | ty::Alias(ty::Opaque | ty::Weak, _) - | ty::Infer(_) - | ty::Placeholder(_) => { - let predicate = self_ty.rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef::new( - self.tcx(), - self.tcx().require_lang_item(LangItem::Destruct, Some(cause.span)), - [nested_ty.into(), host_effect_param], - ), - polarity: ty::PredicatePolarity::Positive, - }); - - nested.push(Obligation::with_depth( - tcx, - cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - predicate, - )); - } - } - } - - Ok(nested) - } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 621babe9104..4757430a21c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1831,12 +1831,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No, // (*) - (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => { - DropVictim::Yes - } - (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => { - DropVictim::No - } + (BuiltinCandidate { has_nested: false }, _) => DropVictim::Yes, + (_, BuiltinCandidate { has_nested: false }) => DropVictim::No, (ParamCandidate(other), ParamCandidate(victim)) => { let same_except_bound_vars = other.skip_binder().trait_ref @@ -1855,11 +1851,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } - // Drop otherwise equivalent non-const fn pointer candidates - (FnPointerCandidate { .. }, FnPointerCandidate { fn_host_effect }) => { - DropVictim::drop_if(*fn_host_effect == self.tcx().consts.true_) - } - ( ParamCandidate(other_cand), ImplCandidate(..) @@ -2766,7 +2757,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { &mut self, self_ty: Ty<'tcx>, fn_trait_def_id: DefId, - fn_host_effect: ty::Const<'tcx>, ) -> ty::PolyTraitRef<'tcx> { let ty::Closure(_, args) = *self_ty.kind() else { bug!("expected closure, found {self_ty}"); @@ -2779,7 +2769,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { self_ty, closure_sig, util::TupleArgumentsFlag::No, - fn_host_effect, ) .map_bound(|(trait_ref, _)| trait_ref) } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index aed2e3d61aa..b7a2f20b769 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -215,22 +215,13 @@ pub(crate) fn closure_trait_ref_and_return_type<'tcx>( self_ty: Ty<'tcx>, sig: ty::PolyFnSig<'tcx>, tuple_arguments: TupleArgumentsFlag, - fn_host_effect: ty::Const<'tcx>, ) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> { assert!(!self_ty.has_escaping_bound_vars()); let arguments_tuple = match tuple_arguments { TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()), }; - let trait_ref = if tcx.has_host_param(fn_trait_def_id) { - ty::TraitRef::new(tcx, fn_trait_def_id, [ - ty::GenericArg::from(self_ty), - ty::GenericArg::from(arguments_tuple), - ty::GenericArg::from(fn_host_effect), - ]) - } else { - ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]) - }; + let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]); sig.map_bound(|sig| (trait_ref, sig.output())) } diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index f4c00d8287d..00f83237224 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -190,15 +190,6 @@ fn is_same_generics<'tcx>( .enumerate() .skip(1) // skip `Self` implicit arg .all(|(arg_index, arg)| { - if [ - implied_by_generics.host_effect_index, - implied_generics.host_effect_index, - ] - .contains(&Some(arg_index)) - { - // skip host effect params in determining whether generics are same - return true; - } if let Some(ty) = arg.as_type() { if let &ty::Param(ty::ParamTy { index, .. }) = ty.kind() // `index == 0` means that it's referring to `Self`, diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index b7a3569ccf0..cab16b4927d 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -274,23 +274,10 @@ pub fn implements_trait_with_env_from_iter<'tcx>( .map(|arg| arg.into().unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into())) .collect::>(); - // If an effect arg was not specified, we need to specify it. - let effect_arg = if tcx - .generics_of(trait_id) - .host_effect_index - .is_some_and(|x| args.get(x - 1).is_none()) - { - Some(GenericArg::from(callee_id.map_or(tcx.consts.true_, |def_id| { - tcx.expected_host_effect_param_for_body(def_id) - }))) - } else { - None - }; - let trait_ref = TraitRef::new( tcx, trait_id, - [GenericArg::from(ty)].into_iter().chain(args).chain(effect_arg), + [GenericArg::from(ty)].into_iter().chain(args), ); debug_assert_matches!( From 61ed4cb5b4d26a9f12ad0b0fb065b4014348a9dd Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 20 Oct 2024 18:33:59 +0000 Subject: [PATCH 3/6] Remove the BoundConstness::NotConst variant --- compiler/rustc_hir_analysis/src/bounds.rs | 21 ++++++++------- .../src/hir_ty_lowering/bounds.rs | 15 +++++------ .../src/hir_ty_lowering/dyn_compatibility.rs | 2 +- .../src/hir_ty_lowering/mod.rs | 4 +-- compiler/rustc_middle/src/ty/print/pretty.rs | 12 ++++++--- .../traits/fulfillment_errors.rs | 27 ++++++++++--------- compiler/rustc_type_ir/src/predicate.rs | 6 ++--- 7 files changed, 46 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index 7f4ab352ef2..9a2c38e51e2 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -51,7 +51,7 @@ impl<'tcx> Bounds<'tcx> { bound_trait_ref: ty::PolyTraitRef<'tcx>, span: Span, polarity: ty::PredicatePolarity, - constness: ty::BoundConstness, + constness: Option, predicate_filter: PredicateFilter, ) { let clause = ( @@ -88,19 +88,20 @@ impl<'tcx> Bounds<'tcx> { // associated type of `` and make sure that the effect is compatible. let compat_val = match (tcx.def_kind(defining_def_id), constness) { // FIXME(effects): revisit the correctness of this - (_, ty::BoundConstness::Const) => tcx.consts.false_, + (_, Some(ty::BoundConstness::Const)) => tcx.consts.false_, // body owners that can have trait bounds - (DefKind::Const | DefKind::Fn | DefKind::AssocFn, ty::BoundConstness::ConstIfConst) => { - tcx.expected_host_effect_param_for_body(defining_def_id) - } + ( + DefKind::Const | DefKind::Fn | DefKind::AssocFn, + Some(ty::BoundConstness::ConstIfConst), + ) => tcx.expected_host_effect_param_for_body(defining_def_id), - (_, ty::BoundConstness::NotConst) => { + (_, None) => { if !tcx.is_const_trait(bound_trait_ref.def_id()) { return; } tcx.consts.true_ } - (DefKind::Trait, ty::BoundConstness::ConstIfConst) => { + (DefKind::Trait, Some(ty::BoundConstness::ConstIfConst)) => { // we are in a trait, where `bound_trait_ref` could be: // (1) a super trait `trait Foo: ~const Bar`. // - This generates `::Effects: TyCompat<::Effects>` @@ -138,7 +139,7 @@ impl<'tcx> Bounds<'tcx> { return; } - (DefKind::Impl { of_trait: true }, ty::BoundConstness::ConstIfConst) => { + (DefKind::Impl { of_trait: true }, Some(ty::BoundConstness::ConstIfConst)) => { // this is a where clause on an impl header. // push `::Effects` into the set for the `Min` bound. let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else { @@ -172,12 +173,12 @@ impl<'tcx> Bounds<'tcx> { // // FIXME(effects) this is equality for now, which wouldn't be helpful for a non-const implementor // that uses a `Bar` that implements `Trait` with `Maybe` effects. - (DefKind::AssocTy, ty::BoundConstness::ConstIfConst) => { + (DefKind::AssocTy, Some(ty::BoundConstness::ConstIfConst)) => { // FIXME(effects): implement this return; } // probably illegal in this position. - (_, ty::BoundConstness::ConstIfConst) => { + (_, Some(ty::BoundConstness::ConstIfConst)) => { tcx.dcx().span_delayed_bug(span, "invalid `~const` encountered"); return; } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 310f648b980..fb688c23c48 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -171,16 +171,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::GenericBound::Trait(poly_trait_ref) => { let (constness, polarity) = match poly_trait_ref.modifiers { hir::TraitBoundModifier::Const => { - (ty::BoundConstness::Const, ty::PredicatePolarity::Positive) - } - hir::TraitBoundModifier::MaybeConst => { - (ty::BoundConstness::ConstIfConst, ty::PredicatePolarity::Positive) - } - hir::TraitBoundModifier::None => { - (ty::BoundConstness::NotConst, ty::PredicatePolarity::Positive) + (Some(ty::BoundConstness::Const), ty::PredicatePolarity::Positive) } + hir::TraitBoundModifier::MaybeConst => ( + Some(ty::BoundConstness::ConstIfConst), + ty::PredicatePolarity::Positive, + ), + hir::TraitBoundModifier::None => (None, ty::PredicatePolarity::Positive), hir::TraitBoundModifier::Negative => { - (ty::BoundConstness::NotConst, ty::PredicatePolarity::Negative) + (None, ty::PredicatePolarity::Negative) } hir::TraitBoundModifier::Maybe => continue, }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index 2cf97e29060..a1ee120e855 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -51,7 +51,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } = self.lower_poly_trait_ref( &trait_bound.trait_ref, trait_bound.span, - ty::BoundConstness::NotConst, + None, ty::PredicatePolarity::Positive, dummy_self, &mut bounds, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 828809726d2..fe0cd572609 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -652,7 +652,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, trait_ref: &hir::TraitRef<'tcx>, span: Span, - constness: ty::BoundConstness, + constness: Option, polarity: ty::PredicatePolarity, self_ty: Ty<'tcx>, bounds: &mut Bounds<'tcx>, @@ -675,7 +675,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Some(self_ty), ); - if let ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst = constness + if let Some(constness) = constness && !self.tcx().is_const_trait(trait_def_id) { self.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 7ada5fd93ba..b5495fa282b 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1956,7 +1956,6 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { define_scoped_cx!(self); match constness { - ty::BoundConstness::NotConst => {} ty::BoundConstness::Const => { p!("const "); } @@ -2948,7 +2947,10 @@ impl<'tcx> ty::TraitPredicate<'tcx> { } #[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift)] -pub struct TraitPredPrintWithBoundConstness<'tcx>(ty::TraitPredicate<'tcx>, ty::BoundConstness); +pub struct TraitPredPrintWithBoundConstness<'tcx>( + ty::TraitPredicate<'tcx>, + Option, +); impl<'tcx> fmt::Debug for TraitPredPrintWithBoundConstness<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -2966,7 +2968,7 @@ impl<'tcx> ty::PolyTraitPredicate<'tcx> { fn print_with_bound_constness( self, - constness: ty::BoundConstness, + constness: Option, ) -> ty::Binder<'tcx, TraitPredPrintWithBoundConstness<'tcx>> { self.map_bound(|trait_pred| TraitPredPrintWithBoundConstness(trait_pred, constness)) } @@ -3206,7 +3208,9 @@ define_print_and_forward_display! { TraitPredPrintWithBoundConstness<'tcx> { p!(print(self.0.trait_ref.self_ty()), ": "); - p!(pretty_print_bound_constness(self.1)); + if let Some(constness) = self.1 { + p!(pretty_print_bound_constness(constness)); + } if let ty::PredicatePolarity::Negative = self.0.polarity { p!("!"); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 44373ca4866..dfec5d956c9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -290,7 +290,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Drop) - && matches!(predicate_constness, ty::BoundConstness::ConstIfConst | ty::BoundConstness::Const) + && matches!(predicate_constness, Some(ty::BoundConstness::ConstIfConst | ty::BoundConstness::Const)) { err.note("`~const Drop` was renamed to `~const Destruct`"); err.note("See for more details"); @@ -2192,7 +2192,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, trait_predicate: ty::PolyTraitPredicate<'tcx>, message: Option, - predicate_constness: ty::BoundConstness, + predicate_constness: Option, append_const_msg: Option, post_message: String, ) -> String { @@ -2200,19 +2200,21 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .and_then(|cannot_do_this| { match (predicate_constness, append_const_msg) { // do nothing if predicate is not const - (ty::BoundConstness::NotConst, _) => Some(cannot_do_this), + (None, _) => Some(cannot_do_this), // suggested using default post message ( - ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, + Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst), Some(AppendConstMessage::Default), ) => Some(format!("{cannot_do_this} in const contexts")), // overridden post message ( - ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, + Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst), Some(AppendConstMessage::Custom(custom_msg, _)), ) => Some(format!("{cannot_do_this}{custom_msg}")), // fallback to generic message - (ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst, None) => None, + (Some(ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst), None) => { + None + } } }) .unwrap_or_else(|| { @@ -2358,26 +2360,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { p: ty::PolyTraitPredicate<'tcx>, leaf: ty::PolyTraitPredicate<'tcx>, span: Span, - ) -> (ty::PolyTraitPredicate<'tcx>, ty::PolyTraitPredicate<'tcx>, ty::BoundConstness) { + ) -> (ty::PolyTraitPredicate<'tcx>, ty::PolyTraitPredicate<'tcx>, Option) + { let trait_ref = p.to_poly_trait_ref(); if !self.tcx.is_lang_item(trait_ref.def_id(), LangItem::EffectsCompat) { - return (p, leaf, ty::BoundConstness::NotConst); + return (p, leaf, None); } let Some(ty::Alias(ty::AliasTyKind::Projection, projection)) = trait_ref.self_ty().no_bound_vars().map(Ty::kind) else { - return (p, leaf, ty::BoundConstness::NotConst); + return (p, leaf, None); }; let constness = trait_ref.skip_binder().args.const_at(1); let constness = if constness == self.tcx.consts.true_ || constness.is_ct_infer() { - ty::BoundConstness::NotConst + None } else if constness == self.tcx.consts.false_ { - ty::BoundConstness::Const + Some(ty::BoundConstness::Const) } else if matches!(constness.kind(), ty::ConstKind::Param(_)) { - ty::BoundConstness::ConstIfConst + Some(ty::BoundConstness::ConstIfConst) } else { self.dcx().span_bug(span, format!("Unknown constness argument: {constness:?}")); }; diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 8146181df6c..b613505f826 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -726,9 +726,9 @@ pub struct CoercePredicate { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))] pub enum BoundConstness { - /// `Type: Trait` - NotConst, /// `Type: const Trait` + /// + /// A bound is required to be unconditionally const, even in a runtime function. Const, /// `Type: ~const Trait` /// @@ -739,7 +739,6 @@ pub enum BoundConstness { impl BoundConstness { pub fn as_str(self) -> &'static str { match self { - Self::NotConst => "", Self::Const => "const", Self::ConstIfConst => "~const", } @@ -749,7 +748,6 @@ impl BoundConstness { impl fmt::Display for BoundConstness { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::NotConst => f.write_str("normal"), Self::Const => f.write_str("const"), Self::ConstIfConst => f.write_str("~const"), } From 4b28e52f32d5abee7b9964cfc554ab56a2204f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:28:04 +0800 Subject: [PATCH 4/6] bootstrap: extract builder cargo to its own module I found builder.rs to be a massive file which made it hard to digest. To make `RUSTFLAGS` usage hardening easier later, I extracted the cargo part in `builder.rs` into its own module. --- src/bootstrap/src/core/builder.rs | 1218 +---------------------- src/bootstrap/src/core/builder/cargo.rs | 1217 ++++++++++++++++++++++ src/bootstrap/src/core/builder/tests.rs | 2 +- 3 files changed, 1226 insertions(+), 1211 deletions(-) create mode 100644 src/bootstrap/src/core/builder/cargo.rs diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 8cea01434fe..8f8ab112857 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1,7 +1,8 @@ +mod cargo; + use std::any::{Any, type_name}; use std::cell::{Cell, RefCell}; use std::collections::BTreeSet; -use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Write}; use std::hash::Hash; use std::ops::Deref; @@ -12,22 +13,17 @@ use std::{env, fs}; use clap::ValueEnum; +pub use self::cargo::Cargo; pub use crate::Compiler; -use crate::core::build_steps::tool::{self, SourceType}; use crate::core::build_steps::{ - check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, vendor, + check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor, }; -use crate::core::config::flags::{Color, Subcommand}; -use crate::core::config::{DryRun, SplitDebuginfo, TargetSelection}; +use crate::core::config::flags::Subcommand; +use crate::core::config::{DryRun, TargetSelection}; use crate::utils::cache::Cache; use crate::utils::exec::{BootstrapCommand, command}; -use crate::utils::helpers::{ - self, LldThreads, add_dylib_path, add_link_lib_path, check_cfg_arg, exe, libdir, linker_args, - linker_flags, t, -}; -use crate::{ - Build, CLang, Crate, DocTests, EXTRA_CHECK_CFGS, GitRepo, Mode, prepare_behaviour_dump_dir, -}; +use crate::utils::helpers::{self, LldThreads, add_dylib_path, exe, libdir, linker_args, t}; +use crate::{Build, Crate}; #[cfg(test)] mod tests; @@ -1410,870 +1406,6 @@ impl<'a> Builder<'a> { None } - /// Like `cargo`, but only passes flags that are valid for all commands. - pub fn bare_cargo( - &self, - compiler: Compiler, - mode: Mode, - target: TargetSelection, - cmd_kind: Kind, - ) -> BootstrapCommand { - let mut cargo = match cmd_kind { - Kind::Clippy => { - let mut cargo = self.cargo_clippy_cmd(compiler); - cargo.arg(cmd_kind.as_str()); - cargo - } - Kind::MiriSetup => { - let mut cargo = self.cargo_miri_cmd(compiler); - cargo.arg("miri").arg("setup"); - cargo - } - Kind::MiriTest => { - let mut cargo = self.cargo_miri_cmd(compiler); - cargo.arg("miri").arg("test"); - cargo - } - _ => { - let mut cargo = command(&self.initial_cargo); - cargo.arg(cmd_kind.as_str()); - cargo - } - }; - - // Run cargo from the source root so it can find .cargo/config. - // This matters when using vendoring and the working directory is outside the repository. - cargo.current_dir(&self.src); - - let out_dir = self.stage_out(compiler, mode); - cargo.env("CARGO_TARGET_DIR", &out_dir); - - // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger` - // from out of tree it shouldn't matter, since x.py is only used for - // building in-tree. - let color_logs = ["RUSTDOC_LOG_COLOR", "RUSTC_LOG_COLOR", "RUST_LOG_COLOR"]; - match self.build.config.color { - Color::Always => { - cargo.arg("--color=always"); - for log in &color_logs { - cargo.env(log, "always"); - } - } - Color::Never => { - cargo.arg("--color=never"); - for log in &color_logs { - cargo.env(log, "never"); - } - } - Color::Auto => {} // nothing to do - } - - if cmd_kind != Kind::Install { - cargo.arg("--target").arg(target.rustc_target_arg()); - } else { - assert_eq!(target, compiler.host); - } - - if self.config.rust_optimize.is_release() && - // cargo bench/install do not accept `--release` and miri doesn't want it - !matches!(cmd_kind, Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest) - { - cargo.arg("--release"); - } - - // Remove make-related flags to ensure Cargo can correctly set things up - cargo.env_remove("MAKEFLAGS"); - cargo.env_remove("MFLAGS"); - - cargo - } - - /// This will create a `Command` that represents a pending execution of - /// Cargo. This cargo will be configured to use `compiler` as the actual - /// rustc compiler, its output will be scoped by `mode`'s output directory, - /// it will pass the `--target` flag for the specified `target`, and will be - /// executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for commands - /// to be run with Miri. - fn cargo( - &self, - compiler: Compiler, - mode: Mode, - source_type: SourceType, - target: TargetSelection, - cmd_kind: Kind, - ) -> Cargo { - let mut cargo = self.bare_cargo(compiler, mode, target, cmd_kind); - let out_dir = self.stage_out(compiler, mode); - - let mut hostflags = HostFlags::default(); - - // Codegen backends are not yet tracked by -Zbinary-dep-depinfo, - // so we need to explicitly clear out if they've been updated. - for backend in self.codegen_backends(compiler) { - self.clear_if_dirty(&out_dir, &backend); - } - - if cmd_kind == Kind::Doc { - let my_out = match mode { - // This is the intended out directory for compiler documentation. - Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target), - Mode::Std => { - if self.config.cmd.json() { - out_dir.join(target).join("json-doc") - } else { - out_dir.join(target).join("doc") - } - } - _ => panic!("doc mode {mode:?} not expected"), - }; - let rustdoc = self.rustdoc(compiler); - self.clear_if_dirty(&my_out, &rustdoc); - } - - let profile_var = |name: &str| { - let profile = if self.config.rust_optimize.is_release() { "RELEASE" } else { "DEV" }; - format!("CARGO_PROFILE_{}_{}", profile, name) - }; - - // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config - // needs to not accidentally link to libLLVM in stage0/lib. - cargo.env("REAL_LIBRARY_PATH_VAR", helpers::dylib_path_var()); - if let Some(e) = env::var_os(helpers::dylib_path_var()) { - cargo.env("REAL_LIBRARY_PATH", e); - } - - // Set a flag for `check`/`clippy`/`fix`, so that certain build - // scripts can do less work (i.e. not building/requiring LLVM). - if matches!(cmd_kind, Kind::Check | Kind::Clippy | Kind::Fix) { - // If we've not yet built LLVM, or it's stale, then bust - // the rustc_llvm cache. That will always work, even though it - // may mean that on the next non-check build we'll need to rebuild - // rustc_llvm. But if LLVM is stale, that'll be a tiny amount - // of work comparatively, and we'd likely need to rebuild it anyway, - // so that's okay. - if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target, false) - .should_build() - { - cargo.env("RUST_CHECK", "1"); - } - } - - let stage = if compiler.stage == 0 && self.local_rebuild { - // Assume the local-rebuild rustc already has stage1 features. - 1 - } else { - compiler.stage - }; - - // We synthetically interpret a stage0 compiler used to build tools as a - // "raw" compiler in that it's the exact snapshot we download. Normally - // the stage0 build means it uses libraries build by the stage0 - // compiler, but for tools we just use the precompiled libraries that - // we've downloaded - let use_snapshot = mode == Mode::ToolBootstrap; - assert!(!use_snapshot || stage == 0 || self.local_rebuild); - - let maybe_sysroot = self.sysroot(compiler); - let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot }; - let libdir = self.rustc_libdir(compiler); - - let sysroot_str = sysroot.as_os_str().to_str().expect("sysroot should be UTF-8"); - if self.is_verbose() && !matches!(self.config.dry_run, DryRun::SelfCheck) { - println!("using sysroot {sysroot_str}"); - } - - let mut rustflags = Rustflags::new(target); - if stage != 0 { - if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") { - cargo.args(s.split_whitespace()); - } - rustflags.env("RUSTFLAGS_NOT_BOOTSTRAP"); - } else { - if let Ok(s) = env::var("CARGOFLAGS_BOOTSTRAP") { - cargo.args(s.split_whitespace()); - } - rustflags.env("RUSTFLAGS_BOOTSTRAP"); - rustflags.arg("--cfg=bootstrap"); - } - - if cmd_kind == Kind::Clippy { - // clippy overwrites sysroot if we pass it to cargo. - // Pass it directly to clippy instead. - // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`, - // so it has no way of knowing the sysroot. - rustflags.arg("--sysroot"); - rustflags.arg(sysroot_str); - } - - let use_new_symbol_mangling = match self.config.rust_new_symbol_mangling { - Some(setting) => { - // If an explicit setting is given, use that - setting - } - None => { - if mode == Mode::Std { - // The standard library defaults to the legacy scheme - false - } else { - // The compiler and tools default to the new scheme - true - } - } - }; - - // By default, windows-rs depends on a native library that doesn't get copied into the - // sysroot. Passing this cfg enables raw-dylib support instead, which makes the native - // library unnecessary. This can be removed when windows-rs enables raw-dylib - // unconditionally. - if let Mode::Rustc | Mode::ToolRustc = mode { - rustflags.arg("--cfg=windows_raw_dylib"); - } - - if use_new_symbol_mangling { - rustflags.arg("-Csymbol-mangling-version=v0"); - } else { - rustflags.arg("-Csymbol-mangling-version=legacy"); - } - - // FIXME: the following components don't build with `-Zrandomize-layout` yet: - // - wasm-component-ld, due to the `wast`crate - // - rust-analyzer, due to the rowan crate - // so we exclude entire categories of steps here due to lack of fine-grained control over - // rustflags. - if self.config.rust_randomize_layout && mode != Mode::ToolStd && mode != Mode::ToolRustc { - rustflags.arg("-Zrandomize-layout"); - } - - // Enable compile-time checking of `cfg` names, values and Cargo `features`. - // - // Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like - // backtrace, core_simd, std_float, ...), those dependencies have their own - // features but cargo isn't involved in the #[path] process and so cannot pass the - // complete list of features, so for that reason we don't enable checking of - // features for std crates. - if mode == Mode::Std { - rustflags.arg("--check-cfg=cfg(feature,values(any()))"); - } - - // Add extra cfg not defined in/by rustc - // - // Note: Although it would seems that "-Zunstable-options" to `rustflags` is useless as - // cargo would implicitly add it, it was discover that sometimes bootstrap only use - // `rustflags` without `cargo` making it required. - rustflags.arg("-Zunstable-options"); - for (restricted_mode, name, values) in EXTRA_CHECK_CFGS { - if restricted_mode.is_none() || *restricted_mode == Some(mode) { - rustflags.arg(&check_cfg_arg(name, *values)); - } - } - - // FIXME(rust-lang/cargo#5754) we shouldn't be using special command arguments - // to the host invocation here, but rather Cargo should know what flags to pass rustc - // itself. - if stage == 0 { - hostflags.arg("--cfg=bootstrap"); - } - // Cargo doesn't pass RUSTFLAGS to proc_macros: - // https://github.com/rust-lang/cargo/issues/4423 - // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`. - // We also declare that the flag is expected, which we need to do to not - // get warnings about it being unexpected. - hostflags.arg("-Zunstable-options"); - hostflags.arg("--check-cfg=cfg(bootstrap)"); - - // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`, - // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See - // #71458. - let mut rustdocflags = rustflags.clone(); - rustdocflags.propagate_cargo_env("RUSTDOCFLAGS"); - if stage == 0 { - rustdocflags.env("RUSTDOCFLAGS_BOOTSTRAP"); - } else { - rustdocflags.env("RUSTDOCFLAGS_NOT_BOOTSTRAP"); - } - - if let Ok(s) = env::var("CARGOFLAGS") { - cargo.args(s.split_whitespace()); - } - - match mode { - Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {} - Mode::Rustc | Mode::Codegen | Mode::ToolRustc => { - // Build proc macros both for the host and the target unless proc-macros are not - // supported by the target. - if target != compiler.host && cmd_kind != Kind::Check { - let error = command(self.rustc(compiler)) - .arg("--target") - .arg(target.rustc_target_arg()) - .arg("--print=file-names") - .arg("--crate-type=proc-macro") - .arg("-") - .run_capture(self) - .stderr(); - let not_supported = error - .lines() - .any(|line| line.contains("unsupported crate type `proc-macro`")); - if !not_supported { - cargo.arg("-Zdual-proc-macros"); - rustflags.arg("-Zdual-proc-macros"); - } - } - } - } - - // This tells Cargo (and in turn, rustc) to output more complete - // dependency information. Most importantly for bootstrap, this - // includes sysroot artifacts, like libstd, which means that we don't - // need to track those in bootstrap (an error prone process!). This - // feature is currently unstable as there may be some bugs and such, but - // it represents a big improvement in bootstrap's reliability on - // rebuilds, so we're using it here. - // - // For some additional context, see #63470 (the PR originally adding - // this), as well as #63012 which is the tracking issue for this - // feature on the rustc side. - cargo.arg("-Zbinary-dep-depinfo"); - let allow_features = match mode { - Mode::ToolBootstrap | Mode::ToolStd => { - // Restrict the allowed features so we don't depend on nightly - // accidentally. - // - // binary-dep-depinfo is used by bootstrap itself for all - // compilations. - // - // Lots of tools depend on proc_macro2 and proc-macro-error. - // Those have build scripts which assume nightly features are - // available if the `rustc` version is "nighty" or "dev". See - // bin/rustc.rs for why that is a problem. Instead of labeling - // those features for each individual tool that needs them, - // just blanket allow them here. - // - // If this is ever removed, be sure to add something else in - // its place to keep the restrictions in place (or make a way - // to unset RUSTC_BOOTSTRAP). - "binary-dep-depinfo,proc_macro_span,proc_macro_span_shrink,proc_macro_diagnostic" - .to_string() - } - Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => String::new(), - }; - - cargo.arg("-j").arg(self.jobs().to_string()); - - // FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005 - // Force cargo to output binaries with disambiguating hashes in the name - let mut metadata = if compiler.stage == 0 { - // Treat stage0 like a special channel, whether it's a normal prior- - // release rustc or a local rebuild with the same version, so we - // never mix these libraries by accident. - "bootstrap".to_string() - } else { - self.config.channel.to_string() - }; - // We want to make sure that none of the dependencies between - // std/test/rustc unify with one another. This is done for weird linkage - // reasons but the gist of the problem is that if librustc, libtest, and - // libstd all depend on libc from crates.io (which they actually do) we - // want to make sure they all get distinct versions. Things get really - // weird if we try to unify all these dependencies right now, namely - // around how many times the library is linked in dynamic libraries and - // such. If rustc were a static executable or if we didn't ship dylibs - // this wouldn't be a problem, but we do, so it is. This is in general - // just here to make sure things build right. If you can remove this and - // things still build right, please do! - match mode { - Mode::Std => metadata.push_str("std"), - // When we're building rustc tools, they're built with a search path - // that contains things built during the rustc build. For example, - // bitflags is built during the rustc build, and is a dependency of - // rustdoc as well. We're building rustdoc in a different target - // directory, though, which means that Cargo will rebuild the - // dependency. When we go on to build rustdoc, we'll look for - // bitflags, and find two different copies: one built during the - // rustc step and one that we just built. This isn't always a - // problem, somehow -- not really clear why -- but we know that this - // fixes things. - Mode::ToolRustc => metadata.push_str("tool-rustc"), - // Same for codegen backends. - Mode::Codegen => metadata.push_str("codegen"), - _ => {} - } - cargo.env("__CARGO_DEFAULT_LIB_METADATA", &metadata); - - if cmd_kind == Kind::Clippy { - rustflags.arg("-Zforce-unstable-if-unmarked"); - } - - rustflags.arg("-Zmacro-backtrace"); - - let want_rustdoc = self.doc_tests != DocTests::No; - - // Clear the output directory if the real rustc we're using has changed; - // Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc. - // - // Avoid doing this during dry run as that usually means the relevant - // compiler is not yet linked/copied properly. - // - // Only clear out the directory if we're compiling std; otherwise, we - // should let Cargo take care of things for us (via depdep info) - if !self.config.dry_run() && mode == Mode::Std && cmd_kind == Kind::Build { - self.clear_if_dirty(&out_dir, &self.rustc(compiler)); - } - - let rustdoc_path = match cmd_kind { - Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc(compiler), - _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"), - }; - - // Customize the compiler we're running. Specify the compiler to cargo - // as our shim and then pass it some various options used to configure - // how the actual compiler itself is called. - // - // These variables are primarily all read by - // src/bootstrap/bin/{rustc.rs,rustdoc.rs} - cargo - .env("RUSTBUILD_NATIVE_DIR", self.native_dir(target)) - .env("RUSTC_REAL", self.rustc(compiler)) - .env("RUSTC_STAGE", stage.to_string()) - .env("RUSTC_SYSROOT", sysroot) - .env("RUSTC_LIBDIR", libdir) - .env("RUSTDOC", self.bootstrap_out.join("rustdoc")) - .env("RUSTDOC_REAL", rustdoc_path) - .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir()) - .env("RUSTC_BREAK_ON_ICE", "1"); - - // Set RUSTC_WRAPPER to the bootstrap shim, which switches between beta and in-tree - // sysroot depending on whether we're building build scripts. - // NOTE: we intentionally use RUSTC_WRAPPER so that we can support clippy - RUSTC is not - // respected by clippy-driver; RUSTC_WRAPPER happens earlier, before clippy runs. - cargo.env("RUSTC_WRAPPER", self.bootstrap_out.join("rustc")); - // NOTE: we also need to set RUSTC so cargo can run `rustc -vV`; apparently that ignores RUSTC_WRAPPER >:( - cargo.env("RUSTC", self.bootstrap_out.join("rustc")); - - // Someone might have set some previous rustc wrapper (e.g. - // sccache) before bootstrap overrode it. Respect that variable. - if let Some(existing_wrapper) = env::var_os("RUSTC_WRAPPER") { - cargo.env("RUSTC_WRAPPER_REAL", existing_wrapper); - } - - // If this is for `miri-test`, prepare the sysroots. - if cmd_kind == Kind::MiriTest { - self.ensure(compile::Std::new(compiler, compiler.host)); - let host_sysroot = self.sysroot(compiler); - let miri_sysroot = test::Miri::build_miri_sysroot(self, compiler, target); - cargo.env("MIRI_SYSROOT", &miri_sysroot); - cargo.env("MIRI_HOST_SYSROOT", &host_sysroot); - } - - cargo.env(profile_var("STRIP"), self.config.rust_strip.to_string()); - - if let Some(stack_protector) = &self.config.rust_stack_protector { - rustflags.arg(&format!("-Zstack-protector={stack_protector}")); - } - - if !matches!(cmd_kind, Kind::Build | Kind::Check | Kind::Clippy | Kind::Fix) && want_rustdoc - { - cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler)); - } - - let debuginfo_level = match mode { - Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc, - Mode::Std => self.config.rust_debuginfo_level_std, - Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc => { - self.config.rust_debuginfo_level_tools - } - }; - cargo.env(profile_var("DEBUG"), debuginfo_level.to_string()); - if let Some(opt_level) = &self.config.rust_optimize.get_opt_level() { - cargo.env(profile_var("OPT_LEVEL"), opt_level); - } - cargo.env( - profile_var("DEBUG_ASSERTIONS"), - if mode == Mode::Std { - self.config.rust_debug_assertions_std.to_string() - } else { - self.config.rust_debug_assertions.to_string() - }, - ); - cargo.env( - profile_var("OVERFLOW_CHECKS"), - if mode == Mode::Std { - self.config.rust_overflow_checks_std.to_string() - } else { - self.config.rust_overflow_checks.to_string() - }, - ); - - match self.config.split_debuginfo(target) { - SplitDebuginfo::Packed => rustflags.arg("-Csplit-debuginfo=packed"), - SplitDebuginfo::Unpacked => rustflags.arg("-Csplit-debuginfo=unpacked"), - SplitDebuginfo::Off => rustflags.arg("-Csplit-debuginfo=off"), - }; - - if self.config.cmd.bless() { - // Bless `expect!` tests. - cargo.env("UPDATE_EXPECT", "1"); - } - - if !mode.is_tool() { - cargo.env("RUSTC_FORCE_UNSTABLE", "1"); - } - - if let Some(x) = self.crt_static(target) { - if x { - rustflags.arg("-Ctarget-feature=+crt-static"); - } else { - rustflags.arg("-Ctarget-feature=-crt-static"); - } - } - - if let Some(x) = self.crt_static(compiler.host) { - let sign = if x { "+" } else { "-" }; - hostflags.arg(format!("-Ctarget-feature={sign}crt-static")); - } - - if let Some(map_to) = self.build.debuginfo_map_to(GitRepo::Rustc) { - let map = format!("{}={}", self.build.src.display(), map_to); - cargo.env("RUSTC_DEBUGINFO_MAP", map); - - // `rustc` needs to know the virtual `/rustc/$hash` we're mapping to, - // in order to opportunistically reverse it later. - cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to); - } - - if self.config.rust_remap_debuginfo { - let mut env_var = OsString::new(); - if self.config.vendor { - let vendor = self.build.src.join("vendor"); - env_var.push(vendor); - env_var.push("=/rust/deps"); - } else { - let registry_src = t!(home::cargo_home()).join("registry").join("src"); - for entry in t!(std::fs::read_dir(registry_src)) { - if !env_var.is_empty() { - env_var.push("\t"); - } - env_var.push(t!(entry).path()); - env_var.push("=/rust/deps"); - } - } - cargo.env("RUSTC_CARGO_REGISTRY_SRC_TO_REMAP", env_var); - } - - // Enable usage of unstable features - cargo.env("RUSTC_BOOTSTRAP", "1"); - - if self.config.dump_bootstrap_shims { - prepare_behaviour_dump_dir(self.build); - - cargo - .env("DUMP_BOOTSTRAP_SHIMS", self.build.out.join("bootstrap-shims-dump")) - .env("BUILD_OUT", &self.build.out) - .env("CARGO_HOME", t!(home::cargo_home())); - }; - - self.add_rust_test_threads(&mut cargo); - - // Almost all of the crates that we compile as part of the bootstrap may - // have a build script, including the standard library. To compile a - // build script, however, it itself needs a standard library! This - // introduces a bit of a pickle when we're compiling the standard - // library itself. - // - // To work around this we actually end up using the snapshot compiler - // (stage0) for compiling build scripts of the standard library itself. - // The stage0 compiler is guaranteed to have a libstd available for use. - // - // For other crates, however, we know that we've already got a standard - // library up and running, so we can use the normal compiler to compile - // build scripts in that situation. - if mode == Mode::Std { - cargo - .env("RUSTC_SNAPSHOT", &self.initial_rustc) - .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir()); - } else { - cargo - .env("RUSTC_SNAPSHOT", self.rustc(compiler)) - .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_libdir(compiler)); - } - - // Tools that use compiler libraries may inherit the `-lLLVM` link - // requirement, but the `-L` library path is not propagated across - // separate Cargo projects. We can add LLVM's library path to the - // platform-specific environment variable as a workaround. - if mode == Mode::ToolRustc || mode == Mode::Codegen { - if let Some(llvm_config) = self.llvm_config(target) { - let llvm_libdir = - command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout(); - add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo); - } - } - - // Compile everything except libraries and proc macros with the more - // efficient initial-exec TLS model. This doesn't work with `dlopen`, - // so we can't use it by default in general, but we can use it for tools - // and our own internal libraries. - if !mode.must_support_dlopen() && !target.triple.starts_with("powerpc-") { - cargo.env("RUSTC_TLS_MODEL_INITIAL_EXEC", "1"); - } - - // Ignore incremental modes except for stage0, since we're - // not guaranteeing correctness across builds if the compiler - // is changing under your feet. - if self.config.incremental && compiler.stage == 0 { - cargo.env("CARGO_INCREMENTAL", "1"); - } else { - // Don't rely on any default setting for incr. comp. in Cargo - cargo.env("CARGO_INCREMENTAL", "0"); - } - - if let Some(ref on_fail) = self.config.on_fail { - cargo.env("RUSTC_ON_FAIL", on_fail); - } - - if self.config.print_step_timings { - cargo.env("RUSTC_PRINT_STEP_TIMINGS", "1"); - } - - if self.config.print_step_rusage { - cargo.env("RUSTC_PRINT_STEP_RUSAGE", "1"); - } - - if self.config.backtrace_on_ice { - cargo.env("RUSTC_BACKTRACE_ON_ICE", "1"); - } - - if self.is_verbose() { - // This provides very useful logs especially when debugging build cache-related stuff. - cargo.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); - } - - cargo.env("RUSTC_VERBOSE", self.verbosity.to_string()); - - // Downstream forks of the Rust compiler might want to use a custom libc to add support for - // targets that are not yet available upstream. Adding a patch to replace libc with a - // custom one would cause compilation errors though, because Cargo would interpret the - // custom libc as part of the workspace, and apply the check-cfg lints on it. - // - // The libc build script emits check-cfg flags only when this environment variable is set, - // so this line allows the use of custom libcs. - cargo.env("LIBC_CHECK_CFG", "1"); - - if source_type == SourceType::InTree { - let mut lint_flags = Vec::new(); - // When extending this list, add the new lints to the RUSTFLAGS of the - // build_bootstrap function of src/bootstrap/bootstrap.py as well as - // some code doesn't go through this `rustc` wrapper. - lint_flags.push("-Wrust_2018_idioms"); - lint_flags.push("-Wunused_lifetimes"); - - if self.config.deny_warnings { - lint_flags.push("-Dwarnings"); - rustdocflags.arg("-Dwarnings"); - } - - // This does not use RUSTFLAGS due to caching issues with Cargo. - // Clippy is treated as an "in tree" tool, but shares the same - // cache as other "submodule" tools. With these options set in - // RUSTFLAGS, that causes *every* shared dependency to be rebuilt. - // By injecting this into the rustc wrapper, this circumvents - // Cargo's fingerprint detection. This is fine because lint flags - // are always ignored in dependencies. Eventually this should be - // fixed via better support from Cargo. - cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" ")); - - rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes"); - } - - if mode == Mode::Rustc { - rustflags.arg("-Wrustc::internal"); - // FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all - // of the individual lints are satisfied. - rustflags.arg("-Wkeyword_idents_2024"); - rustflags.arg("-Wunsafe_op_in_unsafe_fn"); - } - - if self.config.rust_frame_pointers { - rustflags.arg("-Cforce-frame-pointers=true"); - } - - // If Control Flow Guard is enabled, pass the `control-flow-guard` flag to rustc - // when compiling the standard library, since this might be linked into the final outputs - // produced by rustc. Since this mitigation is only available on Windows, only enable it - // for the standard library in case the compiler is run on a non-Windows platform. - // This is not needed for stage 0 artifacts because these will only be used for building - // the stage 1 compiler. - if cfg!(windows) - && mode == Mode::Std - && self.config.control_flow_guard - && compiler.stage >= 1 - { - rustflags.arg("-Ccontrol-flow-guard"); - } - - // If EHCont Guard is enabled, pass the `-Zehcont-guard` flag to rustc when compiling the - // standard library, since this might be linked into the final outputs produced by rustc. - // Since this mitigation is only available on Windows, only enable it for the standard - // library in case the compiler is run on a non-Windows platform. - // This is not needed for stage 0 artifacts because these will only be used for building - // the stage 1 compiler. - if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard && compiler.stage >= 1 { - rustflags.arg("-Zehcont-guard"); - } - - // For `cargo doc` invocations, make rustdoc print the Rust version into the docs - // This replaces spaces with tabs because RUSTDOCFLAGS does not - // support arguments with regular spaces. Hopefully someday Cargo will - // have space support. - let rust_version = self.rust_version().replace(' ', "\t"); - rustdocflags.arg("--crate-version").arg(&rust_version); - - // Environment variables *required* throughout the build - // - // FIXME: should update code to not require this env var - - // The host this new compiler will *run* on. - cargo.env("CFG_COMPILER_HOST_TRIPLE", target.triple); - // The host this new compiler is being *built* on. - cargo.env("CFG_COMPILER_BUILD_TRIPLE", compiler.host.triple); - - // Set this for all builds to make sure doc builds also get it. - cargo.env("CFG_RELEASE_CHANNEL", &self.config.channel); - - // This one's a bit tricky. As of the time of this writing the compiler - // links to the `winapi` crate on crates.io. This crate provides raw - // bindings to Windows system functions, sort of like libc does for - // Unix. This crate also, however, provides "import libraries" for the - // MinGW targets. There's an import library per dll in the windows - // distribution which is what's linked to. These custom import libraries - // are used because the winapi crate can reference Windows functions not - // present in the MinGW import libraries. - // - // For example MinGW may ship libdbghelp.a, but it may not have - // references to all the functions in the dbghelp dll. Instead the - // custom import library for dbghelp in the winapi crates has all this - // information. - // - // Unfortunately for us though the import libraries are linked by - // default via `-ldylib=winapi_foo`. That is, they're linked with the - // `dylib` type with a `winapi_` prefix (so the winapi ones don't - // conflict with the system MinGW ones). This consequently means that - // the binaries we ship of things like rustc_codegen_llvm (aka the rustc_codegen_llvm - // DLL) when linked against *again*, for example with procedural macros - // or plugins, will trigger the propagation logic of `-ldylib`, passing - // `-lwinapi_foo` to the linker again. This isn't actually available in - // our distribution, however, so the link fails. - // - // To solve this problem we tell winapi to not use its bundled import - // libraries. This means that it will link to the system MinGW import - // libraries by default, and the `-ldylib=foo` directives will still get - // passed to the final linker, but they'll look like `-lfoo` which can - // be resolved because MinGW has the import library. The downside is we - // don't get newer functions from Windows, but we don't use any of them - // anyway. - if !mode.is_tool() { - cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1"); - } - - for _ in 0..self.verbosity { - cargo.arg("-v"); - } - - match (mode, self.config.rust_codegen_units_std, self.config.rust_codegen_units) { - (Mode::Std, Some(n), _) | (_, _, Some(n)) => { - cargo.env(profile_var("CODEGEN_UNITS"), n.to_string()); - } - _ => { - // Don't set anything - } - } - - if self.config.locked_deps { - cargo.arg("--locked"); - } - if self.config.vendor || self.is_sudo { - cargo.arg("--frozen"); - } - - // Try to use a sysroot-relative bindir, in case it was configured absolutely. - cargo.env("RUSTC_INSTALL_BINDIR", self.config.bindir_relative()); - - cargo.force_coloring_in_ci(); - - // When we build Rust dylibs they're all intended for intermediate - // usage, so make sure we pass the -Cprefer-dynamic flag instead of - // linking all deps statically into the dylib. - if matches!(mode, Mode::Std) { - rustflags.arg("-Cprefer-dynamic"); - } - if matches!(mode, Mode::Rustc) && !self.link_std_into_rustc_driver(target) { - rustflags.arg("-Cprefer-dynamic"); - } - - cargo.env( - "RUSTC_LINK_STD_INTO_RUSTC_DRIVER", - if self.link_std_into_rustc_driver(target) { "1" } else { "0" }, - ); - - // When building incrementally we default to a lower ThinLTO import limit - // (unless explicitly specified otherwise). This will produce a somewhat - // slower code but give way better compile times. - { - let limit = match self.config.rust_thin_lto_import_instr_limit { - Some(limit) => Some(limit), - None if self.config.incremental => Some(10), - _ => None, - }; - - if let Some(limit) = limit { - if stage == 0 - || self.config.default_codegen_backend(target).unwrap_or_default() == "llvm" - { - rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}")); - } - } - } - - if matches!(mode, Mode::Std) { - if let Some(mir_opt_level) = self.config.rust_validate_mir_opts { - rustflags.arg("-Zvalidate-mir"); - rustflags.arg(&format!("-Zmir-opt-level={mir_opt_level}")); - } - if self.config.rust_randomize_layout { - rustflags.arg("--cfg=randomized_layouts"); - } - // Always enable inlining MIR when building the standard library. - // Without this flag, MIR inlining is disabled when incremental compilation is enabled. - // That causes some mir-opt tests which inline functions from the standard library to - // break when incremental compilation is enabled. So this overrides the "no inlining - // during incremental builds" heuristic for the standard library. - rustflags.arg("-Zinline-mir"); - - // Similarly, we need to keep debug info for functions inlined into other std functions, - // even if we're not going to output debuginfo for the crate we're currently building, - // so that it'll be available when downstream consumers of std try to use it. - rustflags.arg("-Zinline-mir-preserve-debug"); - } - - if self.config.rustc_parallel - && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen) - { - // keep in sync with `bootstrap/lib.rs:Build::rustc_features` - // `cfg` option for rustc, `features` option for cargo, for conditional compilation - rustflags.arg("--cfg=parallel_compiler"); - rustdocflags.arg("--cfg=parallel_compiler"); - } - - Cargo { - command: cargo, - compiler, - target, - rustflags, - rustdocflags, - hostflags, - allow_features, - } - } - /// Ensure that a given step is built, returning its output. This will /// cache the step, so it is safe (and good!) to call this as often as /// needed to ensure that all dependencies are built. @@ -2399,337 +1531,3 @@ impl<'a> Builder<'a> { } } } - -/// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler later. -/// -/// `-Z crate-attr` flags will be applied recursively on the target code using the `rustc_parse::parser::Parser`. -/// See `rustc_builtin_macros::cmdline_attrs::inject` for more information. -#[derive(Debug, Clone)] -struct Rustflags(String, TargetSelection); - -impl Rustflags { - fn new(target: TargetSelection) -> Rustflags { - let mut ret = Rustflags(String::new(), target); - ret.propagate_cargo_env("RUSTFLAGS"); - ret - } - - /// By default, cargo will pick up on various variables in the environment. However, bootstrap - /// reuses those variables to pass additional flags to rustdoc, so by default they get overridden. - /// Explicitly add back any previous value in the environment. - /// - /// `prefix` is usually `RUSTFLAGS` or `RUSTDOCFLAGS`. - fn propagate_cargo_env(&mut self, prefix: &str) { - // Inherit `RUSTFLAGS` by default ... - self.env(prefix); - - // ... and also handle target-specific env RUSTFLAGS if they're configured. - let target_specific = format!("CARGO_TARGET_{}_{}", crate::envify(&self.1.triple), prefix); - self.env(&target_specific); - } - - fn env(&mut self, env: &str) { - if let Ok(s) = env::var(env) { - for part in s.split(' ') { - self.arg(part); - } - } - } - - fn arg(&mut self, arg: &str) -> &mut Self { - assert_eq!(arg.split(' ').count(), 1); - if !self.0.is_empty() { - self.0.push(' '); - } - self.0.push_str(arg); - self - } -} - -/// Flags that are passed to the `rustc` shim binary. -/// These flags will only be applied when compiling host code, i.e. when -/// `--target` is unset. -#[derive(Debug, Default)] -pub struct HostFlags { - rustc: Vec, -} - -impl HostFlags { - const SEPARATOR: &'static str = " "; - - /// Adds a host rustc flag. - fn arg>(&mut self, flag: S) { - let value = flag.into().trim().to_string(); - assert!(!value.contains(Self::SEPARATOR)); - self.rustc.push(value); - } - - /// Encodes all the flags into a single string. - fn encode(self) -> String { - self.rustc.join(Self::SEPARATOR) - } -} - -#[derive(Debug)] -pub struct Cargo { - command: BootstrapCommand, - compiler: Compiler, - target: TargetSelection, - rustflags: Rustflags, - rustdocflags: Rustflags, - hostflags: HostFlags, - allow_features: String, -} - -impl Cargo { - /// Calls `Builder::cargo` and `Cargo::configure_linker` to prepare an invocation of `cargo` to be run. - pub fn new( - builder: &Builder<'_>, - compiler: Compiler, - mode: Mode, - source_type: SourceType, - target: TargetSelection, - cmd_kind: Kind, - ) -> Cargo { - let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd_kind); - - match cmd_kind { - // No need to configure the target linker for these command types, - // as they don't invoke rustc at all. - Kind::Clean | Kind::Suggest | Kind::Format | Kind::Setup => {} - _ => { - cargo.configure_linker(builder); - } - } - - cargo - } - - pub fn into_cmd(self) -> BootstrapCommand { - self.into() - } - - /// Same as `Cargo::new` except this one doesn't configure the linker with `Cargo::configure_linker` - pub fn new_for_mir_opt_tests( - builder: &Builder<'_>, - compiler: Compiler, - mode: Mode, - source_type: SourceType, - target: TargetSelection, - cmd_kind: Kind, - ) -> Cargo { - builder.cargo(compiler, mode, source_type, target, cmd_kind) - } - - pub fn rustdocflag(&mut self, arg: &str) -> &mut Cargo { - self.rustdocflags.arg(arg); - self - } - - pub fn rustflag(&mut self, arg: &str) -> &mut Cargo { - self.rustflags.arg(arg); - self - } - - pub fn arg(&mut self, arg: impl AsRef) -> &mut Cargo { - self.command.arg(arg.as_ref()); - self - } - - pub fn args(&mut self, args: I) -> &mut Cargo - where - I: IntoIterator, - S: AsRef, - { - for arg in args { - self.arg(arg.as_ref()); - } - self - } - - pub fn env(&mut self, key: impl AsRef, value: impl AsRef) -> &mut Cargo { - // These are managed through rustflag/rustdocflag interfaces. - assert_ne!(key.as_ref(), "RUSTFLAGS"); - assert_ne!(key.as_ref(), "RUSTDOCFLAGS"); - self.command.env(key.as_ref(), value.as_ref()); - self - } - - pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>) { - builder.add_rustc_lib_path(self.compiler, &mut self.command); - } - - pub fn current_dir(&mut self, dir: &Path) -> &mut Cargo { - self.command.current_dir(dir); - self - } - - /// Adds nightly-only features that this invocation is allowed to use. - /// - /// By default, all nightly features are allowed. Once this is called, it - /// will be restricted to the given set. - pub fn allow_features(&mut self, features: &str) -> &mut Cargo { - if !self.allow_features.is_empty() { - self.allow_features.push(','); - } - self.allow_features.push_str(features); - self - } - - fn configure_linker(&mut self, builder: &Builder<'_>) -> &mut Cargo { - let target = self.target; - let compiler = self.compiler; - - // Dealing with rpath here is a little special, so let's go into some - // detail. First off, `-rpath` is a linker option on Unix platforms - // which adds to the runtime dynamic loader path when looking for - // dynamic libraries. We use this by default on Unix platforms to ensure - // that our nightlies behave the same on Windows, that is they work out - // of the box. This can be disabled by setting `rpath = false` in `[rust]` - // table of `config.toml` - // - // Ok, so the astute might be wondering "why isn't `-C rpath` used - // here?" and that is indeed a good question to ask. This codegen - // option is the compiler's current interface to generating an rpath. - // Unfortunately it doesn't quite suffice for us. The flag currently - // takes no value as an argument, so the compiler calculates what it - // should pass to the linker as `-rpath`. This unfortunately is based on - // the **compile time** directory structure which when building with - // Cargo will be very different than the runtime directory structure. - // - // All that's a really long winded way of saying that if we use - // `-Crpath` then the executables generated have the wrong rpath of - // something like `$ORIGIN/deps` when in fact the way we distribute - // rustc requires the rpath to be `$ORIGIN/../lib`. - // - // So, all in all, to set up the correct rpath we pass the linker - // argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it - // fun to pass a flag to a tool to pass a flag to pass a flag to a tool - // to change a flag in a binary? - if builder.config.rpath_enabled(target) && helpers::use_host_linker(target) { - let libdir = builder.sysroot_libdir_relative(compiler).to_str().unwrap(); - let rpath = if target.contains("apple") { - // Note that we need to take one extra step on macOS to also pass - // `-Wl,-instal_name,@rpath/...` to get things to work right. To - // do that we pass a weird flag to the compiler to get it to do - // so. Note that this is definitely a hack, and we should likely - // flesh out rpath support more fully in the future. - self.rustflags.arg("-Zosx-rpath-install-name"); - Some(format!("-Wl,-rpath,@loader_path/../{libdir}")) - } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") { - self.rustflags.arg("-Clink-args=-Wl,-z,origin"); - Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}")) - } else { - None - }; - if let Some(rpath) = rpath { - self.rustflags.arg(&format!("-Clink-args={rpath}")); - } - } - - for arg in linker_args(builder, compiler.host, LldThreads::Yes) { - self.hostflags.arg(&arg); - } - - if let Some(target_linker) = builder.linker(target) { - let target = crate::envify(&target.triple); - self.command.env(format!("CARGO_TARGET_{target}_LINKER"), target_linker); - } - // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not - // `linker_args` here. - for flag in linker_flags(builder, target, LldThreads::Yes) { - self.rustflags.arg(&flag); - } - for arg in linker_args(builder, target, LldThreads::Yes) { - self.rustdocflags.arg(&arg); - } - - if !builder.config.dry_run() - && builder.cc.borrow()[&target].args().iter().any(|arg| arg == "-gz") - { - self.rustflags.arg("-Clink-arg=-gz"); - } - - // Throughout the build Cargo can execute a number of build scripts - // compiling C/C++ code and we need to pass compilers, archivers, flags, etc - // obtained previously to those build scripts. - // Build scripts use either the `cc` crate or `configure/make` so we pass - // the options through environment variables that are fetched and understood by both. - // - // FIXME: the guard against msvc shouldn't need to be here - if target.is_msvc() { - if let Some(ref cl) = builder.config.llvm_clang_cl { - // FIXME: There is a bug in Clang 18 when building for ARM64: - // https://github.com/llvm/llvm-project/pull/81849. This is - // fixed in LLVM 19, but can't be backported. - if !target.starts_with("aarch64") && !target.starts_with("arm64ec") { - self.command.env("CC", cl).env("CXX", cl); - } - } - } else { - let ccache = builder.config.ccache.as_ref(); - let ccacheify = |s: &Path| { - let ccache = match ccache { - Some(ref s) => s, - None => return s.display().to_string(), - }; - // FIXME: the cc-rs crate only recognizes the literal strings - // `ccache` and `sccache` when doing caching compilations, so we - // mirror that here. It should probably be fixed upstream to - // accept a new env var or otherwise work with custom ccache - // vars. - match &ccache[..] { - "ccache" | "sccache" => format!("{} {}", ccache, s.display()), - _ => s.display().to_string(), - } - }; - let triple_underscored = target.triple.replace('-', "_"); - let cc = ccacheify(&builder.cc(target)); - self.command.env(format!("CC_{triple_underscored}"), &cc); - - let cflags = builder.cflags(target, GitRepo::Rustc, CLang::C).join(" "); - self.command.env(format!("CFLAGS_{triple_underscored}"), &cflags); - - if let Some(ar) = builder.ar(target) { - let ranlib = format!("{} s", ar.display()); - self.command - .env(format!("AR_{triple_underscored}"), ar) - .env(format!("RANLIB_{triple_underscored}"), ranlib); - } - - if let Ok(cxx) = builder.cxx(target) { - let cxx = ccacheify(&cxx); - let cxxflags = builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" "); - self.command - .env(format!("CXX_{triple_underscored}"), &cxx) - .env(format!("CXXFLAGS_{triple_underscored}"), cxxflags); - } - } - - self - } -} - -impl From for BootstrapCommand { - fn from(mut cargo: Cargo) -> BootstrapCommand { - let rustflags = &cargo.rustflags.0; - if !rustflags.is_empty() { - cargo.command.env("RUSTFLAGS", rustflags); - } - - let rustdocflags = &cargo.rustdocflags.0; - if !rustdocflags.is_empty() { - cargo.command.env("RUSTDOCFLAGS", rustdocflags); - } - - let encoded_hostflags = cargo.hostflags.encode(); - if !encoded_hostflags.is_empty() { - cargo.command.env("RUSTC_HOST_FLAGS", encoded_hostflags); - } - - if !cargo.allow_features.is_empty() { - cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features); - } - cargo.command - } -} diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs new file mode 100644 index 00000000000..b33545341cc --- /dev/null +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -0,0 +1,1217 @@ +use std::env; +use std::ffi::{OsStr, OsString}; +use std::path::{Path, PathBuf}; + +use super::{Builder, Kind}; +use crate::core::build_steps::tool::SourceType; +use crate::core::build_steps::{compile, test}; +use crate::core::config::SplitDebuginfo; +use crate::core::config::flags::Color; +use crate::utils::helpers::{ + self, LldThreads, add_link_lib_path, check_cfg_arg, linker_args, linker_flags, +}; +use crate::{ + BootstrapCommand, CLang, Compiler, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode, + TargetSelection, command, prepare_behaviour_dump_dir, t, +}; + +/// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler +/// later. +/// +/// `-Z crate-attr` flags will be applied recursively on the target code using the +/// `rustc_parse::parser::Parser`. See `rustc_builtin_macros::cmdline_attrs::inject` for more +/// information. +#[derive(Debug, Clone)] +struct Rustflags(String, TargetSelection); + +impl Rustflags { + fn new(target: TargetSelection) -> Rustflags { + let mut ret = Rustflags(String::new(), target); + ret.propagate_cargo_env("RUSTFLAGS"); + ret + } + + /// By default, cargo will pick up on various variables in the environment. However, bootstrap + /// reuses those variables to pass additional flags to rustdoc, so by default they get + /// overridden. Explicitly add back any previous value in the environment. + /// + /// `prefix` is usually `RUSTFLAGS` or `RUSTDOCFLAGS`. + fn propagate_cargo_env(&mut self, prefix: &str) { + // Inherit `RUSTFLAGS` by default ... + self.env(prefix); + + // ... and also handle target-specific env RUSTFLAGS if they're configured. + let target_specific = format!("CARGO_TARGET_{}_{}", crate::envify(&self.1.triple), prefix); + self.env(&target_specific); + } + + fn env(&mut self, env: &str) { + if let Ok(s) = env::var(env) { + for part in s.split(' ') { + self.arg(part); + } + } + } + + fn arg(&mut self, arg: &str) -> &mut Self { + assert_eq!(arg.split(' ').count(), 1); + if !self.0.is_empty() { + self.0.push(' '); + } + self.0.push_str(arg); + self + } +} + +/// Flags that are passed to the `rustc` shim binary. These flags will only be applied when +/// compiling host code, i.e. when `--target` is unset. +#[derive(Debug, Default)] +struct HostFlags { + rustc: Vec, +} + +impl HostFlags { + const SEPARATOR: &'static str = " "; + + /// Adds a host rustc flag. + fn arg>(&mut self, flag: S) { + let value = flag.into().trim().to_string(); + assert!(!value.contains(Self::SEPARATOR)); + self.rustc.push(value); + } + + /// Encodes all the flags into a single string. + fn encode(self) -> String { + self.rustc.join(Self::SEPARATOR) + } +} + +#[derive(Debug)] +pub struct Cargo { + command: BootstrapCommand, + compiler: Compiler, + target: TargetSelection, + rustflags: Rustflags, + rustdocflags: Rustflags, + hostflags: HostFlags, + allow_features: String, +} + +impl Cargo { + /// Calls `Builder::cargo` and `Cargo::configure_linker` to prepare an invocation of `cargo` to be run. + pub fn new( + builder: &Builder<'_>, + compiler: Compiler, + mode: Mode, + source_type: SourceType, + target: TargetSelection, + cmd_kind: Kind, + ) -> Cargo { + let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd_kind); + + match cmd_kind { + // No need to configure the target linker for these command types, + // as they don't invoke rustc at all. + Kind::Clean | Kind::Suggest | Kind::Format | Kind::Setup => {} + _ => { + cargo.configure_linker(builder); + } + } + + cargo + } + + pub fn into_cmd(self) -> BootstrapCommand { + self.into() + } + + /// Same as `Cargo::new` except this one doesn't configure the linker with `Cargo::configure_linker` + pub fn new_for_mir_opt_tests( + builder: &Builder<'_>, + compiler: Compiler, + mode: Mode, + source_type: SourceType, + target: TargetSelection, + cmd_kind: Kind, + ) -> Cargo { + builder.cargo(compiler, mode, source_type, target, cmd_kind) + } + + pub fn rustdocflag(&mut self, arg: &str) -> &mut Cargo { + self.rustdocflags.arg(arg); + self + } + + pub fn rustflag(&mut self, arg: &str) -> &mut Cargo { + self.rustflags.arg(arg); + self + } + + pub fn arg(&mut self, arg: impl AsRef) -> &mut Cargo { + self.command.arg(arg.as_ref()); + self + } + + pub fn args(&mut self, args: I) -> &mut Cargo + where + I: IntoIterator, + S: AsRef, + { + for arg in args { + self.arg(arg.as_ref()); + } + self + } + + pub fn env(&mut self, key: impl AsRef, value: impl AsRef) -> &mut Cargo { + // These are managed through rustflag/rustdocflag interfaces. + assert_ne!(key.as_ref(), "RUSTFLAGS"); + assert_ne!(key.as_ref(), "RUSTDOCFLAGS"); + self.command.env(key.as_ref(), value.as_ref()); + self + } + + pub fn add_rustc_lib_path(&mut self, builder: &Builder<'_>) { + builder.add_rustc_lib_path(self.compiler, &mut self.command); + } + + pub fn current_dir(&mut self, dir: &Path) -> &mut Cargo { + self.command.current_dir(dir); + self + } + + /// Adds nightly-only features that this invocation is allowed to use. + /// + /// By default, all nightly features are allowed. Once this is called, it + /// will be restricted to the given set. + pub fn allow_features(&mut self, features: &str) -> &mut Cargo { + if !self.allow_features.is_empty() { + self.allow_features.push(','); + } + self.allow_features.push_str(features); + self + } + + fn configure_linker(&mut self, builder: &Builder<'_>) -> &mut Cargo { + let target = self.target; + let compiler = self.compiler; + + // Dealing with rpath here is a little special, so let's go into some + // detail. First off, `-rpath` is a linker option on Unix platforms + // which adds to the runtime dynamic loader path when looking for + // dynamic libraries. We use this by default on Unix platforms to ensure + // that our nightlies behave the same on Windows, that is they work out + // of the box. This can be disabled by setting `rpath = false` in `[rust]` + // table of `config.toml` + // + // Ok, so the astute might be wondering "why isn't `-C rpath` used + // here?" and that is indeed a good question to ask. This codegen + // option is the compiler's current interface to generating an rpath. + // Unfortunately it doesn't quite suffice for us. The flag currently + // takes no value as an argument, so the compiler calculates what it + // should pass to the linker as `-rpath`. This unfortunately is based on + // the **compile time** directory structure which when building with + // Cargo will be very different than the runtime directory structure. + // + // All that's a really long winded way of saying that if we use + // `-Crpath` then the executables generated have the wrong rpath of + // something like `$ORIGIN/deps` when in fact the way we distribute + // rustc requires the rpath to be `$ORIGIN/../lib`. + // + // So, all in all, to set up the correct rpath we pass the linker + // argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it + // fun to pass a flag to a tool to pass a flag to pass a flag to a tool + // to change a flag in a binary? + if builder.config.rpath_enabled(target) && helpers::use_host_linker(target) { + let libdir = builder.sysroot_libdir_relative(compiler).to_str().unwrap(); + let rpath = if target.contains("apple") { + // Note that we need to take one extra step on macOS to also pass + // `-Wl,-instal_name,@rpath/...` to get things to work right. To + // do that we pass a weird flag to the compiler to get it to do + // so. Note that this is definitely a hack, and we should likely + // flesh out rpath support more fully in the future. + self.rustflags.arg("-Zosx-rpath-install-name"); + Some(format!("-Wl,-rpath,@loader_path/../{libdir}")) + } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") { + self.rustflags.arg("-Clink-args=-Wl,-z,origin"); + Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}")) + } else { + None + }; + if let Some(rpath) = rpath { + self.rustflags.arg(&format!("-Clink-args={rpath}")); + } + } + + for arg in linker_args(builder, compiler.host, LldThreads::Yes) { + self.hostflags.arg(&arg); + } + + if let Some(target_linker) = builder.linker(target) { + let target = crate::envify(&target.triple); + self.command.env(format!("CARGO_TARGET_{target}_LINKER"), target_linker); + } + // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not + // `linker_args` here. + for flag in linker_flags(builder, target, LldThreads::Yes) { + self.rustflags.arg(&flag); + } + for arg in linker_args(builder, target, LldThreads::Yes) { + self.rustdocflags.arg(&arg); + } + + if !builder.config.dry_run() + && builder.cc.borrow()[&target].args().iter().any(|arg| arg == "-gz") + { + self.rustflags.arg("-Clink-arg=-gz"); + } + + // Throughout the build Cargo can execute a number of build scripts + // compiling C/C++ code and we need to pass compilers, archivers, flags, etc + // obtained previously to those build scripts. + // Build scripts use either the `cc` crate or `configure/make` so we pass + // the options through environment variables that are fetched and understood by both. + // + // FIXME: the guard against msvc shouldn't need to be here + if target.is_msvc() { + if let Some(ref cl) = builder.config.llvm_clang_cl { + // FIXME: There is a bug in Clang 18 when building for ARM64: + // https://github.com/llvm/llvm-project/pull/81849. This is + // fixed in LLVM 19, but can't be backported. + if !target.starts_with("aarch64") && !target.starts_with("arm64ec") { + self.command.env("CC", cl).env("CXX", cl); + } + } + } else { + let ccache = builder.config.ccache.as_ref(); + let ccacheify = |s: &Path| { + let ccache = match ccache { + Some(ref s) => s, + None => return s.display().to_string(), + }; + // FIXME: the cc-rs crate only recognizes the literal strings + // `ccache` and `sccache` when doing caching compilations, so we + // mirror that here. It should probably be fixed upstream to + // accept a new env var or otherwise work with custom ccache + // vars. + match &ccache[..] { + "ccache" | "sccache" => format!("{} {}", ccache, s.display()), + _ => s.display().to_string(), + } + }; + let triple_underscored = target.triple.replace('-', "_"); + let cc = ccacheify(&builder.cc(target)); + self.command.env(format!("CC_{triple_underscored}"), &cc); + + let cflags = builder.cflags(target, GitRepo::Rustc, CLang::C).join(" "); + self.command.env(format!("CFLAGS_{triple_underscored}"), &cflags); + + if let Some(ar) = builder.ar(target) { + let ranlib = format!("{} s", ar.display()); + self.command + .env(format!("AR_{triple_underscored}"), ar) + .env(format!("RANLIB_{triple_underscored}"), ranlib); + } + + if let Ok(cxx) = builder.cxx(target) { + let cxx = ccacheify(&cxx); + let cxxflags = builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" "); + self.command + .env(format!("CXX_{triple_underscored}"), &cxx) + .env(format!("CXXFLAGS_{triple_underscored}"), cxxflags); + } + } + + self + } +} + +impl From for BootstrapCommand { + fn from(mut cargo: Cargo) -> BootstrapCommand { + let rustflags = &cargo.rustflags.0; + if !rustflags.is_empty() { + cargo.command.env("RUSTFLAGS", rustflags); + } + + let rustdocflags = &cargo.rustdocflags.0; + if !rustdocflags.is_empty() { + cargo.command.env("RUSTDOCFLAGS", rustdocflags); + } + + let encoded_hostflags = cargo.hostflags.encode(); + if !encoded_hostflags.is_empty() { + cargo.command.env("RUSTC_HOST_FLAGS", encoded_hostflags); + } + + if !cargo.allow_features.is_empty() { + cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features); + } + cargo.command + } +} + +impl Builder<'_> { + /// Like `cargo`, but only passes flags that are valid for all commands. + pub fn bare_cargo( + &self, + compiler: Compiler, + mode: Mode, + target: TargetSelection, + cmd_kind: Kind, + ) -> BootstrapCommand { + let mut cargo = match cmd_kind { + Kind::Clippy => { + let mut cargo = self.cargo_clippy_cmd(compiler); + cargo.arg(cmd_kind.as_str()); + cargo + } + Kind::MiriSetup => { + let mut cargo = self.cargo_miri_cmd(compiler); + cargo.arg("miri").arg("setup"); + cargo + } + Kind::MiriTest => { + let mut cargo = self.cargo_miri_cmd(compiler); + cargo.arg("miri").arg("test"); + cargo + } + _ => { + let mut cargo = command(&self.initial_cargo); + cargo.arg(cmd_kind.as_str()); + cargo + } + }; + + // Run cargo from the source root so it can find .cargo/config. + // This matters when using vendoring and the working directory is outside the repository. + cargo.current_dir(&self.src); + + let out_dir = self.stage_out(compiler, mode); + cargo.env("CARGO_TARGET_DIR", &out_dir); + + // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger` + // from out of tree it shouldn't matter, since x.py is only used for + // building in-tree. + let color_logs = ["RUSTDOC_LOG_COLOR", "RUSTC_LOG_COLOR", "RUST_LOG_COLOR"]; + match self.build.config.color { + Color::Always => { + cargo.arg("--color=always"); + for log in &color_logs { + cargo.env(log, "always"); + } + } + Color::Never => { + cargo.arg("--color=never"); + for log in &color_logs { + cargo.env(log, "never"); + } + } + Color::Auto => {} // nothing to do + } + + if cmd_kind != Kind::Install { + cargo.arg("--target").arg(target.rustc_target_arg()); + } else { + assert_eq!(target, compiler.host); + } + + if self.config.rust_optimize.is_release() && + // cargo bench/install do not accept `--release` and miri doesn't want it + !matches!(cmd_kind, Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest) + { + cargo.arg("--release"); + } + + // Remove make-related flags to ensure Cargo can correctly set things up + cargo.env_remove("MAKEFLAGS"); + cargo.env_remove("MFLAGS"); + + cargo + } + + /// This will create a `Command` that represents a pending execution of + /// Cargo. This cargo will be configured to use `compiler` as the actual + /// rustc compiler, its output will be scoped by `mode`'s output directory, + /// it will pass the `--target` flag for the specified `target`, and will be + /// executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for commands + /// to be run with Miri. + fn cargo( + &self, + compiler: Compiler, + mode: Mode, + source_type: SourceType, + target: TargetSelection, + cmd_kind: Kind, + ) -> Cargo { + let mut cargo = self.bare_cargo(compiler, mode, target, cmd_kind); + let out_dir = self.stage_out(compiler, mode); + + let mut hostflags = HostFlags::default(); + + // Codegen backends are not yet tracked by -Zbinary-dep-depinfo, + // so we need to explicitly clear out if they've been updated. + for backend in self.codegen_backends(compiler) { + self.clear_if_dirty(&out_dir, &backend); + } + + if cmd_kind == Kind::Doc { + let my_out = match mode { + // This is the intended out directory for compiler documentation. + Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target), + Mode::Std => { + if self.config.cmd.json() { + out_dir.join(target).join("json-doc") + } else { + out_dir.join(target).join("doc") + } + } + _ => panic!("doc mode {mode:?} not expected"), + }; + let rustdoc = self.rustdoc(compiler); + self.clear_if_dirty(&my_out, &rustdoc); + } + + let profile_var = |name: &str| { + let profile = if self.config.rust_optimize.is_release() { "RELEASE" } else { "DEV" }; + format!("CARGO_PROFILE_{}_{}", profile, name) + }; + + // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config + // needs to not accidentally link to libLLVM in stage0/lib. + cargo.env("REAL_LIBRARY_PATH_VAR", helpers::dylib_path_var()); + if let Some(e) = env::var_os(helpers::dylib_path_var()) { + cargo.env("REAL_LIBRARY_PATH", e); + } + + // Set a flag for `check`/`clippy`/`fix`, so that certain build + // scripts can do less work (i.e. not building/requiring LLVM). + if matches!(cmd_kind, Kind::Check | Kind::Clippy | Kind::Fix) { + // If we've not yet built LLVM, or it's stale, then bust + // the rustc_llvm cache. That will always work, even though it + // may mean that on the next non-check build we'll need to rebuild + // rustc_llvm. But if LLVM is stale, that'll be a tiny amount + // of work comparatively, and we'd likely need to rebuild it anyway, + // so that's okay. + if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target, false) + .should_build() + { + cargo.env("RUST_CHECK", "1"); + } + } + + let stage = if compiler.stage == 0 && self.local_rebuild { + // Assume the local-rebuild rustc already has stage1 features. + 1 + } else { + compiler.stage + }; + + // We synthetically interpret a stage0 compiler used to build tools as a + // "raw" compiler in that it's the exact snapshot we download. Normally + // the stage0 build means it uses libraries build by the stage0 + // compiler, but for tools we just use the precompiled libraries that + // we've downloaded + let use_snapshot = mode == Mode::ToolBootstrap; + assert!(!use_snapshot || stage == 0 || self.local_rebuild); + + let maybe_sysroot = self.sysroot(compiler); + let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot }; + let libdir = self.rustc_libdir(compiler); + + let sysroot_str = sysroot.as_os_str().to_str().expect("sysroot should be UTF-8"); + if self.is_verbose() && !matches!(self.config.dry_run, DryRun::SelfCheck) { + println!("using sysroot {sysroot_str}"); + } + + let mut rustflags = Rustflags::new(target); + if stage != 0 { + if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") { + cargo.args(s.split_whitespace()); + } + rustflags.env("RUSTFLAGS_NOT_BOOTSTRAP"); + } else { + if let Ok(s) = env::var("CARGOFLAGS_BOOTSTRAP") { + cargo.args(s.split_whitespace()); + } + rustflags.env("RUSTFLAGS_BOOTSTRAP"); + rustflags.arg("--cfg=bootstrap"); + } + + if cmd_kind == Kind::Clippy { + // clippy overwrites sysroot if we pass it to cargo. + // Pass it directly to clippy instead. + // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`, + // so it has no way of knowing the sysroot. + rustflags.arg("--sysroot"); + rustflags.arg(sysroot_str); + } + + let use_new_symbol_mangling = match self.config.rust_new_symbol_mangling { + Some(setting) => { + // If an explicit setting is given, use that + setting + } + None => { + if mode == Mode::Std { + // The standard library defaults to the legacy scheme + false + } else { + // The compiler and tools default to the new scheme + true + } + } + }; + + // By default, windows-rs depends on a native library that doesn't get copied into the + // sysroot. Passing this cfg enables raw-dylib support instead, which makes the native + // library unnecessary. This can be removed when windows-rs enables raw-dylib + // unconditionally. + if let Mode::Rustc | Mode::ToolRustc = mode { + rustflags.arg("--cfg=windows_raw_dylib"); + } + + if use_new_symbol_mangling { + rustflags.arg("-Csymbol-mangling-version=v0"); + } else { + rustflags.arg("-Csymbol-mangling-version=legacy"); + } + + // FIXME: the following components don't build with `-Zrandomize-layout` yet: + // - wasm-component-ld, due to the `wast`crate + // - rust-analyzer, due to the rowan crate + // so we exclude entire categories of steps here due to lack of fine-grained control over + // rustflags. + if self.config.rust_randomize_layout && mode != Mode::ToolStd && mode != Mode::ToolRustc { + rustflags.arg("-Zrandomize-layout"); + } + + // Enable compile-time checking of `cfg` names, values and Cargo `features`. + // + // Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like + // backtrace, core_simd, std_float, ...), those dependencies have their own + // features but cargo isn't involved in the #[path] process and so cannot pass the + // complete list of features, so for that reason we don't enable checking of + // features for std crates. + if mode == Mode::Std { + rustflags.arg("--check-cfg=cfg(feature,values(any()))"); + } + + // Add extra cfg not defined in/by rustc + // + // Note: Although it would seems that "-Zunstable-options" to `rustflags` is useless as + // cargo would implicitly add it, it was discover that sometimes bootstrap only use + // `rustflags` without `cargo` making it required. + rustflags.arg("-Zunstable-options"); + for (restricted_mode, name, values) in EXTRA_CHECK_CFGS { + if restricted_mode.is_none() || *restricted_mode == Some(mode) { + rustflags.arg(&check_cfg_arg(name, *values)); + } + } + + // FIXME(rust-lang/cargo#5754) we shouldn't be using special command arguments + // to the host invocation here, but rather Cargo should know what flags to pass rustc + // itself. + if stage == 0 { + hostflags.arg("--cfg=bootstrap"); + } + // Cargo doesn't pass RUSTFLAGS to proc_macros: + // https://github.com/rust-lang/cargo/issues/4423 + // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`. + // We also declare that the flag is expected, which we need to do to not + // get warnings about it being unexpected. + hostflags.arg("-Zunstable-options"); + hostflags.arg("--check-cfg=cfg(bootstrap)"); + + // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`, + // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See + // #71458. + let mut rustdocflags = rustflags.clone(); + rustdocflags.propagate_cargo_env("RUSTDOCFLAGS"); + if stage == 0 { + rustdocflags.env("RUSTDOCFLAGS_BOOTSTRAP"); + } else { + rustdocflags.env("RUSTDOCFLAGS_NOT_BOOTSTRAP"); + } + + if let Ok(s) = env::var("CARGOFLAGS") { + cargo.args(s.split_whitespace()); + } + + match mode { + Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {} + Mode::Rustc | Mode::Codegen | Mode::ToolRustc => { + // Build proc macros both for the host and the target unless proc-macros are not + // supported by the target. + if target != compiler.host && cmd_kind != Kind::Check { + let error = command(self.rustc(compiler)) + .arg("--target") + .arg(target.rustc_target_arg()) + .arg("--print=file-names") + .arg("--crate-type=proc-macro") + .arg("-") + .run_capture(self) + .stderr(); + let not_supported = error + .lines() + .any(|line| line.contains("unsupported crate type `proc-macro`")); + if !not_supported { + cargo.arg("-Zdual-proc-macros"); + rustflags.arg("-Zdual-proc-macros"); + } + } + } + } + + // This tells Cargo (and in turn, rustc) to output more complete + // dependency information. Most importantly for bootstrap, this + // includes sysroot artifacts, like libstd, which means that we don't + // need to track those in bootstrap (an error prone process!). This + // feature is currently unstable as there may be some bugs and such, but + // it represents a big improvement in bootstrap's reliability on + // rebuilds, so we're using it here. + // + // For some additional context, see #63470 (the PR originally adding + // this), as well as #63012 which is the tracking issue for this + // feature on the rustc side. + cargo.arg("-Zbinary-dep-depinfo"); + let allow_features = match mode { + Mode::ToolBootstrap | Mode::ToolStd => { + // Restrict the allowed features so we don't depend on nightly + // accidentally. + // + // binary-dep-depinfo is used by bootstrap itself for all + // compilations. + // + // Lots of tools depend on proc_macro2 and proc-macro-error. + // Those have build scripts which assume nightly features are + // available if the `rustc` version is "nighty" or "dev". See + // bin/rustc.rs for why that is a problem. Instead of labeling + // those features for each individual tool that needs them, + // just blanket allow them here. + // + // If this is ever removed, be sure to add something else in + // its place to keep the restrictions in place (or make a way + // to unset RUSTC_BOOTSTRAP). + "binary-dep-depinfo,proc_macro_span,proc_macro_span_shrink,proc_macro_diagnostic" + .to_string() + } + Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => String::new(), + }; + + cargo.arg("-j").arg(self.jobs().to_string()); + + // FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005 + // Force cargo to output binaries with disambiguating hashes in the name + let mut metadata = if compiler.stage == 0 { + // Treat stage0 like a special channel, whether it's a normal prior- + // release rustc or a local rebuild with the same version, so we + // never mix these libraries by accident. + "bootstrap".to_string() + } else { + self.config.channel.to_string() + }; + // We want to make sure that none of the dependencies between + // std/test/rustc unify with one another. This is done for weird linkage + // reasons but the gist of the problem is that if librustc, libtest, and + // libstd all depend on libc from crates.io (which they actually do) we + // want to make sure they all get distinct versions. Things get really + // weird if we try to unify all these dependencies right now, namely + // around how many times the library is linked in dynamic libraries and + // such. If rustc were a static executable or if we didn't ship dylibs + // this wouldn't be a problem, but we do, so it is. This is in general + // just here to make sure things build right. If you can remove this and + // things still build right, please do! + match mode { + Mode::Std => metadata.push_str("std"), + // When we're building rustc tools, they're built with a search path + // that contains things built during the rustc build. For example, + // bitflags is built during the rustc build, and is a dependency of + // rustdoc as well. We're building rustdoc in a different target + // directory, though, which means that Cargo will rebuild the + // dependency. When we go on to build rustdoc, we'll look for + // bitflags, and find two different copies: one built during the + // rustc step and one that we just built. This isn't always a + // problem, somehow -- not really clear why -- but we know that this + // fixes things. + Mode::ToolRustc => metadata.push_str("tool-rustc"), + // Same for codegen backends. + Mode::Codegen => metadata.push_str("codegen"), + _ => {} + } + cargo.env("__CARGO_DEFAULT_LIB_METADATA", &metadata); + + if cmd_kind == Kind::Clippy { + rustflags.arg("-Zforce-unstable-if-unmarked"); + } + + rustflags.arg("-Zmacro-backtrace"); + + let want_rustdoc = self.doc_tests != DocTests::No; + + // Clear the output directory if the real rustc we're using has changed; + // Cargo cannot detect this as it thinks rustc is bootstrap/debug/rustc. + // + // Avoid doing this during dry run as that usually means the relevant + // compiler is not yet linked/copied properly. + // + // Only clear out the directory if we're compiling std; otherwise, we + // should let Cargo take care of things for us (via depdep info) + if !self.config.dry_run() && mode == Mode::Std && cmd_kind == Kind::Build { + self.clear_if_dirty(&out_dir, &self.rustc(compiler)); + } + + let rustdoc_path = match cmd_kind { + Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc(compiler), + _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"), + }; + + // Customize the compiler we're running. Specify the compiler to cargo + // as our shim and then pass it some various options used to configure + // how the actual compiler itself is called. + // + // These variables are primarily all read by + // src/bootstrap/bin/{rustc.rs,rustdoc.rs} + cargo + .env("RUSTBUILD_NATIVE_DIR", self.native_dir(target)) + .env("RUSTC_REAL", self.rustc(compiler)) + .env("RUSTC_STAGE", stage.to_string()) + .env("RUSTC_SYSROOT", sysroot) + .env("RUSTC_LIBDIR", libdir) + .env("RUSTDOC", self.bootstrap_out.join("rustdoc")) + .env("RUSTDOC_REAL", rustdoc_path) + .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir()) + .env("RUSTC_BREAK_ON_ICE", "1"); + + // Set RUSTC_WRAPPER to the bootstrap shim, which switches between beta and in-tree + // sysroot depending on whether we're building build scripts. + // NOTE: we intentionally use RUSTC_WRAPPER so that we can support clippy - RUSTC is not + // respected by clippy-driver; RUSTC_WRAPPER happens earlier, before clippy runs. + cargo.env("RUSTC_WRAPPER", self.bootstrap_out.join("rustc")); + // NOTE: we also need to set RUSTC so cargo can run `rustc -vV`; apparently that ignores RUSTC_WRAPPER >:( + cargo.env("RUSTC", self.bootstrap_out.join("rustc")); + + // Someone might have set some previous rustc wrapper (e.g. + // sccache) before bootstrap overrode it. Respect that variable. + if let Some(existing_wrapper) = env::var_os("RUSTC_WRAPPER") { + cargo.env("RUSTC_WRAPPER_REAL", existing_wrapper); + } + + // If this is for `miri-test`, prepare the sysroots. + if cmd_kind == Kind::MiriTest { + self.ensure(compile::Std::new(compiler, compiler.host)); + let host_sysroot = self.sysroot(compiler); + let miri_sysroot = test::Miri::build_miri_sysroot(self, compiler, target); + cargo.env("MIRI_SYSROOT", &miri_sysroot); + cargo.env("MIRI_HOST_SYSROOT", &host_sysroot); + } + + cargo.env(profile_var("STRIP"), self.config.rust_strip.to_string()); + + if let Some(stack_protector) = &self.config.rust_stack_protector { + rustflags.arg(&format!("-Zstack-protector={stack_protector}")); + } + + if !matches!(cmd_kind, Kind::Build | Kind::Check | Kind::Clippy | Kind::Fix) && want_rustdoc + { + cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler)); + } + + let debuginfo_level = match mode { + Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc, + Mode::Std => self.config.rust_debuginfo_level_std, + Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc => { + self.config.rust_debuginfo_level_tools + } + }; + cargo.env(profile_var("DEBUG"), debuginfo_level.to_string()); + if let Some(opt_level) = &self.config.rust_optimize.get_opt_level() { + cargo.env(profile_var("OPT_LEVEL"), opt_level); + } + cargo.env( + profile_var("DEBUG_ASSERTIONS"), + if mode == Mode::Std { + self.config.rust_debug_assertions_std.to_string() + } else { + self.config.rust_debug_assertions.to_string() + }, + ); + cargo.env( + profile_var("OVERFLOW_CHECKS"), + if mode == Mode::Std { + self.config.rust_overflow_checks_std.to_string() + } else { + self.config.rust_overflow_checks.to_string() + }, + ); + + match self.config.split_debuginfo(target) { + SplitDebuginfo::Packed => rustflags.arg("-Csplit-debuginfo=packed"), + SplitDebuginfo::Unpacked => rustflags.arg("-Csplit-debuginfo=unpacked"), + SplitDebuginfo::Off => rustflags.arg("-Csplit-debuginfo=off"), + }; + + if self.config.cmd.bless() { + // Bless `expect!` tests. + cargo.env("UPDATE_EXPECT", "1"); + } + + if !mode.is_tool() { + cargo.env("RUSTC_FORCE_UNSTABLE", "1"); + } + + if let Some(x) = self.crt_static(target) { + if x { + rustflags.arg("-Ctarget-feature=+crt-static"); + } else { + rustflags.arg("-Ctarget-feature=-crt-static"); + } + } + + if let Some(x) = self.crt_static(compiler.host) { + let sign = if x { "+" } else { "-" }; + hostflags.arg(format!("-Ctarget-feature={sign}crt-static")); + } + + if let Some(map_to) = self.build.debuginfo_map_to(GitRepo::Rustc) { + let map = format!("{}={}", self.build.src.display(), map_to); + cargo.env("RUSTC_DEBUGINFO_MAP", map); + + // `rustc` needs to know the virtual `/rustc/$hash` we're mapping to, + // in order to opportunistically reverse it later. + cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to); + } + + if self.config.rust_remap_debuginfo { + let mut env_var = OsString::new(); + if self.config.vendor { + let vendor = self.build.src.join("vendor"); + env_var.push(vendor); + env_var.push("=/rust/deps"); + } else { + let registry_src = t!(home::cargo_home()).join("registry").join("src"); + for entry in t!(std::fs::read_dir(registry_src)) { + if !env_var.is_empty() { + env_var.push("\t"); + } + env_var.push(t!(entry).path()); + env_var.push("=/rust/deps"); + } + } + cargo.env("RUSTC_CARGO_REGISTRY_SRC_TO_REMAP", env_var); + } + + // Enable usage of unstable features + cargo.env("RUSTC_BOOTSTRAP", "1"); + + if self.config.dump_bootstrap_shims { + prepare_behaviour_dump_dir(self.build); + + cargo + .env("DUMP_BOOTSTRAP_SHIMS", self.build.out.join("bootstrap-shims-dump")) + .env("BUILD_OUT", &self.build.out) + .env("CARGO_HOME", t!(home::cargo_home())); + }; + + self.add_rust_test_threads(&mut cargo); + + // Almost all of the crates that we compile as part of the bootstrap may + // have a build script, including the standard library. To compile a + // build script, however, it itself needs a standard library! This + // introduces a bit of a pickle when we're compiling the standard + // library itself. + // + // To work around this we actually end up using the snapshot compiler + // (stage0) for compiling build scripts of the standard library itself. + // The stage0 compiler is guaranteed to have a libstd available for use. + // + // For other crates, however, we know that we've already got a standard + // library up and running, so we can use the normal compiler to compile + // build scripts in that situation. + if mode == Mode::Std { + cargo + .env("RUSTC_SNAPSHOT", &self.initial_rustc) + .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_snapshot_libdir()); + } else { + cargo + .env("RUSTC_SNAPSHOT", self.rustc(compiler)) + .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_libdir(compiler)); + } + + // Tools that use compiler libraries may inherit the `-lLLVM` link + // requirement, but the `-L` library path is not propagated across + // separate Cargo projects. We can add LLVM's library path to the + // platform-specific environment variable as a workaround. + if mode == Mode::ToolRustc || mode == Mode::Codegen { + if let Some(llvm_config) = self.llvm_config(target) { + let llvm_libdir = + command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout(); + add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo); + } + } + + // Compile everything except libraries and proc macros with the more + // efficient initial-exec TLS model. This doesn't work with `dlopen`, + // so we can't use it by default in general, but we can use it for tools + // and our own internal libraries. + if !mode.must_support_dlopen() && !target.triple.starts_with("powerpc-") { + cargo.env("RUSTC_TLS_MODEL_INITIAL_EXEC", "1"); + } + + // Ignore incremental modes except for stage0, since we're + // not guaranteeing correctness across builds if the compiler + // is changing under your feet. + if self.config.incremental && compiler.stage == 0 { + cargo.env("CARGO_INCREMENTAL", "1"); + } else { + // Don't rely on any default setting for incr. comp. in Cargo + cargo.env("CARGO_INCREMENTAL", "0"); + } + + if let Some(ref on_fail) = self.config.on_fail { + cargo.env("RUSTC_ON_FAIL", on_fail); + } + + if self.config.print_step_timings { + cargo.env("RUSTC_PRINT_STEP_TIMINGS", "1"); + } + + if self.config.print_step_rusage { + cargo.env("RUSTC_PRINT_STEP_RUSAGE", "1"); + } + + if self.config.backtrace_on_ice { + cargo.env("RUSTC_BACKTRACE_ON_ICE", "1"); + } + + if self.is_verbose() { + // This provides very useful logs especially when debugging build cache-related stuff. + cargo.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); + } + + cargo.env("RUSTC_VERBOSE", self.verbosity.to_string()); + + // Downstream forks of the Rust compiler might want to use a custom libc to add support for + // targets that are not yet available upstream. Adding a patch to replace libc with a + // custom one would cause compilation errors though, because Cargo would interpret the + // custom libc as part of the workspace, and apply the check-cfg lints on it. + // + // The libc build script emits check-cfg flags only when this environment variable is set, + // so this line allows the use of custom libcs. + cargo.env("LIBC_CHECK_CFG", "1"); + + if source_type == SourceType::InTree { + let mut lint_flags = Vec::new(); + // When extending this list, add the new lints to the RUSTFLAGS of the + // build_bootstrap function of src/bootstrap/bootstrap.py as well as + // some code doesn't go through this `rustc` wrapper. + lint_flags.push("-Wrust_2018_idioms"); + lint_flags.push("-Wunused_lifetimes"); + + if self.config.deny_warnings { + lint_flags.push("-Dwarnings"); + rustdocflags.arg("-Dwarnings"); + } + + // This does not use RUSTFLAGS due to caching issues with Cargo. + // Clippy is treated as an "in tree" tool, but shares the same + // cache as other "submodule" tools. With these options set in + // RUSTFLAGS, that causes *every* shared dependency to be rebuilt. + // By injecting this into the rustc wrapper, this circumvents + // Cargo's fingerprint detection. This is fine because lint flags + // are always ignored in dependencies. Eventually this should be + // fixed via better support from Cargo. + cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" ")); + + rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes"); + } + + if mode == Mode::Rustc { + rustflags.arg("-Wrustc::internal"); + // FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all + // of the individual lints are satisfied. + rustflags.arg("-Wkeyword_idents_2024"); + rustflags.arg("-Wunsafe_op_in_unsafe_fn"); + } + + if self.config.rust_frame_pointers { + rustflags.arg("-Cforce-frame-pointers=true"); + } + + // If Control Flow Guard is enabled, pass the `control-flow-guard` flag to rustc + // when compiling the standard library, since this might be linked into the final outputs + // produced by rustc. Since this mitigation is only available on Windows, only enable it + // for the standard library in case the compiler is run on a non-Windows platform. + // This is not needed for stage 0 artifacts because these will only be used for building + // the stage 1 compiler. + if cfg!(windows) + && mode == Mode::Std + && self.config.control_flow_guard + && compiler.stage >= 1 + { + rustflags.arg("-Ccontrol-flow-guard"); + } + + // If EHCont Guard is enabled, pass the `-Zehcont-guard` flag to rustc when compiling the + // standard library, since this might be linked into the final outputs produced by rustc. + // Since this mitigation is only available on Windows, only enable it for the standard + // library in case the compiler is run on a non-Windows platform. + // This is not needed for stage 0 artifacts because these will only be used for building + // the stage 1 compiler. + if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard && compiler.stage >= 1 { + rustflags.arg("-Zehcont-guard"); + } + + // For `cargo doc` invocations, make rustdoc print the Rust version into the docs + // This replaces spaces with tabs because RUSTDOCFLAGS does not + // support arguments with regular spaces. Hopefully someday Cargo will + // have space support. + let rust_version = self.rust_version().replace(' ', "\t"); + rustdocflags.arg("--crate-version").arg(&rust_version); + + // Environment variables *required* throughout the build + // + // FIXME: should update code to not require this env var + + // The host this new compiler will *run* on. + cargo.env("CFG_COMPILER_HOST_TRIPLE", target.triple); + // The host this new compiler is being *built* on. + cargo.env("CFG_COMPILER_BUILD_TRIPLE", compiler.host.triple); + + // Set this for all builds to make sure doc builds also get it. + cargo.env("CFG_RELEASE_CHANNEL", &self.config.channel); + + // This one's a bit tricky. As of the time of this writing the compiler + // links to the `winapi` crate on crates.io. This crate provides raw + // bindings to Windows system functions, sort of like libc does for + // Unix. This crate also, however, provides "import libraries" for the + // MinGW targets. There's an import library per dll in the windows + // distribution which is what's linked to. These custom import libraries + // are used because the winapi crate can reference Windows functions not + // present in the MinGW import libraries. + // + // For example MinGW may ship libdbghelp.a, but it may not have + // references to all the functions in the dbghelp dll. Instead the + // custom import library for dbghelp in the winapi crates has all this + // information. + // + // Unfortunately for us though the import libraries are linked by + // default via `-ldylib=winapi_foo`. That is, they're linked with the + // `dylib` type with a `winapi_` prefix (so the winapi ones don't + // conflict with the system MinGW ones). This consequently means that + // the binaries we ship of things like rustc_codegen_llvm (aka the rustc_codegen_llvm + // DLL) when linked against *again*, for example with procedural macros + // or plugins, will trigger the propagation logic of `-ldylib`, passing + // `-lwinapi_foo` to the linker again. This isn't actually available in + // our distribution, however, so the link fails. + // + // To solve this problem we tell winapi to not use its bundled import + // libraries. This means that it will link to the system MinGW import + // libraries by default, and the `-ldylib=foo` directives will still get + // passed to the final linker, but they'll look like `-lfoo` which can + // be resolved because MinGW has the import library. The downside is we + // don't get newer functions from Windows, but we don't use any of them + // anyway. + if !mode.is_tool() { + cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1"); + } + + for _ in 0..self.verbosity { + cargo.arg("-v"); + } + + match (mode, self.config.rust_codegen_units_std, self.config.rust_codegen_units) { + (Mode::Std, Some(n), _) | (_, _, Some(n)) => { + cargo.env(profile_var("CODEGEN_UNITS"), n.to_string()); + } + _ => { + // Don't set anything + } + } + + if self.config.locked_deps { + cargo.arg("--locked"); + } + if self.config.vendor || self.is_sudo { + cargo.arg("--frozen"); + } + + // Try to use a sysroot-relative bindir, in case it was configured absolutely. + cargo.env("RUSTC_INSTALL_BINDIR", self.config.bindir_relative()); + + cargo.force_coloring_in_ci(); + + // When we build Rust dylibs they're all intended for intermediate + // usage, so make sure we pass the -Cprefer-dynamic flag instead of + // linking all deps statically into the dylib. + if matches!(mode, Mode::Std) { + rustflags.arg("-Cprefer-dynamic"); + } + if matches!(mode, Mode::Rustc) && !self.link_std_into_rustc_driver(target) { + rustflags.arg("-Cprefer-dynamic"); + } + + cargo.env( + "RUSTC_LINK_STD_INTO_RUSTC_DRIVER", + if self.link_std_into_rustc_driver(target) { "1" } else { "0" }, + ); + + // When building incrementally we default to a lower ThinLTO import limit + // (unless explicitly specified otherwise). This will produce a somewhat + // slower code but give way better compile times. + { + let limit = match self.config.rust_thin_lto_import_instr_limit { + Some(limit) => Some(limit), + None if self.config.incremental => Some(10), + _ => None, + }; + + if let Some(limit) = limit { + if stage == 0 + || self.config.default_codegen_backend(target).unwrap_or_default() == "llvm" + { + rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}")); + } + } + } + + if matches!(mode, Mode::Std) { + if let Some(mir_opt_level) = self.config.rust_validate_mir_opts { + rustflags.arg("-Zvalidate-mir"); + rustflags.arg(&format!("-Zmir-opt-level={mir_opt_level}")); + } + if self.config.rust_randomize_layout { + rustflags.arg("--cfg=randomized_layouts"); + } + // Always enable inlining MIR when building the standard library. + // Without this flag, MIR inlining is disabled when incremental compilation is enabled. + // That causes some mir-opt tests which inline functions from the standard library to + // break when incremental compilation is enabled. So this overrides the "no inlining + // during incremental builds" heuristic for the standard library. + rustflags.arg("-Zinline-mir"); + + // Similarly, we need to keep debug info for functions inlined into other std functions, + // even if we're not going to output debuginfo for the crate we're currently building, + // so that it'll be available when downstream consumers of std try to use it. + rustflags.arg("-Zinline-mir-preserve-debug"); + } + + if self.config.rustc_parallel + && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen) + { + // keep in sync with `bootstrap/lib.rs:Build::rustc_features` + // `cfg` option for rustc, `features` option for cargo, for conditional compilation + rustflags.arg("--cfg=parallel_compiler"); + rustdocflags.arg("--cfg=parallel_compiler"); + } + + Cargo { + command: cargo, + compiler, + target, + rustflags, + rustdocflags, + hostflags, + allow_features, + } + } +} diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 695a66834d4..21c5f7232a1 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -637,7 +637,7 @@ mod dist { assert_eq!(first(builder.cache.all::()), &[test::Crate { compiler: Compiler { host, stage: 0 }, target: host, - mode: Mode::Std, + mode: crate::Mode::Std, crates: vec!["std".to_owned()], },]); } From 1d77715f83cd495d132f95e9f6c8babb96b44e9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:03:22 +0800 Subject: [PATCH 5/6] bootstrap: minor docs cleanup --- src/bootstrap/src/core/builder/cargo.rs | 27 ++++++++++++++----------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index b33545341cc..c3d0994f164 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -98,7 +98,8 @@ pub struct Cargo { } impl Cargo { - /// Calls `Builder::cargo` and `Cargo::configure_linker` to prepare an invocation of `cargo` to be run. + /// Calls [`Builder::cargo`] and [`Cargo::configure_linker`] to prepare an invocation of `cargo` + /// to be run. pub fn new( builder: &Builder<'_>, compiler: Compiler, @@ -125,7 +126,8 @@ impl Cargo { self.into() } - /// Same as `Cargo::new` except this one doesn't configure the linker with `Cargo::configure_linker` + /// Same as [`Cargo::new`] except this one doesn't configure the linker with + /// [`Cargo::configure_linker`]. pub fn new_for_mir_opt_tests( builder: &Builder<'_>, compiler: Compiler, @@ -163,8 +165,10 @@ impl Cargo { self } + /// Add an env var to the cargo command instance. Note that `RUSTFLAGS`/`RUSTDOCFLAGS` must go + /// through [`Cargo::rustdocflags`] and [`Cargo::rustflags`] because inconsistent `RUSTFLAGS` + /// and `RUSTDOCFLAGS` usages will trigger spurious rebuilds. pub fn env(&mut self, key: impl AsRef, value: impl AsRef) -> &mut Cargo { - // These are managed through rustflag/rustdocflag interfaces. assert_ne!(key.as_ref(), "RUSTFLAGS"); assert_ne!(key.as_ref(), "RUSTDOCFLAGS"); self.command.env(key.as_ref(), value.as_ref()); @@ -182,8 +186,8 @@ impl Cargo { /// Adds nightly-only features that this invocation is allowed to use. /// - /// By default, all nightly features are allowed. Once this is called, it - /// will be restricted to the given set. + /// By default, all nightly features are allowed. Once this is called, it will be restricted to + /// the given set. pub fn allow_features(&mut self, features: &str) -> &mut Cargo { if !self.allow_features.is_empty() { self.allow_features.push(','); @@ -351,7 +355,7 @@ impl From for BootstrapCommand { } impl Builder<'_> { - /// Like `cargo`, but only passes flags that are valid for all commands. + /// Like [`Builder::cargo`], but only passes flags that are valid for all commands. pub fn bare_cargo( &self, compiler: Compiler, @@ -429,12 +433,11 @@ impl Builder<'_> { cargo } - /// This will create a `Command` that represents a pending execution of - /// Cargo. This cargo will be configured to use `compiler` as the actual - /// rustc compiler, its output will be scoped by `mode`'s output directory, - /// it will pass the `--target` flag for the specified `target`, and will be - /// executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for commands - /// to be run with Miri. + /// This will create a [`BootstrapCommand`] that represents a pending execution of cargo. This + /// cargo will be configured to use `compiler` as the actual rustc compiler, its output will be + /// scoped by `mode`'s output directory, it will pass the `--target` flag for the specified + /// `target`, and will be executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for + /// commands to be run with Miri. fn cargo( &self, compiler: Compiler, From 2b4bf2dbb06a47a01ccea4f55bdb9990a911c0fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:54:28 +0800 Subject: [PATCH 6/6] bootstrap: move `builder.rs` under `builder/` directory --- src/bootstrap/src/core/{builder.rs => builder/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/bootstrap/src/core/{builder.rs => builder/mod.rs} (100%) diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder/mod.rs similarity index 100% rename from src/bootstrap/src/core/builder.rs rename to src/bootstrap/src/core/builder/mod.rs