Skip to content

Commit 34dd67f

Browse files
Auto merge of #145477 - cjgillot:codegen-mode, r=<try>
[EXPERIMENT] Introduce `TypingMode::Codegen` to avoid layout cycles
2 parents cd7cbe8 + a35a84a commit 34dd67f

File tree

67 files changed

+527
-419
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+527
-419
lines changed

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::assert_matches::debug_assert_matches;
12
use std::sync::atomic::Ordering::Relaxed;
23

34
use either::{Left, Right};
@@ -324,23 +325,44 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
324325
tcx: TyCtxt<'tcx>,
325326
key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>,
326327
) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
328+
let ty::PseudoCanonicalInput { typing_env, value } = key;
329+
327330
// This shouldn't be used for statics, since statics are conceptually places,
328331
// not values -- so what we do here could break pointer identity.
329-
assert!(key.value.promoted.is_some() || !tcx.is_static(key.value.instance.def_id()));
330-
// Const eval always happens in PostAnalysis mode . See the comment in
332+
assert!(value.promoted.is_some() || !tcx.is_static(value.instance.def_id()));
333+
334+
// Const eval always happens in PostAnalysis or Codegen mode. See the comment in
331335
// `InterpCx::new` for more details.
332-
debug_assert_eq!(key.typing_env.typing_mode, ty::TypingMode::PostAnalysis);
336+
debug_assert_matches!(
337+
typing_env.typing_mode,
338+
ty::TypingMode::PostAnalysis | ty::TypingMode::Codegen
339+
);
340+
333341
if cfg!(debug_assertions) {
334342
// Make sure we format the instance even if we do not print it.
335343
// This serves as a regression test against an ICE on printing.
336344
// The next two lines concatenated contain some discussion:
337345
// https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/
338346
// subject/anon_const_instance_printing/near/135980032
339-
let instance = with_no_trimmed_paths!(key.value.instance.to_string());
340-
trace!("const eval: {:?} ({})", key, instance);
347+
let instance = with_no_trimmed_paths!(value.instance.to_string());
348+
trace!("const eval: {:?} ({}) inside {:?}", value, instance, typing_env);
349+
}
350+
351+
// We are in codegen. It's very likely this constant has been evaluated in PostAnalysis before.
352+
// Try to reuse this evaluation, and only re-run if we hit a `TooGeneric` error.
353+
if let ty::TypingMode::Codegen = typing_env.typing_mode {
354+
let with_postanalysis = ty::TypingEnv {
355+
typing_mode: ty::TypingMode::PostAnalysis,
356+
param_env: typing_env.param_env,
357+
};
358+
let with_postanalysis = tcx.eval_to_allocation_raw(with_postanalysis.as_query_input(value));
359+
match with_postanalysis {
360+
Ok(_) | Err(ErrorHandled::Reported(..)) => return with_postanalysis,
361+
Err(ErrorHandled::TooGeneric(_)) => {}
362+
}
341363
}
342364

343-
eval_in_interpreter(tcx, key.value, key.typing_env)
365+
eval_in_interpreter(tcx, value, typing_env)
344366
}
345367

346368
fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(

compiler/rustc_const_eval/src/const_eval/valtrees.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::assert_matches::debug_assert_matches;
2+
13
use rustc_abi::{BackendRepr, FieldIdx, VariantIdx};
24
use rustc_data_structures::stack::ensure_sufficient_stack;
35
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ValTreeCreationError};
@@ -230,9 +232,12 @@ pub(crate) fn eval_to_valtree<'tcx>(
230232
typing_env: ty::TypingEnv<'tcx>,
231233
cid: GlobalId<'tcx>,
232234
) -> EvalToValTreeResult<'tcx> {
233-
// Const eval always happens in PostAnalysis mode . See the comment in
235+
// Const eval always happens in PostAnalysis or Codegen mode . See the comment in
234236
// `InterpCx::new` for more details.
235-
debug_assert_eq!(typing_env.typing_mode, ty::TypingMode::PostAnalysis);
237+
debug_assert_matches!(
238+
typing_env.typing_mode,
239+
ty::TypingMode::PostAnalysis | ty::TypingMode::Codegen
240+
);
236241
let const_alloc = tcx.eval_to_allocation_raw(typing_env.as_query_input(cid))?;
237242

238243
// FIXME Need to provide a span to `eval_to_valtree`

compiler/rustc_const_eval/src/interpret/eval_context.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,11 +226,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
226226
typing_env: ty::TypingEnv<'tcx>,
227227
machine: M,
228228
) -> Self {
229-
// Const eval always happens in post analysis mode in order to be able to use the hidden types of
230-
// opaque types. This is needed for trivial things like `size_of`, but also for using associated
231-
// types that are not specified in the opaque type. We also use MIR bodies whose opaque types have
232-
// already been revealed, so we'd be able to at least partially observe the hidden types anyways.
233-
debug_assert_matches!(typing_env.typing_mode, ty::TypingMode::PostAnalysis);
229+
// Const eval always happens in post analysis or codegen mode in order to be able to use
230+
// the hidden types of opaque types. This is needed for trivial things like `size_of`, but
231+
// also for using associated types that are not specified in the opaque type. We also use
232+
// MIR bodies whose opaque types have already been revealed, so we'd be able to at least
233+
// partially observe the hidden types anyways.
234+
debug_assert_matches!(
235+
typing_env.typing_mode,
236+
ty::TypingMode::PostAnalysis | ty::TypingMode::Codegen
237+
);
234238
InterpCx {
235239
machine,
236240
tcx: tcx.at(root_span),

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8383
*self.deferred_cast_checks.borrow_mut() = deferred_cast_checks;
8484
}
8585

86-
pub(in super::super) fn check_transmutes(&self) {
87-
let mut deferred_transmute_checks = self.deferred_transmute_checks.borrow_mut();
88-
debug!("FnCtxt::check_transmutes: {} deferred checks", deferred_transmute_checks.len());
89-
for (from, to, hir_id) in deferred_transmute_checks.drain(..) {
90-
self.check_transmute(from, to, hir_id);
91-
}
92-
}
93-
9486
pub(in super::super) fn check_asms(&self) {
9587
let mut deferred_asm_checks = self.deferred_asm_checks.borrow_mut();
9688
debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len());

compiler/rustc_hir_typeck/src/intrinsicck.rs

Lines changed: 104 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ use rustc_hir as hir;
77
use rustc_index::Idx;
88
use rustc_middle::bug;
99
use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
10-
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
10+
use rustc_middle::ty::{self, Ty, TyCtxt};
11+
use rustc_span::def_id::LocalDefId;
1112
use tracing::trace;
1213

13-
use super::FnCtxt;
14-
1514
/// If the type is `Option<T>`, it will return `T`, otherwise
1615
/// the type itself. Works on most `Option`-like types.
1716
fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
@@ -39,119 +38,117 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
3938
ty
4039
}
4140

42-
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
43-
/// FIXME: Move this check out of typeck, since it'll easily cycle when revealing opaques,
44-
/// and we shouldn't need to check anything here if the typeck results are tainted.
45-
pub(crate) fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) {
46-
let tcx = self.tcx;
47-
let dl = &tcx.data_layout;
48-
let span = tcx.hir_span(hir_id);
49-
let normalize = |ty| {
50-
let ty = self.resolve_vars_if_possible(ty);
51-
if let Ok(ty) =
52-
self.tcx.try_normalize_erasing_regions(self.typing_env(self.param_env), ty)
53-
{
54-
ty
41+
/// Try to display a sensible error with as much information as possible.
42+
fn skeleton_string<'tcx>(
43+
ty: Ty<'tcx>,
44+
sk: Result<SizeSkeleton<'tcx>, &'tcx LayoutError<'tcx>>,
45+
) -> String {
46+
match sk {
47+
Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
48+
Ok(SizeSkeleton::Known(size, _)) => {
49+
if let Some(v) = u128::from(size.bytes()).checked_mul(8) {
50+
format!("{v} bits")
5551
} else {
56-
Ty::new_error_with_message(
57-
tcx,
58-
span,
59-
"tried to normalize non-wf type in check_transmute",
60-
)
52+
// `u128` should definitely be able to hold the size of different architectures
53+
// larger sizes should be reported as error `are too big for the target architecture`
54+
// otherwise we have a bug somewhere
55+
bug!("{:?} overflow for u128", size)
6156
}
62-
};
63-
let from = normalize(from);
64-
let to = normalize(to);
65-
trace!(?from, ?to);
66-
if from.has_non_region_infer() || to.has_non_region_infer() {
67-
// Note: this path is currently not reached in any test, so any
68-
// example that triggers this would be worth minimizing and
69-
// converting into a test.
70-
self.dcx().span_bug(span, "argument to transmute has inference variables");
7157
}
72-
// Transmutes that are only changing lifetimes are always ok.
73-
if from == to {
74-
return;
58+
Ok(SizeSkeleton::Generic(size)) => {
59+
format!("generic size {size}")
60+
}
61+
Err(LayoutError::TooGeneric(bad)) => {
62+
if *bad == ty {
63+
"this type does not have a fixed size".to_owned()
64+
} else {
65+
format!("size can vary because of {bad}")
66+
}
67+
}
68+
Err(err) => err.to_string(),
69+
}
70+
}
71+
72+
fn check_transmute<'tcx>(
73+
tcx: TyCtxt<'tcx>,
74+
typing_env: ty::TypingEnv<'tcx>,
75+
from: Ty<'tcx>,
76+
to: Ty<'tcx>,
77+
hir_id: HirId,
78+
) {
79+
let span = || tcx.hir_span(hir_id);
80+
let normalize = |ty| {
81+
if let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, ty) {
82+
ty
83+
} else {
84+
Ty::new_error_with_message(
85+
tcx,
86+
span(),
87+
format!("tried to normalize non-wf type {ty:#?} in check_transmute"),
88+
)
7589
}
90+
};
7691

77-
let skel = |ty| SizeSkeleton::compute(ty, tcx, self.typing_env(self.param_env));
78-
let sk_from = skel(from);
79-
let sk_to = skel(to);
80-
trace!(?sk_from, ?sk_to);
92+
let from = normalize(from);
93+
let to = normalize(to);
94+
trace!(?from, ?to);
8195

82-
// Check for same size using the skeletons.
83-
if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
84-
if sk_from.same_size(sk_to) {
85-
return;
86-
}
96+
// Transmutes that are only changing lifetimes are always ok.
97+
if from == to {
98+
return;
99+
}
87100

88-
// Special-case transmuting from `typeof(function)` and
89-
// `Option<typeof(function)>` to present a clearer error.
90-
let from = unpack_option_like(tcx, from);
91-
if let (&ty::FnDef(..), SizeSkeleton::Known(size_to, _)) = (from.kind(), sk_to)
92-
&& size_to == Pointer(dl.instruction_address_space).size(&tcx)
93-
{
94-
struct_span_code_err!(self.dcx(), span, E0591, "can't transmute zero-sized type")
95-
.with_note(format!("source type: {from}"))
96-
.with_note(format!("target type: {to}"))
97-
.with_help("cast with `as` to a pointer instead")
98-
.emit();
99-
return;
100-
}
101+
let sk_from = SizeSkeleton::compute(from, tcx, typing_env);
102+
let sk_to = SizeSkeleton::compute(to, tcx, typing_env);
103+
trace!(?sk_from, ?sk_to);
104+
105+
// Check for same size using the skeletons.
106+
if let Ok(sk_from) = sk_from
107+
&& let Ok(sk_to) = sk_to
108+
{
109+
if sk_from.same_size(sk_to) {
110+
return;
101111
}
102112

103-
// Try to display a sensible error with as much information as possible.
104-
let skeleton_string = |ty: Ty<'tcx>, sk: Result<_, &_>| match sk {
105-
Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
106-
Ok(SizeSkeleton::Known(size, _)) => {
107-
if let Some(v) = u128::from(size.bytes()).checked_mul(8) {
108-
format!("{v} bits")
109-
} else {
110-
// `u128` should definitely be able to hold the size of different architectures
111-
// larger sizes should be reported as error `are too big for the target architecture`
112-
// otherwise we have a bug somewhere
113-
bug!("{:?} overflow for u128", size)
114-
}
115-
}
116-
Ok(SizeSkeleton::Generic(size)) => {
117-
if let Some(size) =
118-
self.try_structurally_resolve_const(span, size).try_to_target_usize(tcx)
119-
{
120-
format!("{size} bytes")
121-
} else {
122-
format!("generic size {size}")
123-
}
124-
}
125-
Err(LayoutError::TooGeneric(bad)) => {
126-
if *bad == ty {
127-
"this type does not have a fixed size".to_owned()
128-
} else {
129-
format!("size can vary because of {bad}")
130-
}
131-
}
132-
Err(err) => err.to_string(),
133-
};
134-
135-
let mut err = struct_span_code_err!(
136-
self.dcx(),
137-
span,
138-
E0512,
139-
"cannot transmute between types of different sizes, \
140-
or dependently-sized types"
141-
);
142-
if from == to {
143-
err.note(format!("`{from}` does not have a fixed size"));
144-
err.emit();
145-
} else {
146-
err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)))
147-
.note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
148-
if let Err(LayoutError::ReferencesError(_)) = sk_from {
149-
err.delay_as_bug();
150-
} else if let Err(LayoutError::ReferencesError(_)) = sk_to {
151-
err.delay_as_bug();
152-
} else {
153-
err.emit();
154-
}
113+
// Special-case transmuting from `typeof(function)` and
114+
// `Option<typeof(function)>` to present a clearer error.
115+
let from = unpack_option_like(tcx, from);
116+
if let ty::FnDef(..) = from.kind()
117+
&& let SizeSkeleton::Known(size_to, _) = sk_to
118+
&& size_to == Pointer(tcx.data_layout.instruction_address_space).size(&tcx)
119+
{
120+
struct_span_code_err!(tcx.sess.dcx(), span(), E0591, "can't transmute zero-sized type")
121+
.with_note(format!("source type: {from}"))
122+
.with_note(format!("target type: {to}"))
123+
.with_help("cast with `as` to a pointer instead")
124+
.emit();
125+
return;
155126
}
156127
}
128+
129+
let mut err = struct_span_code_err!(
130+
tcx.sess.dcx(),
131+
span(),
132+
E0512,
133+
"cannot transmute between types of different sizes, or dependently-sized types"
134+
);
135+
if from == to {
136+
err.note(format!("`{from}` does not have a fixed size"));
137+
err.emit();
138+
} else {
139+
err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)));
140+
err.note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
141+
err.emit();
142+
}
143+
}
144+
145+
pub(crate) fn check_transmutes(tcx: TyCtxt<'_>, owner: LocalDefId) {
146+
assert!(!tcx.is_typeck_child(owner.to_def_id()));
147+
let typeck_results = tcx.typeck(owner);
148+
let None = typeck_results.tainted_by_errors else { return };
149+
150+
let typing_env = ty::TypingEnv::codegen(tcx, owner);
151+
for &(from, to, hir_id) in &typeck_results.transmutes_to_check {
152+
check_transmute(tcx, typing_env, from, to, hir_id);
153+
}
157154
}

compiler/rustc_hir_typeck/src/lib.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,10 +251,6 @@ fn typeck_with_inspect<'tcx>(
251251
fcx.report_ambiguity_errors();
252252
}
253253

254-
if let None = fcx.infcx.tainted_by_errors() {
255-
fcx.check_transmutes();
256-
}
257-
258254
fcx.check_asms();
259255

260256
let typeck_results = fcx.resolve_type_vars_in_body(body);
@@ -555,6 +551,7 @@ pub fn provide(providers: &mut Providers) {
555551
method_autoderef_steps: method::probe::method_autoderef_steps,
556552
typeck,
557553
used_trait_imports,
554+
check_transmutes: intrinsicck::check_transmutes,
558555
..*providers
559556
};
560557
}

compiler/rustc_hir_typeck/src/writeback.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7474
wbcx.visit_user_provided_tys();
7575
wbcx.visit_user_provided_sigs();
7676
wbcx.visit_coroutine_interior();
77+
wbcx.visit_transmutes();
7778
wbcx.visit_offset_of_container_types();
7879

7980
wbcx.typeck_results.rvalue_scopes =
@@ -532,6 +533,18 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
532533
}
533534
}
534535

536+
fn visit_transmutes(&mut self) {
537+
let tcx = self.tcx();
538+
let fcx_typeck_results = self.fcx.typeck_results.borrow();
539+
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
540+
for &(from, to, hir_id) in self.fcx.deferred_transmute_checks.borrow().iter() {
541+
let span = tcx.hir_span(hir_id);
542+
let from = self.resolve(from, &span);
543+
let to = self.resolve(to, &span);
544+
self.typeck_results.transmutes_to_check.push((from, to, hir_id));
545+
}
546+
}
547+
535548
#[instrument(skip(self), level = "debug")]
536549
fn visit_opaque_types(&mut self) {
537550
let tcx = self.tcx();

0 commit comments

Comments
 (0)