From 7fccac3ea0db7cbbb1b84ff4ab824f8d85f415fe Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Wed, 13 Apr 2022 16:38:16 -0700 Subject: [PATCH] Typecheck dyn* coercions Also changes things to treat dyn* as a sized type, unlike dyn Trait. --- compiler/rustc_middle/src/ty/sty.rs | 12 ++- .../src/traits/object_safety.rs | 4 +- .../src/traits/select/mod.rs | 2 + compiler/rustc_typeck/src/astconv/mod.rs | 19 +++-- compiler/rustc_typeck/src/check/cast.rs | 80 ++++++++++++++++++- compiler/rustc_typeck/src/check/expr.rs | 20 +++-- .../src/check/fn_ctxt/suggestions.rs | 2 +- .../ui/async-await/dyn-star-trait-error.rs | 12 +++ .../async-await/dyn-star-trait-error.stderr | 9 +++ src/test/ui/dyn-star/make-dyn-star.rs | 14 ++++ src/test/ui/dyn-star/syntax.rs | 3 +- 11 files changed, 148 insertions(+), 29 deletions(-) create mode 100644 src/test/ui/async-await/dyn-star-trait-error.rs create mode 100644 src/test/ui/async-await/dyn-star-trait-error.stderr create mode 100644 src/test/ui/dyn-star/make-dyn-star.rs diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 702c2d27187..6713660ab8f 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -31,7 +31,7 @@ use rustc_type_ir::sty::TyKind::*; use rustc_type_ir::RegionKind as IrRegionKind; -use rustc_type_ir::TyKind as IrTyKind; +use rustc_type_ir::{TraitObjectRepresentation, TyKind as IrTyKind}; // Re-export the `TyKind` from `rustc_type_ir` here for convenience #[rustc_diagnostic_item = "TyKind"] @@ -692,6 +692,9 @@ pub fn stable_cmp(&self, tcx: TyCtxt<'tcx>, other: &Self) -> Ordering { } impl<'tcx> Binder<'tcx, ExistentialPredicate<'tcx>> { + /// Given an existential predicate like `?Self: PartialEq` (e.g., derived from `dyn PartialEq`), + /// and a concrete type `self_ty`, returns a full predicate where the existentially quantified variable `?Self` + /// has been replaced with `self_ty` (e.g., `self_ty: PartialEq`, in our example). pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::Predicate<'tcx> { use crate::ty::ToPredicate; match self.skip_binder() { @@ -1849,7 +1852,12 @@ pub fn is_floating_point(self) -> bool { #[inline] pub fn is_trait(self) -> bool { - matches!(self.kind(), Dynamic(..)) + matches!(self.kind(), Dynamic(_, _, TraitObjectRepresentation::Unsized)) + } + + #[inline] + pub fn is_dyn_star(self) -> bool { + matches!(self.kind(), Dynamic(_, _, TraitObjectRepresentation::Sized)) } #[inline] diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index f31dde044f9..43ee5b55455 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -20,8 +20,8 @@ use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst}; use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst}; use rustc_middle::ty::{ - self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, - TraitObjectRepresentation, + self, EarlyBinder, TraitObjectRepresentation, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitor, }; use rustc_middle::ty::{Predicate, ToPredicate}; use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 8b15e10ba9c..a64179c14f5 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -38,6 +38,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::TraitObjectRepresentation; use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitable}; use rustc_span::symbol::sym; @@ -1865,6 +1866,7 @@ fn sized_conditions( | ty::Array(..) | ty::Closure(..) | ty::Never + | ty::Dynamic(_, _, TraitObjectRepresentation::Sized) | ty::Error(_) => { // safe for everything Where(ty::Binder::dummy(Vec::new())) diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 586ede3d90c..0010bcc4bf6 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -28,10 +28,10 @@ use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; +use rustc_middle::ty::TraitObjectRepresentation; use rustc_middle::ty::{ self, Const, DefIdTree, EarlyBinder, IsSuggestable, Ty, TyCtxt, TypeVisitable, }; -use rustc_middle::ty::{TraitObjectRepresentation}; use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS}; use rustc_span::edition::Edition; use rustc_span::lev_distance::find_best_match_for_name; @@ -1253,6 +1253,7 @@ fn conv_object_ty_poly_trait_ref( trait_bounds: &[hir::PolyTraitRef<'_>], lifetime: &hir::Lifetime, borrowed: bool, + representation: TraitObjectRepresentation, ) -> Ty<'tcx> { let tcx = self.tcx(); @@ -1573,11 +1574,7 @@ trait here instead: `trait NewTrait: {} {{}}`", }; debug!("region_bound: {:?}", region_bound); - let ty = tcx.mk_dynamic( - existential_predicates, - region_bound, - TraitObjectRepresentation::Unsized, // FIXME: check whether the source syntax was dyn or dyn* - ); + let ty = tcx.mk_dynamic(existential_predicates, region_bound, representation); debug!("trait_object_type: {:?}", ty); ty } @@ -2623,9 +2620,15 @@ fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool, in_path: bool Some(ast_ty), )) } - hir::TyKind::TraitObject(bounds, ref lifetime, _) => { + hir::TyKind::TraitObject(bounds, ref lifetime, repr) => { self.maybe_lint_bare_trait(ast_ty, in_path); - self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed) + let repr = match repr { + TraitObjectSyntax::Dyn | TraitObjectSyntax::None => { + TraitObjectRepresentation::Unsized + } + TraitObjectSyntax::DynStar => TraitObjectRepresentation::Sized, + }; + self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed, repr) } hir::TyKind::Path(hir::QPath::Resolved(ref maybe_qself, ref path)) => { debug!(?maybe_qself, ?path); diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index a3fcda5864b..fa6224f1915 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -35,12 +35,15 @@ use hir::def_id::LOCAL_CRATE; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; +use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable, VariantDef}; +use rustc_middle::ty::{ + self, Binder, TraitObjectRepresentation, Ty, TypeAndMut, TypeVisitable, VariantDef, +}; use rustc_session::lint; use rustc_session::Session; use rustc_span::symbol::sym; @@ -52,9 +55,12 @@ /// a function context. #[derive(Debug)] pub struct CastCheck<'tcx> { + /// The expression whose value is being casted expr: &'tcx hir::Expr<'tcx>, + /// The source type for the cast expression expr_ty: Ty<'tcx>, expr_span: Span, + /// The target type. That is, the type we are casting to. cast_ty: Ty<'tcx>, cast_span: Span, span: Span, @@ -199,8 +205,76 @@ fn make_invalid_casting_error<'a, 'tcx>( ) } +pub enum CastCheckResult<'tcx> { + Ok, + Deferred(CastCheck<'tcx>), + Err(ErrorGuaranteed), +} + +pub fn check_cast<'tcx>( + fcx: &FnCtxt<'_, 'tcx>, + expr: &'tcx hir::Expr<'tcx>, + expr_ty: Ty<'tcx>, + cast_ty: Ty<'tcx>, + cast_span: Span, + span: Span, +) -> CastCheckResult<'tcx> { + if cast_ty.is_dyn_star() { + check_dyn_star_cast(fcx, expr, expr_ty, cast_ty) + } else { + match CastCheck::new(fcx, expr, expr_ty, cast_ty, cast_span, span) { + Ok(check) => CastCheckResult::Deferred(check), + Err(e) => CastCheckResult::Err(e), + } + } +} + +fn check_dyn_star_cast<'tcx>( + fcx: &FnCtxt<'_, 'tcx>, + expr: &'tcx hir::Expr<'tcx>, + expr_ty: Ty<'tcx>, + cast_ty: Ty<'tcx>, +) -> CastCheckResult<'tcx> { + // Find the bounds in the dyn*. For eaxmple, if we have + // + // let x = 22_usize as dyn* (Clone + Debug + 'static) + // + // this would return `existential_predicates = [?Self: Clone, ?Self: Debug]` and `region = 'static`. + let (existential_predicates, region) = match cast_ty.kind() { + ty::Dynamic(predicates, region, TraitObjectRepresentation::Sized) => (predicates, region), + _ => panic!("Invalid dyn* cast_ty"), + }; + + let cause = ObligationCause::new( + expr.span, + fcx.body_id, + // FIXME: Use a better obligation cause code + ObligationCauseCode::MiscObligation, + ); + + // For each existential predicate (e.g., `?Self: Clone`) substitute + // the type of the expression (e.g., `usize` in our example above) + // and then require that the resulting predicate (e.g., `usize: Clone`) + // holds (it does). + for existential_predicate in existential_predicates.iter() { + let predicate = existential_predicate.with_self_ty(fcx.tcx, expr_ty); + fcx.register_predicate(Obligation::new(cause.clone(), fcx.param_env, predicate)); + } + + // Enforce the region bound `'static` (e.g., `usize: 'static`, in our example). + fcx.register_predicate(Obligation::new( + cause, + fcx.param_env, + fcx.tcx.mk_predicate(Binder::dummy(ty::PredicateKind::TypeOutlives( + ty::OutlivesPredicate(expr_ty, *region), + ))), + )); + + CastCheckResult::Ok +} + impl<'a, 'tcx> CastCheck<'tcx> { - pub fn new( + fn new( fcx: &FnCtxt<'a, 'tcx>, expr: &'tcx hir::Expr<'tcx>, expr_ty: Ty<'tcx>, @@ -215,7 +289,7 @@ pub fn new( // cases now. We do a more thorough check at the end, once // inference is more completely known. match cast_ty.kind() { - ty::Dynamic(..) | ty::Slice(..) => { + ty::Dynamic(_, _, TraitObjectRepresentation::Unsized) | ty::Slice(..) => { let reported = check.report_cast_to_unsized_type(fcx); Err(reported) } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 21392001364..93b00850069 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -3,18 +3,15 @@ //! See `mod.rs` for more context on type checking in general. use crate::astconv::AstConv as _; -use crate::check::cast; +use crate::check::cast::{self, CastCheckResult}; use crate::check::coercion::CoerceMany; use crate::check::fatally_break_rust; use crate::check::method::SelfSource; -use crate::check::report_unexpected_variant_res; -use crate::check::BreakableCtxt; -use crate::check::Diverges; -use crate::check::DynamicCoerceMany; use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; -use crate::check::FnCtxt; -use crate::check::Needs; -use crate::check::TupleArgumentsFlag::DontTupleArguments; +use crate::check::{ + report_unexpected_variant_res, BreakableCtxt, Diverges, DynamicCoerceMany, FnCtxt, Needs, + TupleArgumentsFlag::DontTupleArguments, +}; use crate::errors::{ FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, YieldExprOutsideOfGenerator, @@ -1252,8 +1249,9 @@ fn check_expr_cast( } else { // Defer other checks until we're done type checking. let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) { - Ok(cast_check) => { + match cast::check_cast(self, e, t_expr, t_cast, t.span, expr.span) { + CastCheckResult::Ok => t_cast, + CastCheckResult::Deferred(cast_check) => { debug!( "check_expr_cast: deferring cast from {:?} to {:?}: {:?}", t_cast, t_expr, cast_check, @@ -1261,7 +1259,7 @@ fn check_expr_cast( deferred_cast_checks.push(cast_check); t_cast } - Err(_) => self.tcx.ty_error(), + CastCheckResult::Err(ErrorGuaranteed { .. }) => self.tcx.ty_error(), } } } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 2bfee9a5364..82b7101baa3 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -182,7 +182,7 @@ pub(in super::super) fn extract_callable_info( } }) } - ty::Dynamic(data, _) => { + ty::Dynamic(data, _, ty::TraitObjectRepresentation::Unsized) => { data.iter().find_map(|pred| { if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output() diff --git a/src/test/ui/async-await/dyn-star-trait-error.rs b/src/test/ui/async-await/dyn-star-trait-error.rs new file mode 100644 index 00000000000..20e183c7455 --- /dev/null +++ b/src/test/ui/async-await/dyn-star-trait-error.rs @@ -0,0 +1,12 @@ +#![feature(async_fn_in_traits)] + +use std::fmt::Debug; + +trait Foo {} + +fn make_dyn_star() { + let i = 42; + let dyn_i: dyn* Foo = i as dyn* Foo; //~ ERROR trait bound `{integer}: Foo` is not satisfied +} + +fn main() {} diff --git a/src/test/ui/async-await/dyn-star-trait-error.stderr b/src/test/ui/async-await/dyn-star-trait-error.stderr new file mode 100644 index 00000000000..932bc50ffde --- /dev/null +++ b/src/test/ui/async-await/dyn-star-trait-error.stderr @@ -0,0 +1,9 @@ +error[E0277]: the trait bound `{integer}: Foo` is not satisfied + --> $DIR/dyn-star-trait-error.rs:9:27 + | +LL | let dyn_i: dyn* Foo = i as dyn* Foo; + | ^ the trait `Foo` is not implemented for `{integer}` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/dyn-star/make-dyn-star.rs b/src/test/ui/dyn-star/make-dyn-star.rs new file mode 100644 index 00000000000..17356bfbd84 --- /dev/null +++ b/src/test/ui/dyn-star/make-dyn-star.rs @@ -0,0 +1,14 @@ +// check-pass +#![feature(dyn_star)] + +use std::fmt::Debug; + +pub fn dyn_star_parameter(_: dyn* Send) { +} + +fn make_dyn_star() { + let i = 42usize; + let dyn_i: dyn* Debug = i as dyn* Debug; +} + +fn main() {} diff --git a/src/test/ui/dyn-star/syntax.rs b/src/test/ui/dyn-star/syntax.rs index dd96bf0672d..7848996e5e0 100644 --- a/src/test/ui/dyn-star/syntax.rs +++ b/src/test/ui/dyn-star/syntax.rs @@ -2,10 +2,9 @@ // // check-pass - #![feature(dyn_star)] -pub fn dyn_star_parameter(_: &dyn* Send) { +pub fn dyn_star_parameter(_: dyn* Send) { } fn main() {}