Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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)

AltStyle によって変換されたページ (->オリジナル) /