Auto merge of rust-lang/rust#141926 - compiler-errors:perf-unsizing, … · rust-lang/rust@8eb675b (original) (raw)

`@@ -35,13 +35,13 @@

`

35

35

`` //! // and are then unable to coerce &7i32 to &mut i32.

``

36

36

```` //! ```

````

37

37

``

38

``

`-

use std::ops::Deref;

`

``

38

`+

use std::ops::{ControlFlow, Deref};

`

39

39

``

40

40

`use rustc_attr_data_structures::InlineAttr;

`

41

41

`use rustc_errors::codes::*;

`

42

42

`use rustc_errors::{Applicability, Diag, struct_span_code_err};

`

43

``

`-

use rustc_hir as hir;

`

44

43

`use rustc_hir::def_id::{DefId, LocalDefId};

`

``

44

`+

use rustc_hir::{self as hir, LangItem};

`

45

45

`use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;

`

46

46

`use rustc_infer::infer::relate::RelateResult;

`

47

47

`use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};

`

`@@ -57,6 +57,8 @@ use rustc_middle::ty::error::TypeError;

`

57

57

`use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};

`

58

58

`use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span};

`

59

59

`use rustc_trait_selection::infer::InferCtxtExt as _;

`

``

60

`+

use rustc_trait_selection::solve::inspect::{self, InferCtxtProofTreeExt, ProofTreeVisitor};

`

``

61

`+

use rustc_trait_selection::solve::{Certainty, Goal, NoSolution};

`

60

62

`use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;

`

61

63

`use rustc_trait_selection::traits::{

`

62

64

`self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt,

`

`@@ -593,118 +595,128 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {

`

593

595

`Adjust::Pointer(PointerCoercion::Unsize),

`

594

596

`)?;

`

595

597

``

596

``

`-

let mut selcx = traits::SelectionContext::new(self);

`

597

``

-

598

598

`` // Create an obligation for Source: CoerceUnsized<Target>.

``

599

599

`let cause = self.cause(self.cause.span, ObligationCauseCode::Coercion { source, target });

`

``

600

`+

let pred = ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]);

`

``

601

`+

let obligation = Obligation::new(self.tcx, cause, self.fcx.param_env, pred);

`

600

602

``

601

``

`-

// Use a FIFO queue for this custom fulfillment procedure.

`

602

``

`-

//

`

603

``

`-

// A Vec (or SmallVec) is not a natural choice for a queue. However,

`

604

``

`-

// this code path is hot, and this queue usually has a max length of 1

`

605

``

`-

// and almost never more than 3. By using a SmallVec we avoid an

`

606

``

`-

// allocation, at the (very small) cost of (occasionally) having to

`

607

``

`-

// shift subsequent elements down when removing the front element.

`

608

``

`-

let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![Obligation::new(

`

609

``

`-

self.tcx,

`

610

``

`-

cause,

`

611

``

`-

self.fcx.param_env,

`

612

``

`-

ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target])

`

613

``

`-

)];

`

614

``

-

615

``

`` -

// Keep resolving CoerceUnsized and Unsize predicates to avoid

``

616

``

`` -

// emitting a coercion in cases like Foo<$1> -> Foo<$2>, where

``

617

``

`-

// inference might unify those two inner type variables later.

`

618

``

`-

let traits = [coerce_unsized_did, unsize_did];

`

619

``

`-

while !queue.is_empty() {

`

620

``

`-

let obligation = queue.remove(0);

`

621

``

`-

let trait_pred = match obligation.predicate.kind().no_bound_vars() {

`

622

``

`-

Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)))

`

623

``

`-

if traits.contains(&trait_pred.def_id()) =>

`

624

``

`-

{

`

625

``

`-

self.resolve_vars_if_possible(trait_pred)

`

626

``

`-

}

`

627

``

`-

// Eagerly process alias-relate obligations in new trait solver,

`

628

``

`-

// since these can be emitted in the process of solving trait goals,

`

629

``

`-

// but we need to constrain vars before processing goals mentioning

`

630

``

`-

// them.

`

631

``

`-

Some(ty::PredicateKind::AliasRelate(..)) => {

`

632

``

`-

let ocx = ObligationCtxt::new(self);

`

633

``

`-

ocx.register_obligation(obligation);

`

634

``

`-

if !ocx.select_where_possible().is_empty() {

`

635

``

`-

return Err(TypeError::Mismatch);

`

``

603

`+

if self.next_trait_solver() {

`

``

604

`+

coercion.obligations.push(obligation);

`

``

605

+

``

606

`+

if self

`

``

607

`+

.infcx

`

``

608

`+

.visit_proof_tree(

`

``

609

`+

Goal::new(self.tcx, self.param_env, pred),

`

``

610

`+

&mut CoerceVisitor { fcx: self.fcx, span: self.cause.span },

`

``

611

`+

)

`

``

612

`+

.is_break()

`

``

613

`+

{

`

``

614

`+

return Err(TypeError::Mismatch);

`

``

615

`+

}

`

``

616

`+

} else {

`

``

617

`+

let mut selcx = traits::SelectionContext::new(self);

`

``

618

`+

// Use a FIFO queue for this custom fulfillment procedure.

`

``

619

`+

//

`

``

620

`+

// A Vec (or SmallVec) is not a natural choice for a queue. However,

`

``

621

`+

// this code path is hot, and this queue usually has a max length of 1

`

``

622

`+

// and almost never more than 3. By using a SmallVec we avoid an

`

``

623

`+

// allocation, at the (very small) cost of (occasionally) having to

`

``

624

`+

// shift subsequent elements down when removing the front element.

`

``

625

`+

let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![obligation];

`

``

626

+

``

627

`` +

// Keep resolving CoerceUnsized and Unsize predicates to avoid

``

``

628

`` +

// emitting a coercion in cases like Foo<$1> -> Foo<$2>, where

``

``

629

`+

// inference might unify those two inner type variables later.

`

``

630

`+

let traits = [coerce_unsized_did, unsize_did];

`

``

631

`+

while !queue.is_empty() {

`

``

632

`+

let obligation = queue.remove(0);

`

``

633

`+

let trait_pred = match obligation.predicate.kind().no_bound_vars() {

`

``

634

`+

Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)))

`

``

635

`+

if traits.contains(&trait_pred.def_id()) =>

`

``

636

`+

{

`

``

637

`+

self.resolve_vars_if_possible(trait_pred)

`

636

638

`}

`

637

``

`-

coercion.obligations.extend(ocx.into_pending_obligations());

`

638

``

`-

continue;

`

639

``

`-

}

`

640

``

`-

_ => {

`

641

``

`-

coercion.obligations.push(obligation);

`

642

``

`-

continue;

`

643

``

`-

}

`

644

``

`-

};

`

645

``

`-

debug!("coerce_unsized resolve step: {:?}", trait_pred);

`

646

``

`-

match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) {

`

647

``

`-

// Uncertain or unimplemented.

`

648

``

`-

Ok(None) => {

`

649

``

`-

if trait_pred.def_id() == unsize_did {

`

650

``

`-

let self_ty = trait_pred.self_ty();

`

651

``

`-

let unsize_ty = trait_pred.trait_ref.args[1].expect_ty();

`

652

``

`-

debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred);

`

653

``

`-

match (self_ty.kind(), unsize_ty.kind()) {

`

654

``

`-

(&ty::Infer(ty::TyVar(v)), ty::Dynamic(..))

`

655

``

`-

if self.type_var_is_sized(v) =>

`

656

``

`-

{

`

657

``

`-

debug!("coerce_unsized: have sized infer {:?}", v);

`

658

``

`-

coercion.obligations.push(obligation);

`

659

``

`` -

// $0: Unsize<dyn Trait> where we know that $0: Sized, try going

``

660

``

`-

// for unsizing.

`

661

``

`-

}

`

662

``

`-

_ => {

`

663

``

`` -

// Some other case for $0: Unsize<Something>. Note that we

``

664

``

`` -

// hit this case even if Something is a sized type, so just

``

665

``

`-

// don't do the coercion.

`

666

``

`-

debug!("coerce_unsized: ambiguous unsize");

`

667

``

`-

return Err(TypeError::Mismatch);

`

``

639

`+

// Eagerly process alias-relate obligations in new trait solver,

`

``

640

`+

// since these can be emitted in the process of solving trait goals,

`

``

641

`+

// but we need to constrain vars before processing goals mentioning

`

``

642

`+

// them.

`

``

643

`+

Some(ty::PredicateKind::AliasRelate(..)) => {

`

``

644

`+

let ocx = ObligationCtxt::new(self);

`

``

645

`+

ocx.register_obligation(obligation);

`

``

646

`+

if !ocx.select_where_possible().is_empty() {

`

``

647

`+

return Err(TypeError::Mismatch);

`

``

648

`+

}

`

``

649

`+

coercion.obligations.extend(ocx.into_pending_obligations());

`

``

650

`+

continue;

`

``

651

`+

}

`

``

652

`+

_ => {

`

``

653

`+

coercion.obligations.push(obligation);

`

``

654

`+

continue;

`

``

655

`+

}

`

``

656

`+

};

`

``

657

`+

debug!("coerce_unsized resolve step: {:?}", trait_pred);

`

``

658

`+

match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) {

`

``

659

`+

// Uncertain or unimplemented.

`

``

660

`+

Ok(None) => {

`

``

661

`+

if trait_pred.def_id() == unsize_did {

`

``

662

`+

let self_ty = trait_pred.self_ty();

`

``

663

`+

let unsize_ty = trait_pred.trait_ref.args[1].expect_ty();

`

``

664

`+

debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred);

`

``

665

`+

match (self_ty.kind(), unsize_ty.kind()) {

`

``

666

`+

(&ty::Infer(ty::TyVar(v)), ty::Dynamic(..))

`

``

667

`+

if self.type_var_is_sized(v) =>

`

``

668

`+

{

`

``

669

`+

debug!("coerce_unsized: have sized infer {:?}", v);

`

``

670

`+

coercion.obligations.push(obligation);

`

``

671

`` +

// $0: Unsize<dyn Trait> where we know that $0: Sized, try going

``

``

672

`+

// for unsizing.

`

``

673

`+

}

`

``

674

`+

_ => {

`

``

675

`` +

// Some other case for $0: Unsize<Something>. Note that we

``

``

676

`` +

// hit this case even if Something is a sized type, so just

``

``

677

`+

// don't do the coercion.

`

``

678

`+

debug!("coerce_unsized: ambiguous unsize");

`

``

679

`+

return Err(TypeError::Mismatch);

`

``

680

`+

}

`

668

681

`}

`

``

682

`+

} else {

`

``

683

`+

debug!("coerce_unsized: early return - ambiguous");

`

``

684

`+

return Err(TypeError::Mismatch);

`

669

685

`}

`

670

``

`-

} else {

`

671

``

`-

debug!("coerce_unsized: early return - ambiguous");

`

``

686

`+

}

`

``

687

`+

Err(traits::Unimplemented) => {

`

``

688

`+

debug!("coerce_unsized: early return - can't prove obligation");

`

672

689

`return Err(TypeError::Mismatch);

`

673

690

`}

`

674

``

`-

}

`

675

``

`-

Err(traits::Unimplemented) => {

`

676

``

`-

debug!("coerce_unsized: early return - can't prove obligation");

`

677

``

`-

return Err(TypeError::Mismatch);

`

678

``

`-

}

`

679

691

``

680

``

`-

Err(SelectionError::TraitDynIncompatible(_)) => {

`

681

``

`-

// Dyn compatibility errors in coercion will always be due to the

`

682

``

`` -

// fact that the RHS of the coercion is a non-dyn compatible dyn Trait

``

683

``

`-

// writen in source somewhere (otherwise we will never have lowered

`

684

``

`-

// the dyn trait from HIR to middle).

`

685

``

`-

//

`

686

``

`-

// There's no reason to emit yet another dyn compatibility error,

`

687

``

`-

// especially since the span will differ slightly and thus not be

`

688

``

`-

// deduplicated at all!

`

689

``

`-

self.fcx.set_tainted_by_errors(

`

690

``

`-

self.fcx

`

691

``

`-

.dcx()

`

692

``

`-

.span_delayed_bug(self.cause.span, "dyn compatibility during coercion"),

`

693

``

`-

);

`

694

``

`-

}

`

695

``

`-

Err(err) => {

`

696

``

`-

let guar = self.err_ctxt().report_selection_error(

`

697

``

`-

obligation.clone(),

`

698

``

`-

&obligation,

`

699

``

`-

&err,

`

700

``

`-

);

`

701

``

`-

self.fcx.set_tainted_by_errors(guar);

`

702

``

`-

// Treat this like an obligation and follow through

`

703

``

`-

// with the unsizing - the lack of a coercion should

`

704

``

`-

// be silent, as it causes a type mismatch later.

`

705

``

`-

}

`

``

692

`+

Err(SelectionError::TraitDynIncompatible(_)) => {

`

``

693

`+

// Dyn compatibility errors in coercion will always be due to the

`

``

694

`` +

// fact that the RHS of the coercion is a non-dyn compatible dyn Trait

``

``

695

`+

// writen in source somewhere (otherwise we will never have lowered

`

``

696

`+

// the dyn trait from HIR to middle).

`

``

697

`+

//

`

``

698

`+

// There's no reason to emit yet another dyn compatibility error,

`

``

699

`+

// especially since the span will differ slightly and thus not be

`

``

700

`+

// deduplicated at all!

`

``

701

`+

self.fcx.set_tainted_by_errors(self.fcx.dcx().span_delayed_bug(

`

``

702

`+

self.cause.span,

`

``

703

`+

"dyn compatibility during coercion",

`

``

704

`+

));

`

``

705

`+

}

`

``

706

`+

Err(err) => {

`

``

707

`+

let guar = self.err_ctxt().report_selection_error(

`

``

708

`+

obligation.clone(),

`

``

709

`+

&obligation,

`

``

710

`+

&err,

`

``

711

`+

);

`

``

712

`+

self.fcx.set_tainted_by_errors(guar);

`

``

713

`+

// Treat this like an obligation and follow through

`

``

714

`+

// with the unsizing - the lack of a coercion should

`

``

715

`+

// be silent, as it causes a type mismatch later.

`

``

716

`+

}

`

706

717

``

707

``

`-

Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()),

`

``

718

`+

Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()),

`

``

719

`+

}

`

708

720

`}

`

709

721

`}

`

710

722

``

`@@ -2022,3 +2034,49 @@ impl AsCoercionSite for hir::Arm<'_> {

`

2022

2034

`self.body

`

2023

2035

`}

`

2024

2036

`}

`

``

2037

+

``

2038

`+

struct CoerceVisitor<'a, 'tcx> {

`

``

2039

`+

fcx: &'a FnCtxt<'a, 'tcx>,

`

``

2040

`+

span: Span,

`

``

2041

`+

}

`

``

2042

+

``

2043

`+

impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> {

`

``

2044

`+

type Result = ControlFlow<()>;

`

``

2045

+

``

2046

`+

fn span(&self) -> Span {

`

``

2047

`+

self.span

`

``

2048

`+

}

`

``

2049

+

``

2050

`+

fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {

`

``

2051

`+

let Some(pred) = goal.goal().predicate.as_trait_clause() else {

`

``

2052

`+

return ControlFlow::Continue(());

`

``

2053

`+

};

`

``

2054

+

``

2055

`+

if !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize)

`

``

2056

`+

&& !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::CoerceUnsized)

`

``

2057

`+

{

`

``

2058

`+

return ControlFlow::Continue(());

`

``

2059

`+

}

`

``

2060

+

``

2061

`+

match goal.result() {

`

``

2062

`+

Ok(Certainty::Yes) => ControlFlow::Continue(()),

`

``

2063

`+

Err(NoSolution) => ControlFlow::Break(()),

`

``

2064

`+

Ok(Certainty::Maybe(_)) => {

`

``

2065

`+

// FIXME: structurally normalize?

`

``

2066

`+

if self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize)

`

``

2067

`+

&& let ty::Dynamic(..) = pred.skip_binder().trait_ref.args.type_at(1).kind()

`

``

2068

`+

&& let ty::Infer(ty::TyVar(vid)) = *pred.self_ty().skip_binder().kind()

`

``

2069

`+

&& self.fcx.type_var_is_sized(vid)

`

``

2070

`+

{

`

``

2071

`+

ControlFlow::Continue(())

`

``

2072

`+

} else if let Some(cand) = goal.unique_applicable_candidate()

`

``

2073

`+

&& cand.shallow_certainty() == Certainty::Yes

`

``

2074

`+

{

`

``

2075

`+

cand.visit_nested_no_probe(self)

`

``

2076

`+

} else {

`

``

2077

`+

ControlFlow::Break(())

`

``

2078

`+

}

`

``

2079

`+

}

`

``

2080

`+

}

`

``

2081

`+

}

`

``

2082

`+

}

`