Skip to content

Commit 21a95f9

Browse files
committed
wf: ensure that non-Rust-functions have sized arguments, and everyone has sized return types
1 parent bbcc169 commit 21a95f9

32 files changed

+261
-98
lines changed

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1509,6 +1509,7 @@ fn check_fn_or_method<'tcx>(
15091509
let has_implicit_self = hir_decl.implicit_self != hir::ImplicitSelfKind::None;
15101510
let mut inputs = sig.inputs().iter().skip(if has_implicit_self { 1 } else { 0 });
15111511
// Check that the argument is a tuple and is sized
1512+
// FIXME: move this to WF check? Currently it is duplicated here and in `confirm_builtin_call` in callee.rs.
15121513
if let Some(ty) = inputs.next() {
15131514
wfcx.register_bound(
15141515
ObligationCause::new(span, wfcx.body_def_id, ObligationCauseCode::RustCall),

compiler/rustc_hir_analysis/src/hir_wf_check.rs

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ pub fn provide(providers: &mut Providers) {
1313
*providers = Providers { diagnostic_hir_wf_check, ..*providers };
1414
}
1515

16+
/// HIR-based well-formedness check, for diagnostics only.
17+
/// This is run after there was a WF error, to try get a better message pointing out what went wrong
18+
/// here.
1619
// Ideally, this would be in `rustc_trait_selection`, but we
1720
// need access to `ItemCtxt`
1821
fn diagnostic_hir_wf_check<'tcx>(

compiler/rustc_hir_typeck/src/callee.rs

+1
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
458458
);
459459

460460
if fn_sig.abi == abi::Abi::RustCall {
461+
// FIXME: move this to WF check? Currently it is duplicated here and in `check_fn_or_method` in wfcheck.rs.
461462
let sp = arg_exprs.last().map_or(call_expr.span, |expr| expr.span);
462463
if let Some(ty) = fn_sig.inputs().last().copied() {
463464
self.register_bound(

compiler/rustc_hir_typeck/src/check.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,10 @@ pub(super) fn check_fn<'a, 'tcx>(
9797
fcx.check_pat_top(&param.pat, param_ty, ty_span, None, None);
9898

9999
// Check that argument is Sized.
100-
if !params_can_be_unsized {
100+
// FIXME: can we share this (and the return type check below) with WF-checking on function
101+
// signatures? However, here we have much better spans available than if we fire an
102+
// obligation for our signature to be well-formed.
103+
if !params_can_be_unsized || !fn_sig.abi.supports_unsized_args() {
101104
fcx.require_type_is_sized(
102105
param_ty,
103106
param.pat.span,

compiler/rustc_hir_typeck/src/expr.rs

+2
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
503503
self.resolve_lang_item_path(lang_item, expr.span, expr.hir_id, hir_id).1
504504
}
505505

506+
/// Called for any way that a path is mentioned in an expression.
507+
/// If the path is used in a function call, `args` has the arguments, otherwise it is empty.
506508
pub(crate) fn check_expr_path(
507509
&self,
508510
qpath: &'tcx hir::QPath<'tcx>,

compiler/rustc_target/src/abi/call/x86_64.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,9 @@ fn reg_component(cls: &[Option<Class>], i: &mut usize, size: Size) -> Option<Reg
153153
}
154154
}
155155

156-
fn cast_target(cls: &[Option<Class>], size: Size) -> Option<CastTarget> {
156+
fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
157157
let mut i = 0;
158-
let lo = reg_component(cls, &mut i, size)?;
158+
let lo = reg_component(cls, &mut i, size).unwrap();
159159
let offset = Size::from_bytes(8) * (i as u64);
160160
let mut target = CastTarget::from(lo);
161161
if size > offset {
@@ -164,7 +164,7 @@ fn cast_target(cls: &[Option<Class>], size: Size) -> Option<CastTarget> {
164164
}
165165
}
166166
assert_eq!(reg_component(cls, &mut i, Size::ZERO), None);
167-
Some(target)
167+
target
168168
}
169169

170170
const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9
@@ -227,9 +227,7 @@ where
227227
// split into sized chunks passed individually
228228
if arg.layout.is_aggregate() {
229229
let size = arg.layout.size;
230-
if let Some(cast_target) = cast_target(cls, size) {
231-
arg.cast_to(cast_target);
232-
}
230+
arg.cast_to(cast_target(cls, size));
233231
} else {
234232
arg.extend_integer_width_to(32);
235233
}

compiler/rustc_target/src/spec/abi.rs

+9
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,15 @@ impl Abi {
8686
_ => false,
8787
}
8888
}
89+
90+
/// Whether this ABI can in principle support unsized arguments.
91+
/// There might be further restrictions such as nightly feature flags!
92+
pub fn supports_unsized_args(self) -> bool {
93+
match self {
94+
Self::Rust | Self::RustCall | Self::RustIntrinsic | Self::PlatformIntrinsic => true,
95+
_ => false,
96+
}
97+
}
8998
}
9099

91100
#[derive(Copy, Clone)]

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+17-10
Original file line numberDiff line numberDiff line change
@@ -1863,7 +1863,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
18631863
);
18641864
}
18651865

1866-
let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(obligation.cause.body_id));
1866+
let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else {
1867+
return false;
1868+
};
1869+
let body = self.tcx.hir().body(body);
18671870

18681871
let mut visitor = ReturnsVisitor::default();
18691872
visitor.visit_body(&body);
@@ -1922,15 +1925,19 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
19221925
// Point at all the `return`s in the function as they have failed trait bounds.
19231926
let mut visitor = ReturnsVisitor::default();
19241927
visitor.visit_body(&body);
1925-
let typeck_results = self.typeck_results.as_ref().unwrap();
1926-
for expr in &visitor.returns {
1927-
if let Some(returned_ty) = typeck_results.node_type_opt(expr.hir_id) {
1928-
let ty = self.resolve_vars_if_possible(returned_ty);
1929-
if ty.references_error() {
1930-
// don't print out the [type error] here
1931-
err.delay_as_bug();
1932-
} else {
1933-
err.span_label(expr.span, format!("this returned value is of type `{ty}`"));
1928+
if let Some(typeck_results) = self.typeck_results.as_ref() {
1929+
for expr in &visitor.returns {
1930+
if let Some(returned_ty) = typeck_results.node_type_opt(expr.hir_id) {
1931+
let ty = self.resolve_vars_if_possible(returned_ty);
1932+
if ty.references_error() {
1933+
// don't print out the [type error] here
1934+
err.delay_as_bug();
1935+
} else {
1936+
err.span_label(
1937+
expr.span,
1938+
format!("this returned value is of type `{ty}`"),
1939+
);
1940+
}
19341941
}
19351942
}
19361943
}

compiler/rustc_trait_selection/src/traits/wf.rs

+20-3
Original file line numberDiff line numberDiff line change
@@ -727,9 +727,10 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
727727
self.out.extend(obligations);
728728
}
729729

730-
ty::FnPtr(_) => {
731-
// let the loop iterate into the argument/return
732-
// types appearing in the fn signature
730+
ty::FnPtr(fn_sig) => {
731+
// The loop iterates into the argument/return types appearing in the fn
732+
// signature, but we need to do some extra checks.
733+
self.compute_fn_sig_obligations(fn_sig)
733734
}
734735

735736
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
@@ -806,6 +807,22 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
806807
}
807808
}
808809

810+
/// Add the obligations for this signature to be well-formed to `out`.
811+
fn compute_fn_sig_obligations(&mut self, sig: ty::PolyFnSig<'tcx>) {
812+
// The return type must always be sized.
813+
// FIXME(RalfJung): is skip_binder right? It's what the type walker used in `compute` also does.
814+
self.require_sized(sig.skip_binder().output(), traits::SizedReturnType);
815+
// For non-Rust ABIs, the argument type must always be sized.
816+
// FIXME(RalfJung): we don't do the Rust ABI check here, since that depends on feature gates
817+
// and it's not clear to me whether WF depending on feature gates (which can differ across
818+
// crates) is possible or not.
819+
if !sig.skip_binder().abi.supports_unsized_args() {
820+
for &arg in sig.skip_binder().inputs() {
821+
self.require_sized(arg, traits::SizedArgumentType(None));
822+
}
823+
}
824+
}
825+
809826
#[instrument(level = "debug", skip(self))]
810827
fn nominal_obligations(
811828
&mut self,

tests/ui/abi/compatibility.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -307,19 +307,30 @@ mod arrays {
307307
}
308308

309309
// Some tests with unsized types (not all wrappers are compatible with that).
310+
macro_rules! assert_abi_compatible_unsized {
311+
($name:ident, $t1:ty, $t2:ty) => {
312+
mod $name {
313+
use super::*;
314+
// Declaring a `type` doesn't even check well-formedness, so we also declare a function.
315+
fn check_wf(_x: $t1, _y: $t2) {}
316+
// Can only test arguments and only the Rust ABI, since it's unsized.
317+
#[rustc_abi(assert_eq)]
318+
type TestRust = (fn($t1), fn($t2));
319+
}
320+
};
321+
}
310322
macro_rules! test_transparent_unsized {
311323
($name:ident, $t:ty) => {
312324
mod $name {
313325
use super::*;
314-
assert_abi_compatible!(wrap1, $t, Wrapper1<$t>);
315-
assert_abi_compatible!(wrap1_reprc, ReprC1<$t>, ReprC1<Wrapper1<$t>>);
316-
assert_abi_compatible!(wrap2, $t, Wrapper2<$t>);
317-
assert_abi_compatible!(wrap2_reprc, ReprC1<$t>, ReprC1<Wrapper2<$t>>);
326+
assert_abi_compatible_unsized!(wrap1, $t, Wrapper1<$t>);
327+
assert_abi_compatible_unsized!(wrap1_reprc, ReprC1<$t>, ReprC1<Wrapper1<$t>>);
328+
assert_abi_compatible_unsized!(wrap2, $t, Wrapper2<$t>);
329+
assert_abi_compatible_unsized!(wrap2_reprc, ReprC1<$t>, ReprC1<Wrapper2<$t>>);
318330
}
319331
};
320332
}
321333

322-
#[cfg(not(any(target_arch = "mips64", target_arch = "sparc64")))]
323334
mod unsized_ {
324335
use super::*;
325336
test_transparent_unsized!(str_, str);

tests/ui/abi/issue-94223.rs

-8
This file was deleted.

tests/ui/associated-types/associated-types-unsized.fixed

-14
This file was deleted.

tests/ui/associated-types/associated-types-unsized.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// run-rustfix
21
#![allow(dead_code, unused_variables)]
32

43
trait Get {
@@ -8,6 +7,7 @@ trait Get {
87

98
fn foo<T:Get>(t: T) {
109
let x = t.get(); //~ ERROR the size for values of type
10+
//~| ERROR the size for values of type
1111
}
1212

1313
fn main() {

tests/ui/associated-types/associated-types-unsized.stderr

+15-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
error[E0277]: the size for values of type `<T as Get>::Value` cannot be known at compilation time
2-
--> $DIR/associated-types-unsized.rs:10:9
2+
--> $DIR/associated-types-unsized.rs:9:15
3+
|
4+
LL | let x = t.get();
5+
| ^^^ doesn't have a size known at compile-time
6+
|
7+
= help: the trait `Sized` is not implemented for `<T as Get>::Value`
8+
= note: the return type of a function must have a statically known size
9+
help: consider further restricting the associated type
10+
|
11+
LL | fn foo<T:Get>(t: T) where <T as Get>::Value: Sized {
12+
| ++++++++++++++++++++++++++++++
13+
14+
error[E0277]: the size for values of type `<T as Get>::Value` cannot be known at compilation time
15+
--> $DIR/associated-types-unsized.rs:9:9
316
|
417
LL | let x = t.get();
518
| ^ doesn't have a size known at compile-time
@@ -12,6 +25,6 @@ help: consider further restricting the associated type
1225
LL | fn foo<T:Get>(t: T) where <T as Get>::Value: Sized {
1326
| ++++++++++++++++++++++++++++++
1427

15-
error: aborting due to previous error
28+
error: aborting due to 2 previous errors
1629

1730
For more information about this error, try `rustc --explain E0277`.

tests/ui/closures/closure-return-type-must-be-sized.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -52,21 +52,21 @@ impl A for Box<dyn A> {
5252

5353
fn main() {
5454
a::foo::<fn() -> dyn A>(); //~ ERROR E0277
55-
a::bar::<fn() -> dyn A, _>(); //~ ERROR E0277
55+
a::bar::<fn() -> dyn A, _>(); //~ ERROR cannot have an unboxed trait object
5656
a::baz::<fn() -> dyn A>(); //~ ERROR E0277
5757
a::foo::<fn() -> Box<dyn A>>(); // ok
5858
a::bar::<fn() -> Box<dyn A>, _>(); // ok
5959
a::baz::<fn() -> Box<dyn A>>(); // ok
6060

6161
b::foo::<fn() -> dyn A>(); //~ ERROR E0277
62-
b::bar::<fn() -> dyn A, _>(); //~ ERROR E0277
62+
b::bar::<fn() -> dyn A, _>(); //~ ERROR cannot have an unboxed trait object
6363
b::baz::<fn() -> dyn A>(); //~ ERROR E0277
6464
b::foo::<fn() -> Box<dyn A>>(); // ok
6565
b::bar::<fn() -> Box<dyn A>, _>(); // ok
6666
b::baz::<fn() -> Box<dyn A>>(); // ok
6767

6868
c::foo::<fn() -> dyn A>(); //~ ERROR E0277
69-
c::bar::<fn() -> dyn A, _>(); //~ ERROR E0277
69+
c::bar::<fn() -> dyn A, _>(); //~ ERROR cannot have an unboxed trait object
7070
c::baz::<fn() -> dyn A>(); //~ ERROR E0277
7171
c::foo::<fn() -> Box<dyn A>>(); // ok
7272
c::bar::<fn() -> Box<dyn A>, _>(); // ok

tests/ui/closures/closure-return-type-must-be-sized.stderr

+14-22
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,16 @@ LL | a::foo::<fn() -> dyn A>();
77
= help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
88
= note: required because it appears within the type `fn() -> dyn A`
99

10-
error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
10+
error[E0746]: return type cannot have an unboxed trait object
1111
--> $DIR/closure-return-type-must-be-sized.rs:55:14
1212
|
1313
LL | a::bar::<fn() -> dyn A, _>();
1414
| ^^^^^^^^^^^^^ doesn't have a size known at compile-time
1515
|
16-
= help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
17-
= note: required because it appears within the type `fn() -> dyn A`
18-
note: required by a bound in `a::bar`
19-
--> $DIR/closure-return-type-must-be-sized.rs:14:19
16+
help: box the return type, and wrap all of the returned values in `Box::new`
2017
|
21-
LL | pub fn bar<F: FnOnce() -> R, R: ?Sized>() {}
22-
| ^^^^^^^^^^^^^ required by this bound in `bar`
18+
LL | a::bar::<Box<fn() -> dyn A>, _>();
19+
| ++++ +
2320

2421
error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
2522
--> $DIR/closure-return-type-must-be-sized.rs:56:5
@@ -39,19 +36,16 @@ LL | b::foo::<fn() -> dyn A>();
3936
= help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
4037
= note: required because it appears within the type `fn() -> dyn A`
4138

42-
error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
39+
error[E0746]: return type cannot have an unboxed trait object
4340
--> $DIR/closure-return-type-must-be-sized.rs:62:14
4441
|
4542
LL | b::bar::<fn() -> dyn A, _>();
4643
| ^^^^^^^^^^^^^ doesn't have a size known at compile-time
4744
|
48-
= help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
49-
= note: required because it appears within the type `fn() -> dyn A`
50-
note: required by a bound in `b::bar`
51-
--> $DIR/closure-return-type-must-be-sized.rs:28:19
45+
help: box the return type, and wrap all of the returned values in `Box::new`
5246
|
53-
LL | pub fn bar<F: Fn() -> R, R: ?Sized>() {}
54-
| ^^^^^^^^^ required by this bound in `bar`
47+
LL | b::bar::<Box<fn() -> dyn A>, _>();
48+
| ++++ +
5549

5650
error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
5751
--> $DIR/closure-return-type-must-be-sized.rs:63:5
@@ -71,19 +65,16 @@ LL | c::foo::<fn() -> dyn A>();
7165
= help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
7266
= note: required because it appears within the type `fn() -> dyn A`
7367

74-
error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
68+
error[E0746]: return type cannot have an unboxed trait object
7569
--> $DIR/closure-return-type-must-be-sized.rs:69:14
7670
|
7771
LL | c::bar::<fn() -> dyn A, _>();
7872
| ^^^^^^^^^^^^^ doesn't have a size known at compile-time
7973
|
80-
= help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
81-
= note: required because it appears within the type `fn() -> dyn A`
82-
note: required by a bound in `c::bar`
83-
--> $DIR/closure-return-type-must-be-sized.rs:42:19
74+
help: box the return type, and wrap all of the returned values in `Box::new`
8475
|
85-
LL | pub fn bar<F: FnMut() -> R, R: ?Sized>() {}
86-
| ^^^^^^^^^^^^ required by this bound in `bar`
76+
LL | c::bar::<Box<fn() -> dyn A>, _>();
77+
| ++++ +
8778

8879
error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
8980
--> $DIR/closure-return-type-must-be-sized.rs:70:5
@@ -96,4 +87,5 @@ LL | c::baz::<fn() -> dyn A>();
9687

9788
error: aborting due to 9 previous errors
9889

99-
For more information about this error, try `rustc --explain E0277`.
90+
Some errors have detailed explanations: E0277, E0746.
91+
For more information about an error, try `rustc --explain E0277`.

tests/ui/function-pointer/unsized-ret.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ fn foo<F: Fn<T>, T:std::marker::Tuple>(f: Option<F>, t: T) {
99
fn main() {
1010
foo::<fn() -> str, _>(None, ());
1111
//~^ ERROR the size for values of type `str` cannot be known at compilation time
12+
//~| ERROR the size for values of type `str` cannot be known at compilation time
1213

1314
foo::<for<'a> fn(&'a ()) -> (dyn std::fmt::Display + 'a), _>(None, (&(),));
1415
//~^ ERROR the size for values of type `(dyn std::fmt::Display + 'a)` cannot be known at compilation time

0 commit comments

Comments
 (0)