Skip to content

Commit f191420

Browse files
committed
Auto merge of #142941 - compiler-errors:shallow-bail, r=lcnr
Shallowly bail from `coerce_unsized` more We do a *lot* of coercion in HIR typeck. Most of the time we're just coercing a type to itself, but we always try `coerce_unsized` even if it's not necessary. Let's avoid doing that by adding a fast path to `coerce_unsized`; see the comment in that function.
2 parents 35f6036 + 0d37587 commit f191420

File tree

1 file changed

+70
-19
lines changed

1 file changed

+70
-19
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
278278
/// fall back to subtyping (`unify_and`).
279279
fn coerce_from_inference_variable(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
280280
debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b);
281-
assert!(a.is_ty_var() && self.shallow_resolve(a) == a);
282-
assert!(self.shallow_resolve(b) == b);
281+
debug_assert!(a.is_ty_var() && self.shallow_resolve(a) == a);
282+
debug_assert!(self.shallow_resolve(b) == b);
283283

284284
if b.is_ty_var() {
285285
// Two unresolved type variables: create a `Coerce` predicate.
@@ -323,6 +323,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
323323
mutbl_b: hir::Mutability,
324324
) -> CoerceResult<'tcx> {
325325
debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
326+
debug_assert!(self.shallow_resolve(a) == a);
327+
debug_assert!(self.shallow_resolve(b) == b);
326328

327329
// If we have a parameter of type `&M T_a` and the value
328330
// provided is `expr`, we will be adding an implicit borrow,
@@ -514,10 +516,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
514516
///
515517
/// [unsized coercion](https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions)
516518
#[instrument(skip(self), level = "debug")]
517-
fn coerce_unsized(&self, mut source: Ty<'tcx>, mut target: Ty<'tcx>) -> CoerceResult<'tcx> {
518-
source = self.shallow_resolve(source);
519-
target = self.shallow_resolve(target);
519+
fn coerce_unsized(&self, source: Ty<'tcx>, target: Ty<'tcx>) -> CoerceResult<'tcx> {
520520
debug!(?source, ?target);
521+
debug_assert!(self.shallow_resolve(source) == source);
522+
debug_assert!(self.shallow_resolve(target) == target);
521523

522524
// We don't apply any coercions incase either the source or target
523525
// aren't sufficiently well known but tend to instead just equate
@@ -531,6 +533,54 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
531533
return Err(TypeError::Mismatch);
532534
}
533535

536+
// This is an optimization because coercion is one of the most common
537+
// operations that we do in typeck, since it happens at every assignment
538+
// and call arg (among other positions).
539+
//
540+
// These targets are known to never be RHS in `LHS: CoerceUnsized<RHS>`.
541+
// That's because these are built-in types for which a core-provided impl
542+
// doesn't exist, and for which a user-written impl is invalid.
543+
//
544+
// This is technically incomplete when users write impossible bounds like
545+
// `where T: CoerceUnsized<usize>`, for example, but that trait is unstable
546+
// and coercion is allowed to be incomplete. The only case where this matters
547+
// is impossible bounds.
548+
//
549+
// Note that some of these types implement `LHS: Unsize<RHS>`, but they
550+
// do not implement *`CoerceUnsized`* which is the root obligation of the
551+
// check below.
552+
match target.kind() {
553+
ty::Bool
554+
| ty::Char
555+
| ty::Int(_)
556+
| ty::Uint(_)
557+
| ty::Float(_)
558+
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
559+
| ty::Str
560+
| ty::Array(_, _)
561+
| ty::Slice(_)
562+
| ty::FnDef(_, _)
563+
| ty::FnPtr(_, _)
564+
| ty::Dynamic(_, _, _)
565+
| ty::Closure(_, _)
566+
| ty::CoroutineClosure(_, _)
567+
| ty::Coroutine(_, _)
568+
| ty::CoroutineWitness(_, _)
569+
| ty::Never
570+
| ty::Tuple(_) => return Err(TypeError::Mismatch),
571+
_ => {}
572+
}
573+
// Additionally, we ignore `&str -> &str` coercions, which happen very
574+
// commonly since strings are one of the most used argument types in Rust,
575+
// we do coercions when type checking call expressions.
576+
if let ty::Ref(_, source_pointee, ty::Mutability::Not) = *source.kind()
577+
&& source_pointee.is_str()
578+
&& let ty::Ref(_, target_pointee, ty::Mutability::Not) = *target.kind()
579+
&& target_pointee.is_str()
580+
{
581+
return Err(TypeError::Mismatch);
582+
}
583+
534584
let traits =
535585
(self.tcx.lang_items().unsize_trait(), self.tcx.lang_items().coerce_unsized_trait());
536586
let (Some(unsize_did), Some(coerce_unsized_did)) = traits else {
@@ -800,6 +850,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
800850
/// - `Pin<Box<T>>` as `Pin<&mut T>`
801851
#[instrument(skip(self), level = "trace")]
802852
fn coerce_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
853+
debug_assert!(self.shallow_resolve(a) == a);
854+
debug_assert!(self.shallow_resolve(b) == b);
855+
803856
// We need to make sure the two types are compatible for coercion.
804857
// Then we will build a ReborrowPin adjustment and return that as an InferOk.
805858

@@ -848,6 +901,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
848901
b: Ty<'tcx>,
849902
adjustment: Option<Adjust>,
850903
) -> CoerceResult<'tcx> {
904+
debug_assert!(self.shallow_resolve(b) == b);
905+
851906
self.commit_if_ok(|snapshot| {
852907
let outer_universe = self.infcx.universe();
853908

@@ -888,24 +943,19 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
888943
fn_ty_a: ty::PolyFnSig<'tcx>,
889944
b: Ty<'tcx>,
890945
) -> CoerceResult<'tcx> {
891-
//! Attempts to coerce from the type of a Rust function item
892-
//! into a closure or a `proc`.
893-
//!
894-
895-
let b = self.shallow_resolve(b);
896946
debug!(?fn_ty_a, ?b, "coerce_from_fn_pointer");
947+
debug_assert!(self.shallow_resolve(b) == b);
897948

898949
self.coerce_from_safe_fn(fn_ty_a, b, None)
899950
}
900951

901952
fn coerce_from_fn_item(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
902-
//! Attempts to coerce from the type of a Rust function item
903-
//! into a closure or a `proc`.
953+
debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b);
954+
debug_assert!(self.shallow_resolve(a) == a);
955+
debug_assert!(self.shallow_resolve(b) == b);
904956

905-
let b = self.shallow_resolve(b);
906957
let InferOk { value: b, mut obligations } =
907958
self.at(&self.cause, self.param_env).normalize(b);
908-
debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b);
909959

910960
match b.kind() {
911961
ty::FnPtr(_, b_hdr) => {
@@ -955,18 +1005,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
9551005
}
9561006
}
9571007

1008+
/// Attempts to coerce from the type of a non-capturing closure
1009+
/// into a function pointer.
9581010
fn coerce_closure_to_fn(
9591011
&self,
9601012
a: Ty<'tcx>,
9611013
closure_def_id_a: DefId,
9621014
args_a: GenericArgsRef<'tcx>,
9631015
b: Ty<'tcx>,
9641016
) -> CoerceResult<'tcx> {
965-
//! Attempts to coerce from the type of a non-capturing closure
966-
//! into a function pointer.
967-
//!
968-
969-
let b = self.shallow_resolve(b);
1017+
debug_assert!(self.shallow_resolve(a) == a);
1018+
debug_assert!(self.shallow_resolve(b) == b);
9701019

9711020
match b.kind() {
9721021
// At this point we haven't done capture analysis, which means
@@ -1010,6 +1059,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
10101059
mutbl_b: hir::Mutability,
10111060
) -> CoerceResult<'tcx> {
10121061
debug!("coerce_raw_ptr(a={:?}, b={:?})", a, b);
1062+
debug_assert!(self.shallow_resolve(a) == a);
1063+
debug_assert!(self.shallow_resolve(b) == b);
10131064

10141065
let (is_ref, mt_a) = match *a.kind() {
10151066
ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }),

0 commit comments

Comments
 (0)