Skip to content

Commit 6e5c4b6

Browse files
committed
Auto merge of #116115 - RalfJung:dyn-unsized-args, r=<try>
detect dyn-unsized arguments before they cause ICEs "Fixes" #115709 in a crude way, with a post-mono check. This is entirely caused by us not having a trait to express "type can have its size determined at runtime".
2 parents 8a6bae2 + 0389d09 commit 6e5c4b6

File tree

12 files changed

+192
-15
lines changed

12 files changed

+192
-15
lines changed

compiler/rustc_codegen_cranelift/src/base.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,12 @@ pub(crate) fn verify_func(
250250
}
251251

252252
fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
253-
if let Err(err) =
254-
fx.mir.post_mono_checks(fx.tcx, ty::ParamEnv::reveal_all(), |c| Ok(fx.monomorphize(c)))
255-
{
253+
if let Err(err) = fx.mir.post_mono_checks(
254+
fx.tcx,
255+
ty::ParamEnv::reveal_all(),
256+
|c| Ok(fx.monomorphize(c)),
257+
|ty| Ok(fx.monomorphize(ty)),
258+
) {
256259
err.emit_err(fx.tcx);
257260
fx.bcx.append_block_params_for_function_params(fx.block_map[START_BLOCK]);
258261
fx.bcx.switch_to_block(fx.block_map[START_BLOCK]);

compiler/rustc_codegen_llvm/src/abi.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -393,8 +393,27 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
393393
// alignment, so this respects ABI compatibility.
394394
let ptr_ty = Ty::new_mut_ptr(cx.tcx, arg.layout.ty);
395395
let ptr_layout = cx.layout_of(ptr_ty);
396-
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 0, true));
397-
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true));
396+
if matches!(ptr_layout.abi, abi::Abi::ScalarPair(..)) {
397+
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 0, true));
398+
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true));
399+
} else {
400+
// If this is not a ScalarPair, something went wrong. But this is possible
401+
// e.g. with unsized locals of `extern` type, which can only be detected
402+
// post-monomorphization. So we fill in some fake data (crucially, we add
403+
// the right number of argument types). and rely on someone else showing a
404+
// nice error.
405+
assert!(ptr_layout.abi.is_scalar());
406+
let llvm_ty = ptr_layout.immediate_llvm_type(cx);
407+
llargument_tys.push(llvm_ty);
408+
llargument_tys.push(llvm_ty);
409+
cx.tcx.sess.delay_span_bug(
410+
rustc_span::DUMMY_SP,
411+
format!(
412+
"function argument with invalid unsized type {}",
413+
arg.layout.ty
414+
),
415+
);
416+
}
398417
continue;
399418
}
400419
PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => {

compiler/rustc_codegen_ssa/src/mir/mod.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,12 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
212212
fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut start_bx);
213213

214214
// Rust post-monomorphization checks; we later rely on them.
215-
if let Err(err) =
216-
mir.post_mono_checks(cx.tcx(), ty::ParamEnv::reveal_all(), |c| Ok(fx.monomorphize(c)))
217-
{
215+
if let Err(err) = mir.post_mono_checks(
216+
cx.tcx(),
217+
ty::ParamEnv::reveal_all(),
218+
|c| Ok(fx.monomorphize(c)),
219+
|ty| Ok(fx.monomorphize(ty)),
220+
) {
218221
err.emit_err(cx.tcx());
219222
// This IR shouldn't ever be emitted, but let's try to guard against any of this code
220223
// ever running.

compiler/rustc_const_eval/src/interpret/eval_context.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -752,9 +752,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
752752
if M::POST_MONO_CHECKS {
753753
// `ctfe_query` does some error message decoration that we want to be in effect here.
754754
self.ctfe_query(None, |tcx| {
755-
body.post_mono_checks(*tcx, self.param_env, |c| {
756-
self.subst_from_current_frame_and_normalize_erasing_regions(c)
757-
})
755+
body.post_mono_checks(
756+
*tcx,
757+
self.param_env,
758+
|c| self.subst_from_current_frame_and_normalize_erasing_regions(c),
759+
|ty| self.subst_from_current_frame_and_normalize_erasing_regions(ty),
760+
)
758761
})?;
759762
}
760763

compiler/rustc_middle/messages.ftl

+14
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,20 @@ middle_drop_check_overflow =
5252
overflow while adding drop-check rules for {$ty}
5353
.note = overflowed on {$overflow_ty}
5454
55+
middle_dyn_unsized_local =
56+
{$is_arg ->
57+
[true] function argument
58+
*[other] local variable
59+
} does not have a dynamically computable size
60+
.note = `{$ty}` contains an `extern type`, which is not allowed in {$is_arg ->
61+
[true] function arguments
62+
*[other] local variables
63+
}
64+
65+
middle_dyn_unsized_param =
66+
function parameter does not have a dynamically computable size
67+
.note = `{$ty}` contains an `extern type`, which is not allowed in function parameters
68+
5569
middle_erroneous_constant = erroneous constant encountered
5670
5771
middle_layout_references_error =

compiler/rustc_middle/src/error.rs

+19
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,25 @@ pub struct LimitInvalid<'a> {
5353
pub error_str: &'a str,
5454
}
5555

56+
#[derive(Diagnostic)]
57+
#[diag(middle_dyn_unsized_local)]
58+
#[note]
59+
pub struct DynUnsizedLocal<'tcx> {
60+
#[primary_span]
61+
pub span: Span,
62+
pub ty: Ty<'tcx>,
63+
pub is_arg: bool,
64+
}
65+
66+
#[derive(Diagnostic)]
67+
#[diag(middle_dyn_unsized_param)]
68+
#[note]
69+
pub struct DynUnsizedParam<'tcx> {
70+
#[primary_span]
71+
pub span: Span,
72+
pub ty: Ty<'tcx>,
73+
}
74+
5675
#[derive(Diagnostic)]
5776
#[diag(middle_recursion_limit_reached)]
5877
#[help]

compiler/rustc_middle/src/mir/mod.rs

+58-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//!
33
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html
44
5+
use crate::error::{DynUnsizedLocal, DynUnsizedParam};
56
use crate::mir::interpret::{AllocRange, ConstAllocation, ErrorHandled, Scalar};
67
use crate::mir::visit::MirVisitable;
78
use crate::ty::codec::{TyDecoder, TyEncoder};
@@ -586,14 +587,70 @@ impl<'tcx> Body<'tcx> {
586587
tcx: TyCtxt<'tcx>,
587588
param_env: ty::ParamEnv<'tcx>,
588589
normalize_const: impl Fn(Const<'tcx>) -> Result<Const<'tcx>, ErrorHandled>,
590+
normalize_ty: impl Fn(Ty<'tcx>) -> Result<Ty<'tcx>, ErrorHandled>,
589591
) -> Result<(), ErrorHandled> {
590-
// For now, the only thing we have to check is is to ensure that all the constants used in
592+
// The main thing we have to check is is to ensure that all the constants used in
591593
// the body successfully evaluate.
592594
for &const_ in &self.required_consts {
593595
let c = normalize_const(const_.const_)?;
594596
c.eval(tcx, param_env, Some(const_.span))?;
595597
}
596598

599+
// The second thing we do is guard against unsized locals/arguments that do not have a dynamically computable size.
600+
// Due to a lack of an appropriate trait, we currently have to hack this in
601+
// as a post-mono check.
602+
let mut guaranteed = None; // `Some` if any error was emitted
603+
// First check all locals (including the arguments).
604+
for (idx, local) in self.local_decls.iter_enumerated() {
605+
let ty = local.ty;
606+
// We get invoked in generic code, so we cannot force normalization.
607+
let tail =
608+
tcx.struct_tail_with_normalize(ty, |ty| normalize_ty(ty).unwrap_or(ty), || {});
609+
// If the unsized tail is an `extern type`, emit error.
610+
if matches!(tail.kind(), ty::Foreign(_)) {
611+
assert!(idx.as_u32() > 0); // cannot be the return local
612+
let is_arg = (1..=self.arg_count).contains(&idx.as_usize());
613+
guaranteed = Some(tcx.sess.emit_err(DynUnsizedLocal {
614+
span: local.source_info.span,
615+
ty,
616+
is_arg,
617+
}));
618+
}
619+
}
620+
// The above check covers the callee side. We also need to check the caller.
621+
// For that we check all function calls.
622+
for bb in self.basic_blocks.iter() {
623+
let terminator = bb.terminator();
624+
if let TerminatorKind::Call { args, .. } = &terminator.kind {
625+
for arg in args {
626+
if let Operand::Move(place) = arg {
627+
if place.is_indirect_first_projection() {
628+
// Found an argument of the shape `Move(*arg)`. Make sure
629+
// its size can be determined dynamically.
630+
let ty = place.ty(&self.local_decls, tcx).ty;
631+
let tail = tcx.struct_tail_with_normalize(
632+
ty,
633+
|ty| normalize_ty(ty).unwrap_or(ty),
634+
|| {},
635+
);
636+
// If the unsized tail is an `extern type`, emit error.
637+
if matches!(tail.kind(), ty::Foreign(_)) {
638+
guaranteed = Some(tcx.sess.emit_err(DynUnsizedParam {
639+
span: terminator.source_info.span,
640+
ty,
641+
}));
642+
}
643+
}
644+
}
645+
}
646+
}
647+
}
648+
// Above we made sure to report *all* errors by continuing even after reporting something.
649+
// Here we make sure we return `Err` if there was any error.
650+
if let Some(guaranteed) = guaranteed {
651+
return Err(ErrorHandled::Reported(guaranteed.into(), DUMMY_SP));
652+
}
653+
597654
Ok(())
598655
}
599656
}

compiler/rustc_middle/src/mir/syntax.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -666,9 +666,9 @@ pub enum TerminatorKind<'tcx> {
666666
/// The function that’s being called.
667667
func: Operand<'tcx>,
668668
/// Arguments the function is called with.
669-
/// These are owned by the callee, which is free to modify them.
670-
/// This allows the memory occupied by "by-value" arguments to be
671-
/// reused across function calls without duplicating the contents.
669+
/// Any `Move` operands in this list are owned by the callee, which is free to modify them.
670+
/// This allows the memory occupied by "by-value" arguments to be reused across function
671+
/// calls without duplicating the contents.
672672
args: Vec<Operand<'tcx>>,
673673
/// Where the returned value will be written
674674
destination: Place<'tcx>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![feature(extern_types)]
2+
#![feature(unsized_fn_params)]
3+
4+
extern {
5+
pub type E;
6+
}
7+
8+
fn test(_e: E) {}
9+
10+
pub fn calltest(e: Box<E>) {
11+
test(*e) //~ERROR: does not have a dynamically computable size
12+
}
13+
14+
fn main() {
15+
let b = Box::new(0u32);
16+
calltest(unsafe { std::mem::transmute(b)} );
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: function parameter does not have a dynamically computable size
2+
--> $DIR/unsized-extern-type-arg.rs:LL:CC
3+
|
4+
LL | test(*e)
5+
| ^^^^^^^^
6+
|
7+
= note: `E` contains an `extern type`, which is not allowed in function parameters
8+
9+
error: aborting due to previous error
10+
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// build-fail
2+
#![feature(extern_types)]
3+
#![feature(unsized_fn_params)]
4+
#![crate_type = "lib"]
5+
6+
extern {
7+
pub type E;
8+
}
9+
10+
fn test(e: E) {} //~ERROR: does not have a dynamically computable size
11+
12+
pub fn calltest(e: Box<E>) {
13+
test(*e) //~ERROR: does not have a dynamically computable size
14+
}
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: function argument does not have a dynamically computable size
2+
--> $DIR/extern-type.rs:10:9
3+
|
4+
LL | fn test(e: E) {}
5+
| ^
6+
|
7+
= note: `E` contains an `extern type`, which is not allowed in function arguments
8+
9+
error: function parameter does not have a dynamically computable size
10+
--> $DIR/extern-type.rs:13:5
11+
|
12+
LL | test(*e)
13+
| ^^^^^^^^
14+
|
15+
= note: `E` contains an `extern type`, which is not allowed in function parameters
16+
17+
error: aborting due to 2 previous errors
18+

0 commit comments

Comments
 (0)