Typecheck dyn* coercions

Also changes things to treat dyn* as a sized type, unlike dyn Trait.
This commit is contained in:
Eric Holk 2022-04-13 16:38:16 -07:00
parent 6c01273a15
commit 7fccac3ea0
11 changed files with 148 additions and 29 deletions

View File

@ -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<u32>` (e.g., derived from `dyn PartialEq<u32>`),
/// 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<u32>`, 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]

View File

@ -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;

View File

@ -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()))

View File

@ -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);

View File

@ -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)
}

View File

@ -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(),
}
}
}

View File

@ -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()

View File

@ -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() {}

View File

@ -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`.

View File

@ -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() {}

View File

@ -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() {}