From 67efbeb0faf69bbabd3489d61577b84318ff7b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 5 Apr 2021 20:06:46 -0700 Subject: [PATCH 1/3] Enforce `Sized` return types on `Fn*` bounds In a `fn() -> Out` bound, enforce `Out: Sized` to avoid unsoundness. Fix #82633. --- .../src/traits/select/confirmation.rs | 24 +++++ .../closure-return-type-must-be-sized.rs | 72 ++++++++++++++ .../closure-return-type-must-be-sized.stderr | 99 +++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 src/test/ui/closures/closure-return-type-must-be-sized.rs create mode 100644 src/test/ui/closures/closure-return-type-must-be-sized.stderr diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 31685a012ca2f..4a5024b003c80 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -529,6 +529,30 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.predicate.to_poly_trait_ref(), trait_ref, )?); + + let lang_items = self.tcx().lang_items(); + if [lang_items.fn_once_trait(), lang_items.fn_trait(), lang_items.fn_mut_trait()] + .contains(&Some(obligation.predicate.def_id())) + { + // Do not allow `foo:: A>();` for `A: !Sized` (#82633) + let fn_sig = obligation.predicate.self_ty().skip_binder().fn_sig(self.tcx()); + let ty = fn_sig.output().skip_binder(); + let trait_ref = ty::TraitRef { + def_id: lang_items.sized_trait().unwrap(), + substs: self.tcx().mk_substs_trait(ty, &[]), + }; + if !matches!(ty.kind(), ty::Projection(_) | ty::Opaque(..)) { + let pred = crate::traits::util::predicate_for_trait_ref( + self.tcx(), + obligation.cause.clone(), + obligation.param_env, + trait_ref, + obligation.recursion_depth, + ); + obligations.push(pred); + } + } + Ok(ImplSourceFnPointerData { fn_ty: self_ty, nested: obligations }) } diff --git a/src/test/ui/closures/closure-return-type-must-be-sized.rs b/src/test/ui/closures/closure-return-type-must-be-sized.rs new file mode 100644 index 0000000000000..8c483f2439b51 --- /dev/null +++ b/src/test/ui/closures/closure-return-type-must-be-sized.rs @@ -0,0 +1,72 @@ +#![feature(unboxed_closures)] + +trait A { + fn a() where Self: Sized; +} + +mod a { + use A; + + pub fn foo>() where F::Output: A { + F::Output::a() + } + + pub fn bar R, R: ?Sized>() {} + + pub fn baz>() where F::Output: A, F::Output: Sized { + F::Output::a() + } +} + +mod b { + use A; + + pub fn foo>() where F::Output: A { + F::Output::a() + } + + pub fn bar R, R: ?Sized>() {} + + pub fn baz>() where F::Output: A, F::Output: Sized { + F::Output::a() + } +} + +mod c { + use A; + + pub fn foo>() where F::Output: A { + F::Output::a() + } + + pub fn bar R, R: ?Sized>() {} + + pub fn baz>() where F::Output: A, F::Output: Sized { + F::Output::a() + } +} + +impl A for Box { + fn a() {} +} + +fn main() { + a::foo:: dyn A>(); //~ERROR E0277 + a::bar:: dyn A, _>(); //~ERROR E0277 + a::baz:: dyn A>(); //~ERROR E0277 + a::foo:: Box>(); // ok + a::bar:: Box, _>(); // ok + a::baz:: Box>(); // ok + b::foo:: dyn A>(); //~ERROR E0277 + b::bar:: dyn A, _>(); //~ERROR E0277 + b::baz:: dyn A>(); //~ERROR E0277 + b::foo:: Box>(); // ok + b::bar:: Box, _>(); // ok + b::baz:: Box>(); // ok + c::foo:: dyn A>(); //~ERROR E0277 + c::bar:: dyn A, _>(); //~ERROR E0277 + c::baz:: dyn A>(); //~ERROR E0277 + c::foo:: Box>(); // ok + c::bar:: Box, _>(); // ok + c::baz:: Box>(); // ok +} diff --git a/src/test/ui/closures/closure-return-type-must-be-sized.stderr b/src/test/ui/closures/closure-return-type-must-be-sized.stderr new file mode 100644 index 0000000000000..f013e1b3c6528 --- /dev/null +++ b/src/test/ui/closures/closure-return-type-must-be-sized.stderr @@ -0,0 +1,99 @@ +error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time + --> $DIR/closure-return-type-must-be-sized.rs:54:5 + | +LL | a::foo:: dyn A>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn A + 'static)` + +error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time + --> $DIR/closure-return-type-must-be-sized.rs:55:5 + | +LL | pub fn bar R, R: ?Sized>() {} + | ------------- required by this bound in `a::bar` +... +LL | a::bar:: dyn A, _>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn A + 'static)` + +error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time + --> $DIR/closure-return-type-must-be-sized.rs:56:5 + | +LL | pub fn baz>() where F::Output: A, F::Output: Sized { + | ----- required by this bound in `a::baz` +... +LL | a::baz:: dyn A>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn A + 'static)` + +error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time + --> $DIR/closure-return-type-must-be-sized.rs:60:5 + | +LL | pub fn foo>() where F::Output: A { + | ------ required by this bound in `b::foo` +... +LL | b::foo:: dyn A>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn A + 'static)` + +error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time + --> $DIR/closure-return-type-must-be-sized.rs:61:5 + | +LL | pub fn bar R, R: ?Sized>() {} + | --------- required by this bound in `b::bar` +... +LL | b::bar:: dyn A, _>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn A + 'static)` + +error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time + --> $DIR/closure-return-type-must-be-sized.rs:62:5 + | +LL | pub fn baz>() where F::Output: A, F::Output: Sized { + | ----- required by this bound in `b::baz` +... +LL | b::baz:: dyn A>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn A + 'static)` + +error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time + --> $DIR/closure-return-type-must-be-sized.rs:66:5 + | +LL | pub fn foo>() where F::Output: A { + | --------- required by this bound in `c::foo` +... +LL | c::foo:: dyn A>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn A + 'static)` + +error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time + --> $DIR/closure-return-type-must-be-sized.rs:67:5 + | +LL | pub fn bar R, R: ?Sized>() {} + | ------------ required by this bound in `c::bar` +... +LL | c::bar:: dyn A, _>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn A + 'static)` + +error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time + --> $DIR/closure-return-type-must-be-sized.rs:68:5 + | +LL | pub fn baz>() where F::Output: A, F::Output: Sized { + | ----- required by this bound in `c::baz` +... +LL | c::baz:: dyn A>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn A + 'static)` + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0277`. From 3f7469d420738d31e1d13544969ad4dfd79bbc62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 5 Apr 2021 20:40:26 -0700 Subject: [PATCH 2/3] Improve output --- .../rustc_trait_selection/src/traits/select/confirmation.rs | 4 +++- src/test/ui/closures/closure-return-type-must-be-sized.stderr | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 4a5024b003c80..97af1d1dc7e20 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -34,7 +34,7 @@ use crate::traits::{ ImplSourceUserDefinedData, }; use crate::traits::{ObjectCastObligation, PredicateObligation, TraitObligation}; -use crate::traits::{Obligation, ObligationCause}; +use crate::traits::{Obligation, ObligationCause, ObligationCauseCode}; use crate::traits::{SelectionError, Unimplemented}; use super::BuiltinImplConditions; @@ -533,6 +533,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let lang_items = self.tcx().lang_items(); if [lang_items.fn_once_trait(), lang_items.fn_trait(), lang_items.fn_mut_trait()] .contains(&Some(obligation.predicate.def_id())) + // Skip `MiscObligation`s for better output. + && matches!(obligation.cause.code, ObligationCauseCode::BindingObligation(_, _)) { // Do not allow `foo:: A>();` for `A: !Sized` (#82633) let fn_sig = obligation.predicate.self_ty().skip_binder().fn_sig(self.tcx()); diff --git a/src/test/ui/closures/closure-return-type-must-be-sized.stderr b/src/test/ui/closures/closure-return-type-must-be-sized.stderr index f013e1b3c6528..3618501c6cf39 100644 --- a/src/test/ui/closures/closure-return-type-must-be-sized.stderr +++ b/src/test/ui/closures/closure-return-type-must-be-sized.stderr @@ -1,6 +1,9 @@ error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time --> $DIR/closure-return-type-must-be-sized.rs:54:5 | +LL | pub fn foo>() where F::Output: A { + | ---------- required by this bound in `a::foo` +... LL | a::foo:: dyn A>(); | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | From 670d20d07907c368d44f1862b866545813377f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 6 Apr 2021 13:00:57 -0700 Subject: [PATCH 3/3] Attempt to pacify CI --- .../rustc_trait_selection/src/traits/select/confirmation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 97af1d1dc7e20..14b36a09d5910 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -538,7 +538,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { { // Do not allow `foo:: A>();` for `A: !Sized` (#82633) let fn_sig = obligation.predicate.self_ty().skip_binder().fn_sig(self.tcx()); - let ty = fn_sig.output().skip_binder(); + let ty = self.infcx.replace_bound_vars_with_placeholders(fn_sig.output()); let trait_ref = ty::TraitRef { def_id: lang_items.sized_trait().unwrap(), substs: self.tcx().mk_substs_trait(ty, &[]),