Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/expr.rs')
| -rw-r--r-- | crates/hir-ty/src/infer/expr.rs | 1576 |
1 files changed, 832 insertions, 744 deletions
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index d43c99fc28..fd4e374d9c 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -1,15 +1,12 @@ //! Type inference for expressions. -use std::{ - iter::{repeat, repeat_with}, - mem, -}; +use std::{iter::repeat_with, mem}; -use chalk_ir::{DebruijnIndex, Mutability, TyVariableKind, cast::Cast}; use either::Either; +use hir_def::hir::ClosureKind; use hir_def::{ BlockId, FieldId, GenericDefId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId, - expr_store::path::{GenericArg, GenericArgs, Path}, + expr_store::path::{GenericArg as HirGenericArg, GenericArgs as HirGenericArgs, Path}, hir::{ ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, Expr, ExprId, ExprOrPatId, LabelId, Literal, Pat, PatId, Statement, UnaryOp, generics::GenericParamDataRef, @@ -19,37 +16,48 @@ use hir_def::{ }; use hir_expand::name::Name; use intern::sym; -use stdx::always; +use rustc_ast_ir::Mutability; +use rustc_type_ir::{ + CoroutineArgs, CoroutineArgsParts, InferTy, Interner, + inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Ty as _}, +}; use syntax::ast::RangeOp; +use tracing::debug; use crate::{ - Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, - DeclOrigin, IncorrectGenericsLenKind, Interner, Rawness, Scalar, Substitution, - TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, - autoderef::{Autoderef, builtin_deref, deref_by_trait}, + Adjust, Adjustment, AutoBorrow, CallableDefId, DeclContext, DeclOrigin, + IncorrectGenericsLenKind, Rawness, TraitEnvironment, + autoderef::overloaded_deref_ty, consteval, + db::InternedCoroutine, generics::generics, infer::{ - BreakableKind, - coerce::{CoerceMany, CoerceNever, CoercionCause}, + AllowTwoPhase, BreakableKind, + coerce::{CoerceMany, CoerceNever}, find_continuable, pat::contains_explicit_ref_binding, }, lang_items::lang_items_for_bin_op, lower::{ - LifetimeElisionKind, ParamLoweringMode, lower_to_chalk_mutability, + LifetimeElisionKind, lower_mutability, path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, }, - mapping::{ToChalk, from_chalk}, method_resolution::{self, VisibleFromModule}, - primitive::{self, UintTy}, - static_lifetime, to_chalk_trait_id, + next_solver::{ + Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, TraitRef, Ty, TyKind, + TypeError, + infer::{ + InferOk, + traits::{Obligation, ObligationCause}, + }, + obligation_ctxt::ObligationCtxt, + }, traits::FnTrait, }; use super::{ BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, - cast::CastCheck, coerce::auto_deref_adjust_steps, find_breakable, + cast::CastCheck, find_breakable, }; #[derive(Clone, Copy, PartialEq, Eq)] @@ -58,27 +66,30 @@ pub(crate) enum ExprIsRead { No, } -impl InferenceContext<'_> { +impl<'db> InferenceContext<'_, 'db> { pub(crate) fn infer_expr( &mut self, tgt_expr: ExprId, - expected: &Expectation, + expected: &Expectation<'db>, is_read: ExprIsRead, - ) -> Ty { + ) -> Ty<'db> { let ty = self.infer_expr_inner(tgt_expr, expected, is_read); if let Some(expected_ty) = expected.only_has_type(&mut self.table) { - let could_unify = self.unify(&ty, &expected_ty); + let could_unify = self.unify(ty, expected_ty); if !could_unify { - self.result.type_mismatches.insert( - tgt_expr.into(), - TypeMismatch { expected: expected_ty, actual: ty.clone() }, - ); + self.result + .type_mismatches + .insert(tgt_expr.into(), TypeMismatch { expected: expected_ty, actual: ty }); } } ty } - pub(crate) fn infer_expr_no_expect(&mut self, tgt_expr: ExprId, is_read: ExprIsRead) -> Ty { + pub(crate) fn infer_expr_no_expect( + &mut self, + tgt_expr: ExprId, + is_read: ExprIsRead, + ) -> Ty<'db> { self.infer_expr_inner(tgt_expr, &Expectation::None, is_read) } @@ -87,9 +98,9 @@ impl InferenceContext<'_> { pub(super) fn infer_expr_coerce( &mut self, expr: ExprId, - expected: &Expectation, + expected: &Expectation<'db>, is_read: ExprIsRead, - ) -> Ty { + ) -> Ty<'db> { let ty = self.infer_expr_inner(expr, expected, is_read); if let Some(target) = expected.only_has_type(&mut self.table) { let coerce_never = if self.expr_guaranteed_to_constitute_read_for_never(expr, is_read) { @@ -97,13 +108,12 @@ impl InferenceContext<'_> { } else { CoerceNever::No }; - match self.coerce(Some(expr), &ty, &target, coerce_never) { + match self.coerce(expr.into(), ty, target, AllowTwoPhase::No, coerce_never) { Ok(res) => res, Err(_) => { - self.result.type_mismatches.insert( - expr.into(), - TypeMismatch { expected: target.clone(), actual: ty.clone() }, - ); + self.result + .type_mismatches + .insert(expr.into(), TypeMismatch { expected: target, actual: ty }); target } } @@ -243,56 +253,58 @@ impl InferenceContext<'_> { fn infer_expr_coerce_never( &mut self, expr: ExprId, - expected: &Expectation, + expected: &Expectation<'db>, is_read: ExprIsRead, - ) -> Ty { + ) -> Ty<'db> { let ty = self.infer_expr_inner(expr, expected, is_read); // While we don't allow *arbitrary* coercions here, we *do* allow // coercions from `!` to `expected`. if ty.is_never() { if let Some(adjustments) = self.result.expr_adjustments.get(&expr) { return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments { - target.clone() + *target } else { self.err_ty() }; } if let Some(target) = expected.only_has_type(&mut self.table) { - self.coerce(Some(expr), &ty, &target, CoerceNever::Yes) + self.coerce(expr.into(), ty, target, AllowTwoPhase::No, CoerceNever::Yes) .expect("never-to-any coercion should always succeed") } else { ty } } else { if let Some(expected_ty) = expected.only_has_type(&mut self.table) { - let could_unify = self.unify(&ty, &expected_ty); + let could_unify = self.unify(ty, expected_ty); if !could_unify { - self.result.type_mismatches.insert( - expr.into(), - TypeMismatch { expected: expected_ty, actual: ty.clone() }, - ); + self.result + .type_mismatches + .insert(expr.into(), TypeMismatch { expected: expected_ty, actual: ty }); } } ty } } + #[tracing::instrument(level = "debug", skip(self, is_read), ret)] fn infer_expr_inner( &mut self, tgt_expr: ExprId, - expected: &Expectation, + expected: &Expectation<'db>, is_read: ExprIsRead, - ) -> Ty { + ) -> Ty<'db> { self.db.unwind_if_revision_cancelled(); - let ty = match &self.body[tgt_expr] { + let expr = &self.body[tgt_expr]; + tracing::trace!(?expr); + let ty = match expr { Expr::Missing => self.err_ty(), &Expr::If { condition, then_branch, else_branch } => { let expected = &expected.adjust_for_branches(&mut self.table); self.infer_expr_coerce_never( condition, - &Expectation::HasType(self.result.standard_types.bool_.clone()), + &Expectation::HasType(self.types.bool), ExprIsRead::Yes, ); @@ -300,22 +312,24 @@ impl InferenceContext<'_> { let then_ty = self.infer_expr_inner(then_branch, expected, ExprIsRead::Yes); let then_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); - let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table)); - coerce.coerce(self, Some(then_branch), &then_ty, CoercionCause::Expr(then_branch)); + let mut coercion_sites = [then_branch, tgt_expr]; + if let Some(else_branch) = else_branch { + coercion_sites[1] = else_branch; + } + let mut coerce = CoerceMany::with_coercion_sites( + expected.coercion_target_type(&mut self.table), + &coercion_sites, + ); + coerce.coerce(self, &ObligationCause::new(), then_branch, then_ty); match else_branch { Some(else_branch) => { let else_ty = self.infer_expr_inner(else_branch, expected, ExprIsRead::Yes); let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); - coerce.coerce( - self, - Some(else_branch), - &else_ty, - CoercionCause::Expr(else_branch), - ); + coerce.coerce(self, &ObligationCause::new(), else_branch, else_ty); self.diverges = condition_diverges | then_diverges & else_diverges; } None => { - coerce.coerce_forced_unit(self, CoercionCause::Expr(tgt_expr)); + coerce.coerce_forced_unit(self, tgt_expr, &ObligationCause::new(), true); self.diverges = condition_diverges; } } @@ -331,10 +345,10 @@ impl InferenceContext<'_> { let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read); self.infer_top_pat( pat, - &input_ty, + input_ty, Some(DeclContext { origin: DeclOrigin::LetExpr }), ); - self.result.standard_types.bool_.clone() + self.types.bool } Expr::Block { statements, tail, label, id } => { self.infer_block(tgt_expr, *id, statements, *tail, *label, expected) @@ -354,12 +368,12 @@ impl InferenceContext<'_> { &Expr::Loop { body, label } => { // FIXME: should be: // let ty = expected.coercion_target_type(&mut self.table); - let ty = self.table.new_type_var(); + let ty = self.table.next_ty_var(); let (breaks, ()) = self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| { this.infer_expr( body, - &Expectation::HasType(TyBuilder::unit()), + &Expectation::HasType(this.types.unit), ExprIsRead::Yes, ); }); @@ -369,11 +383,19 @@ impl InferenceContext<'_> { self.diverges = Diverges::Maybe; breaks } - None => self.result.standard_types.never.clone(), + None => self.types.never, } } Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => self - .infer_closure(body, args, ret_type, arg_types, *closure_kind, tgt_expr, expected), + .infer_closure( + *body, + args, + *ret_type, + arg_types, + *closure_kind, + tgt_expr, + expected, + ), Expr::Call { callee, args, .. } => self.infer_call(tgt_expr, *callee, args, expected), Expr::MethodCall { receiver, args, method_name, generic_args } => self .infer_method_call( @@ -394,23 +416,21 @@ impl InferenceContext<'_> { if arms.is_empty() { self.diverges = Diverges::Always; - self.result.standard_types.never.clone() + self.types.never } else { let matchee_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let mut all_arms_diverge = Diverges::Always; for arm in arms.iter() { - let input_ty = self.resolve_ty_shallow(&input_ty); - self.infer_top_pat(arm.pat, &input_ty, None); + let input_ty = self.table.structurally_resolve_type(input_ty); + self.infer_top_pat(arm.pat, input_ty, None); } let expected = expected.adjust_for_branches(&mut self.table); let result_ty = match &expected { // We don't coerce to `()` so that if the match expression is a // statement it's branches can have any consistent type. - Expectation::HasType(ty) if *ty != self.result.standard_types.unit => { - ty.clone() - } - _ => self.table.new_type_var(), + Expectation::HasType(ty) if *ty != self.types.unit => *ty, + _ => self.table.next_ty_var(), }; let mut coerce = CoerceMany::new(result_ty); @@ -419,7 +439,7 @@ impl InferenceContext<'_> { self.diverges = Diverges::Maybe; self.infer_expr_coerce_never( guard_expr, - &Expectation::HasType(self.result.standard_types.bool_.clone()), + &Expectation::HasType(self.types.bool), ExprIsRead::Yes, ); } @@ -427,7 +447,7 @@ impl InferenceContext<'_> { let arm_ty = self.infer_expr_inner(arm.expr, &expected, ExprIsRead::Yes); all_arms_diverge &= self.diverges; - coerce.coerce(self, Some(arm.expr), &arm_ty, CoercionCause::Expr(arm.expr)); + coerce.coerce(self, &ObligationCause::new(), arm.expr, arm_ty); } self.diverges = matchee_diverges | all_arms_diverge; @@ -444,7 +464,7 @@ impl InferenceContext<'_> { bad_value_break: false, }); }; - self.result.standard_types.never.clone() + self.types.never } &Expr::Break { expr, label } => { let val_ty = if let Some(expr) = expr { @@ -468,17 +488,18 @@ impl InferenceContext<'_> { ExprIsRead::Yes, ) } else { - TyBuilder::unit() + self.types.unit }; match find_breakable(&mut self.breakables, label) { Some(ctxt) => match ctxt.coerce.take() { Some(mut coerce) => { - let cause = match expr { - Some(expr) => CoercionCause::Expr(expr), - None => CoercionCause::Expr(tgt_expr), - }; - coerce.coerce(self, expr, &val_ty, cause); + coerce.coerce( + self, + &ObligationCause::new(), + expr.unwrap_or(tgt_expr), + val_ty, + ); // Avoiding borrowck let ctxt = find_breakable(&mut self.breakables, label) @@ -496,12 +517,12 @@ impl InferenceContext<'_> { }); } } - self.result.standard_types.never.clone() + self.types.never } &Expr::Return { expr } => self.infer_expr_return(tgt_expr, expr), &Expr::Become { expr } => self.infer_expr_become(expr), Expr::Yield { expr } => { - if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() { + if let Some((resume_ty, yield_ty)) = self.resume_yield_tys { if let Some(expr) = expr { self.infer_expr_coerce( *expr, @@ -509,32 +530,35 @@ impl InferenceContext<'_> { ExprIsRead::Yes, ); } else { - let unit = self.result.standard_types.unit.clone(); - let _ = self.coerce(Some(tgt_expr), &unit, &yield_ty, CoerceNever::Yes); + let unit = self.types.unit; + let _ = self.coerce( + tgt_expr.into(), + unit, + yield_ty, + AllowTwoPhase::No, + CoerceNever::Yes, + ); } resume_ty } else { // FIXME: report error (yield expr in non-coroutine) - self.result.standard_types.unknown.clone() + self.types.error } } Expr::Yeet { expr } => { if let &Some(expr) = expr { self.infer_expr_no_expect(expr, ExprIsRead::Yes); } - self.result.standard_types.never.clone() + self.types.never } Expr::RecordLit { path, fields, spread, .. } => { let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path.as_deref(), false); if let Some(t) = expected.only_has_type(&mut self.table) { - self.unify(&ty, &t); + self.unify(ty, t); } - let substs = ty - .as_adt() - .map(|(_, s)| s.clone()) - .unwrap_or_else(|| Substitution::empty(Interner)); + let substs = ty.as_adt().map(|(_, s)| s).unwrap_or(self.types.empty_args); if let Some(variant) = def_id { self.write_variant_resolution(tgt_expr.into(), variant); } @@ -572,7 +596,7 @@ impl InferenceContext<'_> { } }; let field_ty = field_def.map_or(self.err_ty(), |it| { - field_types[it].clone().substitute(Interner, &substs) + field_types[it].instantiate(self.interner(), &substs) }); // Field type might have some unknown types @@ -593,7 +617,7 @@ impl InferenceContext<'_> { } } if let Some(expr) = spread { - self.infer_expr(*expr, &Expectation::has_type(ty.clone()), ExprIsRead::Yes); + self.infer_expr(*expr, &Expectation::has_type(ty), ExprIsRead::Yes); } ty } @@ -604,21 +628,13 @@ impl InferenceContext<'_> { } Expr::Cast { expr, type_ref } => { let cast_ty = self.make_body_ty(*type_ref); - let expr_ty = self.infer_expr( - *expr, - &Expectation::Castable(cast_ty.clone()), - ExprIsRead::Yes, - ); - self.deferred_cast_checks.push(CastCheck::new( - tgt_expr, - *expr, - expr_ty, - cast_ty.clone(), - )); + let expr_ty = + self.infer_expr(*expr, &Expectation::Castable(cast_ty), ExprIsRead::Yes); + self.deferred_cast_checks.push(CastCheck::new(tgt_expr, *expr, expr_ty, cast_ty)); cast_ty } Expr::Ref { expr, rawness, mutability } => { - let mutability = lower_to_chalk_mutability(*mutability); + let mutability = lower_mutability(*mutability); let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = expected .only_has_type(&mut self.table) .as_ref() @@ -632,66 +648,65 @@ impl InferenceContext<'_> { // FIXME: record type error - expected reference but found ptr, // which cannot be coerced } - Expectation::rvalue_hint(self, Ty::clone(exp_inner)) + Expectation::rvalue_hint(self, exp_inner) } else { Expectation::none() }; let inner_ty = self.infer_expr_inner(*expr, &expectation, ExprIsRead::Yes); match rawness { - Rawness::RawPtr => TyKind::Raw(mutability, inner_ty), + Rawness::RawPtr => Ty::new_ptr(self.interner(), inner_ty, mutability), Rawness::Ref => { - let lt = self.table.new_lifetime_var(); - TyKind::Ref(mutability, lt, inner_ty) + let lt = self.table.next_region_var(); + Ty::new_ref(self.interner(), lt, inner_ty, mutability) } } - .intern(Interner) } &Expr::Box { expr } => self.infer_expr_box(expr, expected), Expr::UnaryOp { expr, op } => { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes); - let inner_ty = self.resolve_ty_shallow(&inner_ty); + let inner_ty = self.table.try_structurally_resolve_type(inner_ty); // FIXME: Note down method resolution her match op { UnaryOp::Deref => { - if let Some(deref_trait) = self.resolve_lang_trait(LangItem::Deref) { - if let Some(deref_fn) = deref_trait + if let Some(deref_trait) = self.resolve_lang_trait(LangItem::Deref) + && let Some(deref_fn) = deref_trait .trait_items(self.db) .method_by_name(&Name::new_symbol_root(sym::deref)) - { - // FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that - // the mutability is not wrong, and will be fixed in `self.infer_mut`). - self.write_method_resolution( - tgt_expr, - deref_fn, - Substitution::empty(Interner), - ); - } + { + // FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that + // the mutability is not wrong, and will be fixed in `self.infer_mut`). + self.write_method_resolution(tgt_expr, deref_fn, self.types.empty_args); } - if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) { - self.resolve_ty_shallow(derefed) + if let Some(derefed) = inner_ty.builtin_deref(self.db, true) { + self.table.try_structurally_resolve_type(derefed) } else { - deref_by_trait(&mut self.table, inner_ty, false) - .unwrap_or_else(|| self.err_ty()) + let infer_ok = overloaded_deref_ty(&self.table, inner_ty); + match infer_ok { + Some(infer_ok) => self.table.register_infer_ok(infer_ok), + None => self.err_ty(), + } } } UnaryOp::Neg => { - match inner_ty.kind(Interner) { + match inner_ty.kind() { // Fast path for builtins - TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_)) - | TyKind::InferenceVar( - _, - TyVariableKind::Integer | TyVariableKind::Float, - ) => inner_ty, + TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => inner_ty, // Otherwise we resolve via the std::ops::Neg trait _ => self .resolve_associated_type(inner_ty, self.resolve_ops_neg_output()), } } UnaryOp::Not => { - match inner_ty.kind(Interner) { + match inner_ty.kind() { // Fast path for builtins - TyKind::Scalar(Scalar::Bool | Scalar::Int(_) | Scalar::Uint(_)) - | TyKind::InferenceVar(_, TyVariableKind::Integer) => inner_ty, + TyKind::Bool + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => inner_ty, // Otherwise we resolve via the std::ops::Not trait _ => self .resolve_associated_type(inner_ty, self.resolve_ops_not_output()), @@ -701,18 +716,10 @@ impl InferenceContext<'_> { } Expr::BinaryOp { lhs, rhs, op } => match op { Some(BinaryOp::LogicOp(_)) => { - let bool_ty = self.result.standard_types.bool_.clone(); - self.infer_expr_coerce( - *lhs, - &Expectation::HasType(bool_ty.clone()), - ExprIsRead::Yes, - ); + let bool_ty = self.types.bool; + self.infer_expr_coerce(*lhs, &Expectation::HasType(bool_ty), ExprIsRead::Yes); let lhs_diverges = self.diverges; - self.infer_expr_coerce( - *rhs, - &Expectation::HasType(bool_ty.clone()), - ExprIsRead::Yes, - ); + self.infer_expr_coerce(*rhs, &Expectation::HasType(bool_ty), ExprIsRead::Yes); // Depending on the LHS' value, the RHS can never execute. self.diverges = lhs_diverges; bool_ty @@ -759,14 +766,14 @@ impl InferenceContext<'_> { let is_destructuring_assignment = lhs_ty.is_none(); if let Some(lhs_ty) = lhs_ty { - self.write_pat_ty(target, lhs_ty.clone()); + self.write_pat_ty(target, lhs_ty); self.infer_expr_coerce(value, &Expectation::has_type(lhs_ty), ExprIsRead::No); } else { let rhs_ty = self.infer_expr(value, &Expectation::none(), ExprIsRead::Yes); let resolver_guard = self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); self.inside_assignment = true; - self.infer_top_pat(target, &rhs_ty, None); + self.infer_top_pat(target, rhs_ty, None); self.inside_assignment = false; self.resolver.reset_to_guard(resolver_guard); } @@ -777,43 +784,49 @@ impl InferenceContext<'_> { // assignments into blocks. self.table.new_maybe_never_var() } else { - self.result.standard_types.unit.clone() + self.types.unit } } Expr::Range { lhs, rhs, range_type } => { let lhs_ty = lhs.map(|e| self.infer_expr_inner(e, &Expectation::none(), ExprIsRead::Yes)); - let rhs_expect = lhs_ty - .as_ref() - .map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone())); + let rhs_expect = + lhs_ty.as_ref().map_or_else(Expectation::none, |ty| Expectation::has_type(*ty)); let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect, ExprIsRead::Yes)); + let single_arg_adt = |adt, ty: Ty<'db>| { + Ty::new_adt( + self.interner(), + adt, + GenericArgs::new_from_iter(self.interner(), [ty.into()]), + ) + }; match (range_type, lhs_ty, rhs_ty) { (RangeOp::Exclusive, None, None) => match self.resolve_range_full() { - Some(adt) => TyBuilder::adt(self.db, adt).build(), + Some(adt) => Ty::new_adt(self.interner(), adt, self.types.empty_args), None => self.err_ty(), }, (RangeOp::Exclusive, None, Some(ty)) => match self.resolve_range_to() { - Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(), + Some(adt) => single_arg_adt(adt, ty), None => self.err_ty(), }, (RangeOp::Inclusive, None, Some(ty)) => { match self.resolve_range_to_inclusive() { - Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(), + Some(adt) => single_arg_adt(adt, ty), None => self.err_ty(), } } (RangeOp::Exclusive, Some(_), Some(ty)) => match self.resolve_range() { - Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(), + Some(adt) => single_arg_adt(adt, ty), None => self.err_ty(), }, (RangeOp::Inclusive, Some(_), Some(ty)) => { match self.resolve_range_inclusive() { - Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(), + Some(adt) => single_arg_adt(adt, ty), None => self.err_ty(), } } (RangeOp::Exclusive, Some(ty), None) => match self.resolve_range_from() { - Some(adt) => TyBuilder::adt(self.db, adt).push(ty).build(), + Some(adt) => single_arg_adt(adt, ty), None => self.err_ty(), }, (RangeOp::Inclusive, _, None) => self.err_ty(), @@ -824,10 +837,9 @@ impl InferenceContext<'_> { let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes); if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) { - let canonicalized = self.canonicalize(base_ty.clone()); + let canonicalized = self.canonicalize(base_ty); let receiver_adjustments = method_resolution::resolve_indexing_op( - self.db, - self.table.trait_env.clone(), + &mut self.table, canonicalized, index_trait, ); @@ -838,66 +850,52 @@ impl InferenceContext<'_> { // mutability will be fixed up in `InferenceContext::infer_mut`; adj.push(Adjustment::borrow( + self.interner(), Mutability::Not, - self_ty.clone(), - self.table.new_lifetime_var(), + self_ty, + self.table.next_region_var(), )); self.write_expr_adj(*base, adj.into_boxed_slice()); if let Some(func) = index_trait .trait_items(self.db) .method_by_name(&Name::new_symbol_root(sym::index)) { - let subst = TyBuilder::subst_for_def(self.db, index_trait, None); - if subst.remaining() != 2 { - return self.err_ty(); - } - let subst = subst.push(self_ty.clone()).push(index_ty.clone()).build(); + let subst = GenericArgs::new_from_iter( + self.interner(), + [self_ty.into(), index_ty.into()], + ); self.write_method_resolution(tgt_expr, func, subst); } let assoc = self.resolve_ops_index_output(); - self.resolve_associated_type_with_params( - self_ty, - assoc, - &[index_ty.cast(Interner)], - ) + self.resolve_associated_type_with_params(self_ty, assoc, &[index_ty.into()]) } else { self.err_ty() } } Expr::Tuple { exprs, .. } => { - let mut tys = match expected - .only_has_type(&mut self.table) - .as_ref() - .map(|t| t.kind(Interner)) - { - Some(TyKind::Tuple(_, substs)) => substs - .iter(Interner) - .map(|a| a.assert_ty_ref(Interner).clone()) - .chain(repeat_with(|| self.table.new_type_var())) - .take(exprs.len()) - .collect::<Vec<_>>(), - _ => (0..exprs.len()).map(|_| self.table.new_type_var()).collect(), - }; + let mut tys = + match expected.only_has_type(&mut self.table).as_ref().map(|t| t.kind()) { + Some(TyKind::Tuple(substs)) => substs + .iter() + .chain(repeat_with(|| self.table.next_ty_var())) + .take(exprs.len()) + .collect::<Vec<_>>(), + _ => (0..exprs.len()).map(|_| self.table.next_ty_var()).collect(), + }; for (expr, ty) in exprs.iter().zip(tys.iter_mut()) { - *ty = self.infer_expr_coerce( - *expr, - &Expectation::has_type(ty.clone()), - ExprIsRead::Yes, - ); + *ty = + self.infer_expr_coerce(*expr, &Expectation::has_type(*ty), ExprIsRead::Yes); } - TyKind::Tuple(tys.len(), Substitution::from_iter(Interner, tys)).intern(Interner) + Ty::new_tup(self.interner(), &tys) } Expr::Array(array) => self.infer_expr_array(array, expected), Expr::Literal(lit) => match lit { - Literal::Bool(..) => self.result.standard_types.bool_.clone(), - Literal::String(..) => { - TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(Interner)) - .intern(Interner) - } + Literal::Bool(..) => self.types.bool, + Literal::String(..) => self.types.static_str_ref, Literal::ByteString(bs) => { - let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner); + let byte_type = self.types.u8; let len = consteval::usize_const( self.db, @@ -905,74 +903,80 @@ impl InferenceContext<'_> { self.resolver.krate(), ); - let array_type = TyKind::Array(byte_type, len).intern(Interner); - TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(Interner) + let array_type = Ty::new_array_with_const_len(self.interner(), byte_type, len); + Ty::new_ref(self.interner(), self.types.re_static, array_type, Mutability::Not) } - Literal::CString(..) => TyKind::Ref( - Mutability::Not, - static_lifetime(), + Literal::CString(..) => Ty::new_ref( + self.interner(), + self.types.re_static, self.resolve_lang_item(LangItem::CStr) .and_then(LangItemTarget::as_struct) .map_or_else( || self.err_ty(), |strukt| { - TyKind::Adt(AdtId(strukt.into()), Substitution::empty(Interner)) - .intern(Interner) + Ty::new_adt(self.interner(), strukt.into(), self.types.empty_args) }, ), - ) - .intern(Interner), - Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(Interner), + Mutability::Not, + ), + Literal::Char(..) => self.types.char, Literal::Int(_v, ty) => match ty { - Some(int_ty) => { - TyKind::Scalar(Scalar::Int(primitive::int_ty_from_builtin(*int_ty))) - .intern(Interner) - } + Some(int_ty) => match int_ty { + hir_def::builtin_type::BuiltinInt::Isize => self.types.isize, + hir_def::builtin_type::BuiltinInt::I8 => self.types.i8, + hir_def::builtin_type::BuiltinInt::I16 => self.types.i16, + hir_def::builtin_type::BuiltinInt::I32 => self.types.i32, + hir_def::builtin_type::BuiltinInt::I64 => self.types.i64, + hir_def::builtin_type::BuiltinInt::I128 => self.types.i128, + }, None => { let expected_ty = expected.to_option(&mut self.table); - let opt_ty = match expected_ty.as_ref().map(|it| it.kind(Interner)) { - Some(TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))) => expected_ty, - Some(TyKind::Scalar(Scalar::Char)) => { - Some(TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner)) - } - Some(TyKind::Raw(..) | TyKind::FnDef(..) | TyKind::Function(..)) => { - Some(TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner)) + tracing::debug!(?expected_ty); + let opt_ty = match expected_ty.as_ref().map(|it| it.kind()) { + Some(TyKind::Int(_) | TyKind::Uint(_)) => expected_ty, + Some(TyKind::Char) => Some(self.types.u8), + Some(TyKind::RawPtr(..) | TyKind::FnDef(..) | TyKind::FnPtr(..)) => { + Some(self.types.usize) } _ => None, }; - opt_ty.unwrap_or_else(|| self.table.new_integer_var()) + opt_ty.unwrap_or_else(|| self.table.next_int_var()) } }, Literal::Uint(_v, ty) => match ty { - Some(int_ty) => { - TyKind::Scalar(Scalar::Uint(primitive::uint_ty_from_builtin(*int_ty))) - .intern(Interner) - } + Some(int_ty) => match int_ty { + hir_def::builtin_type::BuiltinUint::Usize => self.types.usize, + hir_def::builtin_type::BuiltinUint::U8 => self.types.u8, + hir_def::builtin_type::BuiltinUint::U16 => self.types.u16, + hir_def::builtin_type::BuiltinUint::U32 => self.types.u32, + hir_def::builtin_type::BuiltinUint::U64 => self.types.u64, + hir_def::builtin_type::BuiltinUint::U128 => self.types.u128, + }, None => { let expected_ty = expected.to_option(&mut self.table); - let opt_ty = match expected_ty.as_ref().map(|it| it.kind(Interner)) { - Some(TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))) => expected_ty, - Some(TyKind::Scalar(Scalar::Char)) => { - Some(TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner)) - } - Some(TyKind::Raw(..) | TyKind::FnDef(..) | TyKind::Function(..)) => { - Some(TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner)) + let opt_ty = match expected_ty.as_ref().map(|it| it.kind()) { + Some(TyKind::Int(_) | TyKind::Uint(_)) => expected_ty, + Some(TyKind::Char) => Some(self.types.u8), + Some(TyKind::RawPtr(..) | TyKind::FnDef(..) | TyKind::FnPtr(..)) => { + Some(self.types.usize) } _ => None, }; - opt_ty.unwrap_or_else(|| self.table.new_integer_var()) + opt_ty.unwrap_or_else(|| self.table.next_int_var()) } }, Literal::Float(_v, ty) => match ty { - Some(float_ty) => { - TyKind::Scalar(Scalar::Float(primitive::float_ty_from_builtin(*float_ty))) - .intern(Interner) - } + Some(float_ty) => match float_ty { + hir_def::builtin_type::BuiltinFloat::F16 => self.types.f16, + hir_def::builtin_type::BuiltinFloat::F32 => self.types.f32, + hir_def::builtin_type::BuiltinFloat::F64 => self.types.f64, + hir_def::builtin_type::BuiltinFloat::F128 => self.types.f128, + }, None => { - let opt_ty = expected.to_option(&mut self.table).filter(|ty| { - matches!(ty.kind(Interner), TyKind::Scalar(Scalar::Float(_))) - }); - opt_ty.unwrap_or_else(|| self.table.new_float_var()) + let opt_ty = expected + .to_option(&mut self.table) + .filter(|ty| matches!(ty.kind(), TyKind::Float(_))); + opt_ty.unwrap_or_else(|| self.table.next_float_var()) } }, }, @@ -980,13 +984,10 @@ impl InferenceContext<'_> { // Underscore expression is an error, we render a specialized diagnostic // to let the user know what type is expected though. let expected = expected.to_option(&mut self.table).unwrap_or_else(|| self.err_ty()); - self.push_diagnostic(InferenceDiagnostic::TypedHole { - expr: tgt_expr, - expected: expected.clone(), - }); + self.push_diagnostic(InferenceDiagnostic::TypedHole { expr: tgt_expr, expected }); expected } - Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner), + Expr::OffsetOf(_) => self.types.usize, Expr::InlineAsm(asm) => { let check_expr_asm_operand = |this: &mut Self, expr, is_input: bool| { let ty = this.infer_expr_no_expect(expr, ExprIsRead::Yes); @@ -999,18 +1000,32 @@ impl InferenceContext<'_> { // allows them to be inferred based on how they are used later in the // function. if is_input { - let ty = this.resolve_ty_shallow(&ty); - match ty.kind(Interner) { + let ty = this.table.structurally_resolve_type(ty); + match ty.kind() { TyKind::FnDef(def, parameters) => { - let fnptr_ty = TyKind::Function( - CallableSig::from_def(this.db, *def, parameters).to_fn_ptr(), - ) - .intern(Interner); - _ = this.coerce(Some(expr), &ty, &fnptr_ty, CoerceNever::Yes); + let fnptr_ty = Ty::new_fn_ptr( + this.interner(), + this.interner() + .fn_sig(def) + .instantiate(this.interner(), parameters), + ); + _ = this.coerce( + expr.into(), + ty, + fnptr_ty, + AllowTwoPhase::No, + CoerceNever::Yes, + ); } - TyKind::Ref(mutbl, _, base_ty) => { - let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner); - _ = this.coerce(Some(expr), &ty, &ptr_ty, CoerceNever::Yes); + TyKind::Ref(_, base_ty, mutbl) => { + let ptr_ty = Ty::new_ptr(this.interner(), base_ty, mutbl); + _ = this.coerce( + expr.into(), + ty, + ptr_ty, + AllowTwoPhase::No, + CoerceNever::Yes, + ); } _ => {} } @@ -1033,7 +1048,7 @@ impl InferenceContext<'_> { AsmOperand::Label(expr) => { self.infer_expr( expr, - &Expectation::HasType(self.result.standard_types.unit.clone()), + &Expectation::HasType(self.types.unit), ExprIsRead::No, ); } @@ -1043,17 +1058,13 @@ impl InferenceContext<'_> { // FIXME: `sym` should report for things that are not functions or statics. AsmOperand::Sym(_) => (), }); - if diverge { - self.result.standard_types.never.clone() - } else { - self.result.standard_types.unit.clone() - } + if diverge { self.types.never } else { self.types.unit } } }; // use a new type variable if we got unknown here let ty = self.insert_type_vars_shallow(ty); - self.write_expr_ty(tgt_expr, ty.clone()); - if self.resolve_ty_shallow(&ty).is_never() + self.write_expr_ty(tgt_expr, ty); + if self.shallow_resolve(ty).is_never() && self.expr_guaranteed_to_constitute_read_for_never(tgt_expr, is_read) { // Any expression that produces a value of type `!` must have diverged @@ -1062,7 +1073,7 @@ impl InferenceContext<'_> { ty } - fn infer_expr_path(&mut self, path: &Path, id: ExprOrPatId, scope_id: ExprId) -> Ty { + fn infer_expr_path(&mut self, path: &Path, id: ExprOrPatId, scope_id: ExprId) -> Ty<'db> { let g = self.resolver.update_to_inner_scope(self.db, self.owner, scope_id); let ty = match self.infer_path(path, id) { Some(ty) => ty, @@ -1084,24 +1095,24 @@ impl InferenceContext<'_> { id: &Option<BlockId>, statements: &[Statement], tail: &Option<ExprId>, - ) -> Ty { - let ret_ty = self.table.new_type_var(); + ) -> Ty<'db> { + let ret_ty = self.table.next_ty_var(); let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); - let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); - let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(ret_ty.clone())); + let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty); + let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(ret_ty)); // FIXME: We should handle async blocks like we handle closures let expected = &Expectation::has_type(ret_ty); let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { let ty = this.infer_block(tgt_expr, *id, statements, *tail, None, expected); if let Some(target) = expected.only_has_type(&mut this.table) { - match this.coerce(Some(tgt_expr), &ty, &target, CoerceNever::Yes) { + match this.coerce(tgt_expr.into(), ty, target, AllowTwoPhase::No, CoerceNever::Yes) + { Ok(res) => res, Err(_) => { - this.result.type_mismatches.insert( - tgt_expr.into(), - TypeMismatch { expected: target.clone(), actual: ty.clone() }, - ); + this.result + .type_mismatches + .insert(tgt_expr.into(), TypeMismatch { expected: target, actual: ty }); target } } @@ -1119,57 +1130,75 @@ impl InferenceContext<'_> { pub(crate) fn lower_async_block_type_impl_trait( &mut self, - inner_ty: Ty, + inner_ty: Ty<'db>, tgt_expr: ExprId, - ) -> Ty { - // Use the first type parameter as the output type of future. - // existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType> - let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr); - let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); - TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty)).intern(Interner) + ) -> Ty<'db> { + let coroutine_id = InternedCoroutine(self.owner, tgt_expr); + let coroutine_id = self.db.intern_coroutine(coroutine_id).into(); + let parent_args = GenericArgs::identity_for_item(self.interner(), self.generic_def.into()); + Ty::new_coroutine( + self.interner(), + coroutine_id, + CoroutineArgs::new( + self.interner(), + CoroutineArgsParts { + parent_args, + kind_ty: self.types.unit, + // rustc uses a special lang item type for the resume ty. I don't believe this can cause us problems. + resume_ty: self.types.unit, + yield_ty: self.types.unit, + return_ty: inner_ty, + // FIXME: Infer upvars. + tupled_upvars_ty: self.types.unit, + }, + ) + .args, + ) } pub(crate) fn write_fn_trait_method_resolution( &mut self, fn_x: FnTrait, - derefed_callee: &Ty, - adjustments: &mut Vec<Adjustment>, - callee_ty: &Ty, - params: &[Ty], + derefed_callee: Ty<'db>, + adjustments: &mut Vec<Adjustment<'db>>, + callee_ty: Ty<'db>, + params: &[Ty<'db>], tgt_expr: ExprId, ) { match fn_x { FnTrait::FnOnce | FnTrait::AsyncFnOnce => (), FnTrait::FnMut | FnTrait::AsyncFnMut => { - if let TyKind::Ref(Mutability::Mut, lt, inner) = derefed_callee.kind(Interner) { + if let TyKind::Ref(lt, inner, Mutability::Mut) = derefed_callee.kind() { if adjustments .last() .map(|it| matches!(it.kind, Adjust::Borrow(_))) .unwrap_or(true) { // prefer reborrow to move - adjustments - .push(Adjustment { kind: Adjust::Deref(None), target: inner.clone() }); + adjustments.push(Adjustment { kind: Adjust::Deref(None), target: inner }); adjustments.push(Adjustment::borrow( + self.interner(), Mutability::Mut, - inner.clone(), - lt.clone(), + inner, + lt, )) } } else { adjustments.push(Adjustment::borrow( + self.interner(), Mutability::Mut, - derefed_callee.clone(), - self.table.new_lifetime_var(), + derefed_callee, + self.table.next_region_var(), )); } } FnTrait::Fn | FnTrait::AsyncFn => { - if !matches!(derefed_callee.kind(Interner), TyKind::Ref(Mutability::Not, _, _)) { + if !matches!(derefed_callee.kind(), TyKind::Ref(_, _, Mutability::Not)) { adjustments.push(Adjustment::borrow( + self.interner(), Mutability::Not, - derefed_callee.clone(), - self.table.new_lifetime_var(), + derefed_callee, + self.table.next_region_var(), )); } } @@ -1179,36 +1208,35 @@ impl InferenceContext<'_> { }; let trait_data = trait_.trait_items(self.db); if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) { - let subst = TyBuilder::subst_for_def(self.db, trait_, None) - .push(callee_ty.clone()) - .push(TyBuilder::tuple_with(params.iter().cloned())) - .build(); + let subst = GenericArgs::new_from_iter( + self.interner(), + [ + callee_ty.into(), + Ty::new_tup_from_iter(self.interner(), params.iter().copied()).into(), + ], + ); self.write_method_resolution(tgt_expr, func, subst); } } - fn infer_expr_array( - &mut self, - array: &Array, - expected: &Expectation, - ) -> chalk_ir::Ty<Interner> { - let elem_ty = match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(Interner)) { - Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st.clone(), - _ => self.table.new_type_var(), + fn infer_expr_array(&mut self, array: &Array, expected: &Expectation<'db>) -> Ty<'db> { + let elem_ty = match expected.to_option(&mut self.table).as_ref().map(|t| t.kind()) { + Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st, + _ => self.table.next_ty_var(), }; let krate = self.resolver.krate(); - let expected = Expectation::has_type(elem_ty.clone()); + let expected = Expectation::has_type(elem_ty); let (elem_ty, len) = match array { Array::ElementList { elements, .. } if elements.is_empty() => { (elem_ty, consteval::usize_const(self.db, Some(0), krate)) } Array::ElementList { elements, .. } => { - let mut coerce = CoerceMany::new(elem_ty); + let mut coerce = CoerceMany::with_coercion_sites(elem_ty, elements); for &expr in elements.iter() { let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes); - coerce.coerce(self, Some(expr), &cur_elem_ty, CoercionCause::Expr(expr)); + coerce.coerce(self, &ObligationCause::new(), expr, cur_elem_ty); } ( coerce.complete(self), @@ -1218,10 +1246,10 @@ impl InferenceContext<'_> { &Array::Repeat { initializer, repeat } => { self.infer_expr_coerce( initializer, - &Expectation::has_type(elem_ty.clone()), + &Expectation::has_type(elem_ty), ExprIsRead::Yes, ); - let usize = TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner); + let usize = self.types.usize; match self.body[repeat] { Expr::Underscore => { self.write_expr_ty(repeat, usize); @@ -1229,20 +1257,12 @@ impl InferenceContext<'_> { _ => _ = self.infer_expr(repeat, &Expectation::HasType(usize), ExprIsRead::Yes), } - ( - elem_ty, - consteval::eval_to_const( - repeat, - ParamLoweringMode::Placeholder, - self, - DebruijnIndex::INNERMOST, - ), - ) + (elem_ty, consteval::eval_to_const(repeat, self)) } }; // Try to evaluate unevaluated constant, and insert variable if is not possible. let len = self.table.insert_const_vars_shallow(len); - TyKind::Array(elem_ty, len).intern(Interner) + Ty::new_array_with_const_len(self.interner(), elem_ty, len) } pub(super) fn infer_return(&mut self, expr: ExprId) { @@ -1254,18 +1274,18 @@ impl InferenceContext<'_> { let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes); let mut coerce_many = self.return_coercion.take().unwrap(); - coerce_many.coerce(self, Some(expr), &return_expr_ty, CoercionCause::Expr(expr)); + coerce_many.coerce(self, &ObligationCause::new(), expr, return_expr_ty); self.return_coercion = Some(coerce_many); } - fn infer_expr_return(&mut self, ret: ExprId, expr: Option<ExprId>) -> Ty { + fn infer_expr_return(&mut self, ret: ExprId, expr: Option<ExprId>) -> Ty<'db> { match self.return_coercion { Some(_) => { if let Some(expr) = expr { self.infer_return(expr); } else { let mut coerce = self.return_coercion.take().unwrap(); - coerce.coerce_forced_unit(self, CoercionCause::Expr(ret)); + coerce.coerce_forced_unit(self, ret, &ObligationCause::new(), true); self.return_coercion = Some(coerce); } } @@ -1276,23 +1296,20 @@ impl InferenceContext<'_> { } } } - self.result.standard_types.never.clone() + self.types.never } - fn infer_expr_become(&mut self, expr: ExprId) -> Ty { + fn infer_expr_become(&mut self, expr: ExprId) -> Ty<'db> { match &self.return_coercion { Some(return_coercion) => { let ret_ty = return_coercion.expected_ty(); - let call_expr_ty = self.infer_expr_inner( - expr, - &Expectation::HasType(ret_ty.clone()), - ExprIsRead::Yes, - ); + let call_expr_ty = + self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes); // NB: this should *not* coerce. // tail calls don't support any coercions except lifetimes ones (like `&'static u8 -> &'a u8`). - self.unify(&call_expr_ty, &ret_ty); + self.unify(call_expr_ty, ret_ty); } None => { // FIXME: diagnose `become` outside of functions @@ -1300,10 +1317,10 @@ impl InferenceContext<'_> { } } - self.result.standard_types.never.clone() + self.types.never } - fn infer_expr_box(&mut self, inner_expr: ExprId, expected: &Expectation) -> Ty { + fn infer_expr_box(&mut self, inner_expr: ExprId, expected: &Expectation<'db>) -> Ty<'db> { if let Some(box_id) = self.resolve_boxed_box() { let table = &mut self.table; let inner_exp = expected @@ -1312,16 +1329,22 @@ impl InferenceContext<'_> { .and_then(|e| e.as_adt()) .filter(|(e_adt, _)| e_adt == &box_id) .map(|(_, subts)| { - let g = subts.at(Interner, 0); - Expectation::rvalue_hint(self, Ty::clone(g.assert_ty_ref(Interner))) + let g = subts.type_at(0); + Expectation::rvalue_hint(self, g) }) .unwrap_or_else(Expectation::none); let inner_ty = self.infer_expr_inner(inner_expr, &inner_exp, ExprIsRead::Yes); - TyBuilder::adt(self.db, box_id) - .push(inner_ty) - .fill_with_defaults(self.db, || self.table.new_type_var()) - .build() + Ty::new_adt( + self.interner(), + box_id, + GenericArgs::fill_with_defaults( + self.interner(), + box_id.into(), + [inner_ty.into()], + |_, id, _| self.table.next_var_for_param(id), + ), + ) } else { self.err_ty() } @@ -1333,7 +1356,7 @@ impl InferenceContext<'_> { op: BinaryOp, rhs: ExprId, tgt_expr: ExprId, - ) -> Ty { + ) -> Ty<'db> { let lhs_expectation = Expectation::none(); let is_read = if matches!(op, BinaryOp::Assignment { .. }) { ExprIsRead::Yes @@ -1341,24 +1364,24 @@ impl InferenceContext<'_> { ExprIsRead::No }; let lhs_ty = self.infer_expr(lhs, &lhs_expectation, is_read); - let rhs_ty = self.table.new_type_var(); + let rhs_ty = self.table.next_ty_var(); let trait_func = lang_items_for_bin_op(op).and_then(|(name, lang_item)| { let trait_id = self.resolve_lang_item(lang_item)?.as_trait()?; let func = trait_id.trait_items(self.db).method_by_name(&name)?; Some((trait_id, func)) }); - let (trait_, func) = match trait_func { - Some(it) => it, + let func = match trait_func { + Some((_, it)) => it, None => { // HACK: `rhs_ty` is a general inference variable with no clue at all at this // point. Passing `lhs_ty` as both operands just to check if `lhs_ty` is a builtin // type applicable to `op`. - let ret_ty = if self.is_builtin_binop(&lhs_ty, &lhs_ty, op) { + let ret_ty = if self.is_builtin_binop(lhs_ty, lhs_ty, op) { // Assume both operands are builtin so we can continue inference. No guarantee // on the correctness, rustc would complain as necessary lang items don't seem // to exist anyway. - self.enforce_builtin_binop_types(&lhs_ty, &rhs_ty, op) + self.enforce_builtin_binop_types(lhs_ty, rhs_ty, op) } else { self.err_ty() }; @@ -1371,59 +1394,57 @@ impl InferenceContext<'_> { // HACK: We can use this substitution for the function because the function itself doesn't // have its own generic parameters. - let subst = TyBuilder::subst_for_def(self.db, trait_, None); - if subst.remaining() != 2 { - return Ty::new(Interner, TyKind::Error); - } - let subst = subst.push(lhs_ty.clone()).push(rhs_ty.clone()).build(); + let args = GenericArgs::new_from_iter(self.interner(), [lhs_ty.into(), rhs_ty.into()]); - self.write_method_resolution(tgt_expr, func, subst.clone()); + self.write_method_resolution(tgt_expr, func, args); - let method_ty = self.db.value_ty(func.into()).unwrap().substitute(Interner, &subst); - self.register_obligations_for_call(&method_ty); + let method_ty = self.db.value_ty(func.into()).unwrap().instantiate(self.interner(), args); + self.register_obligations_for_call(method_ty); - self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty.clone()), ExprIsRead::Yes); + self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty), ExprIsRead::Yes); - let ret_ty = match method_ty.callable_sig(self.db) { + let ret_ty = match method_ty.callable_sig(self.interner()) { Some(sig) => { - let p_left = &sig.params()[0]; - if matches!(op, BinaryOp::CmpOp(..) | BinaryOp::Assignment { .. }) { - if let TyKind::Ref(mtbl, lt, _) = p_left.kind(Interner) { - self.write_expr_adj( - lhs, - Box::new([Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), *mtbl)), - target: p_left.clone(), - }]), - ); - } + let sig = sig.skip_binder(); + let p_left = sig.inputs_and_output.as_slice()[0]; + if matches!(op, BinaryOp::CmpOp(..) | BinaryOp::Assignment { .. }) + && let TyKind::Ref(lt, _, mtbl) = p_left.kind() + { + self.write_expr_adj( + lhs, + Box::new([Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(lt, mtbl)), + target: p_left, + }]), + ); } - let p_right = &sig.params()[1]; - if matches!(op, BinaryOp::CmpOp(..)) { - if let TyKind::Ref(mtbl, lt, _) = p_right.kind(Interner) { - self.write_expr_adj( - rhs, - Box::new([Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), *mtbl)), - target: p_right.clone(), - }]), - ); - } + let p_right = sig.inputs_and_output.as_slice()[1]; + if matches!(op, BinaryOp::CmpOp(..)) + && let TyKind::Ref(lt, _, mtbl) = p_right.kind() + { + self.write_expr_adj( + rhs, + Box::new([Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(lt, mtbl)), + target: p_right, + }]), + ); } - sig.ret().clone() + sig.output() } None => self.err_ty(), }; - let ret_ty = self.normalize_associated_types_in(ret_ty); + let ret_ty = self.process_remote_user_written_ty(ret_ty); - if self.is_builtin_binop(&lhs_ty, &rhs_ty, op) { + if self.is_builtin_binop(lhs_ty, rhs_ty, op) { // use knowledge of built-in binary ops, which can sometimes help inference - let builtin_ret = self.enforce_builtin_binop_types(&lhs_ty, &rhs_ty, op); - self.unify(&builtin_ret, &ret_ty); + let builtin_ret = self.enforce_builtin_binop_types(lhs_ty, rhs_ty, op); + self.unify(builtin_ret, ret_ty); + builtin_ret + } else { + ret_ty } - - ret_ty } fn infer_block( @@ -1433,8 +1454,8 @@ impl InferenceContext<'_> { statements: &[Statement], tail: Option<ExprId>, label: Option<LabelId>, - expected: &Expectation, - ) -> Ty { + expected: &Expectation<'db>, + ) -> Ty<'db> { let coerce_ty = expected.coercion_target_type(&mut self.table); let g = self.resolver.update_to_inner_scope(self.db, self.owner, expr); let prev_env = block_id.map(|block_id| { @@ -1451,7 +1472,7 @@ impl InferenceContext<'_> { let decl_ty = type_ref .as_ref() .map(|&tr| this.make_body_ty(tr)) - .unwrap_or_else(|| this.table.new_type_var()); + .unwrap_or_else(|| this.table.next_ty_var()); let ty = if let Some(expr) = initializer { // If we have a subpattern that performs a read, we want to consider this @@ -1465,13 +1486,13 @@ impl InferenceContext<'_> { let ty = if contains_explicit_ref_binding(this.body, *pat) { this.infer_expr( *expr, - &Expectation::has_type(decl_ty.clone()), + &Expectation::has_type(decl_ty), target_is_read, ) } else { this.infer_expr_coerce( *expr, - &Expectation::has_type(decl_ty.clone()), + &Expectation::has_type(decl_ty), target_is_read, ) }; @@ -1484,13 +1505,13 @@ impl InferenceContext<'_> { origin: DeclOrigin::LocalDecl { has_else: else_branch.is_some() }, }; - this.infer_top_pat(*pat, &ty, Some(decl)); + this.infer_top_pat(*pat, ty, Some(decl)); if let Some(expr) = else_branch { let previous_diverges = mem::replace(&mut this.diverges, Diverges::Maybe); this.infer_expr_coerce( *expr, - &Expectation::HasType(this.result.standard_types.never.clone()), + &Expectation::HasType(this.types.never), ExprIsRead::Yes, ); this.diverges = previous_diverges; @@ -1502,7 +1523,7 @@ impl InferenceContext<'_> { } else { this.infer_expr_coerce( expr, - &Expectation::HasType(this.result.standard_types.unit.clone()), + &Expectation::HasType(this.types.unit), ExprIsRead::Yes, ); } @@ -1535,24 +1556,22 @@ impl InferenceContext<'_> { }; if this .coerce( - Some(expr), - &this.result.standard_types.unit.clone(), - &t, + expr.into(), + this.types.unit, + t, + AllowTwoPhase::No, coerce_never, ) .is_err() { this.result.type_mismatches.insert( expr.into(), - TypeMismatch { - expected: t.clone(), - actual: this.result.standard_types.unit.clone(), - }, + TypeMismatch { expected: t, actual: this.types.unit }, ); } t } else { - this.result.standard_types.unit.clone() + this.types.unit } } }); @@ -1566,46 +1585,42 @@ impl InferenceContext<'_> { fn lookup_field( &mut self, - receiver_ty: &Ty, + receiver_ty: Ty<'db>, name: &Name, - ) -> Option<(Ty, Either<FieldId, TupleFieldId>, Vec<Adjustment>, bool)> { - let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false, false); + ) -> Option<(Ty<'db>, Either<FieldId, TupleFieldId>, Vec<Adjustment<'db>>, bool)> { + let interner = self.interner(); + let mut autoderef = self.table.autoderef(receiver_ty); let mut private_field = None; let res = autoderef.by_ref().find_map(|(derefed_ty, _)| { - let (field_id, parameters) = match derefed_ty.kind(Interner) { - TyKind::Tuple(_, substs) => { + let (field_id, parameters) = match derefed_ty.kind() { + TyKind::Tuple(substs) => { return name.as_tuple_index().and_then(|idx| { - substs - .as_slice(Interner) - .get(idx) - .map(|a| a.assert_ty_ref(Interner)) - .cloned() - .map(|ty| { - ( - Either::Right(TupleFieldId { - tuple: TupleId( - self.tuple_field_accesses_rev - .insert_full(substs.clone()) - .0 - as u32, - ), - index: idx as u32, - }), - ty, - ) - }) + substs.as_slice().get(idx).copied().map(|ty| { + ( + Either::Right(TupleFieldId { + tuple: TupleId( + self.tuple_field_accesses_rev.insert_full(substs).0 as u32, + ), + index: idx as u32, + }), + ty, + ) + }) }); } - &TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), ref parameters) => { - let local_id = s.fields(self.db).field(name)?; - let field = FieldId { parent: s.into(), local_id }; - (field, parameters.clone()) - } - &TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), ref parameters) => { - let local_id = u.fields(self.db).field(name)?; - let field = FieldId { parent: u.into(), local_id }; - (field, parameters.clone()) - } + TyKind::Adt(adt, parameters) => match adt.def_id().0 { + hir_def::AdtId::StructId(s) => { + let local_id = s.fields(self.db).field(name)?; + let field = FieldId { parent: s.into(), local_id }; + (field, parameters) + } + hir_def::AdtId::UnionId(u) => { + let local_id = u.fields(self.db).field(name)?; + let field = FieldId { parent: u.into(), local_id }; + (field, parameters) + } + hir_def::AdtId::EnumId(_) => return None, + }, _ => return None, }; let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id] @@ -1617,27 +1632,23 @@ impl InferenceContext<'_> { return None; } let ty = self.db.field_types(field_id.parent)[field_id.local_id] - .clone() - .substitute(Interner, ¶meters); + .instantiate(interner, parameters); Some((Either::Left(field_id), ty)) }); Some(match res { Some((field_id, ty)) => { - let adjustments = auto_deref_adjust_steps(&autoderef); - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); + let adjustments = autoderef.adjust_steps(); + let ty = self.process_remote_user_written_ty(ty); (ty, field_id, adjustments, true) } None => { let (field_id, subst) = private_field?; - let adjustments = auto_deref_adjust_steps(&autoderef); + let adjustments = autoderef.adjust_steps(); let ty = self.db.field_types(field_id.parent)[field_id.local_id] - .clone() - .substitute(Interner, &subst); - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); + .instantiate(self.interner(), subst); + let ty = self.process_remote_user_written_ty(ty); (ty, Either::Left(field_id), adjustments, false) } @@ -1649,8 +1660,8 @@ impl InferenceContext<'_> { tgt_expr: ExprId, receiver: ExprId, name: &Name, - expected: &Expectation, - ) -> Ty { + expected: &Expectation<'db>, + ) -> Ty<'db> { // Field projections don't constitute reads. let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::No); @@ -1660,25 +1671,23 @@ impl InferenceContext<'_> { return self.err_ty(); } - match self.lookup_field(&receiver_ty, name) { + match self.lookup_field(receiver_ty, name) { Some((ty, field_id, adjustments, is_public)) => { self.write_expr_adj(receiver, adjustments.into_boxed_slice()); self.result.field_resolutions.insert(tgt_expr, field_id); - if !is_public { - if let Either::Left(field) = field_id { - // FIXME: Merge this diagnostic into UnresolvedField? - self.push_diagnostic(InferenceDiagnostic::PrivateField { - expr: tgt_expr, - field, - }); - } + if !is_public && let Either::Left(field) = field_id { + // FIXME: Merge this diagnostic into UnresolvedField? + self.push_diagnostic(InferenceDiagnostic::PrivateField { + expr: tgt_expr, + field, + }); } ty } None => { // no field found, lets attempt to resolve it like a function so that IDE things // work out while people are typing - let canonicalized_receiver = self.canonicalize(receiver_ty.clone()); + let canonicalized_receiver = self.canonicalize(receiver_ty); let resolved = method_resolution::lookup_method( self.db, &canonicalized_receiver, @@ -1689,22 +1698,24 @@ impl InferenceContext<'_> { ); self.push_diagnostic(InferenceDiagnostic::UnresolvedField { expr: tgt_expr, - receiver: receiver_ty.clone(), + receiver: receiver_ty, name: name.clone(), method_with_same_name_exists: resolved.is_some(), }); match resolved { Some((adjust, func, _)) => { let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty); - let substs = self.substs_for_method_call(tgt_expr, func.into(), None); + let args = self.substs_for_method_call(tgt_expr, func.into(), None); self.write_expr_adj(receiver, adjustments.into_boxed_slice()); - self.write_method_resolution(tgt_expr, func, substs.clone()); + self.write_method_resolution(tgt_expr, func, args); self.check_method_call( tgt_expr, &[], - self.db.value_ty(func.into()).unwrap(), - substs, + self.db + .value_ty(func.into()) + .unwrap() + .instantiate(self.interner(), args), ty, expected, ) @@ -1720,55 +1731,54 @@ impl InferenceContext<'_> { tgt_expr: ExprId, callee: ExprId, args: &[ExprId], - expected: &Expectation, - ) -> Ty { + expected: &Expectation<'db>, + ) -> Ty<'db> { let callee_ty = self.infer_expr(callee, &Expectation::none(), ExprIsRead::Yes); - let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true); + let interner = self.interner(); + let mut derefs = self.table.autoderef(callee_ty); let (res, derefed_callee) = loop { let Some((callee_deref_ty, _)) = derefs.next() else { - break (None, callee_ty.clone()); + break (None, callee_ty); }; - if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) { + if let Some(res) = derefs.table.callable_sig(callee_deref_ty, args.len()) { break (Some(res), callee_deref_ty); } }; // if the function is unresolved, we use is_varargs=true to // suppress the arg count diagnostic here - let is_varargs = - derefed_callee.callable_sig(self.db).is_some_and(|sig| sig.is_varargs) || res.is_none(); + let is_varargs = derefed_callee.callable_sig(interner).is_some_and(|sig| sig.c_variadic()) + || res.is_none(); let (param_tys, ret_ty) = match res { Some((func, params, ret_ty)) => { - let mut adjustments = auto_deref_adjust_steps(&derefs); - if let TyKind::Closure(c, _) = - self.table.resolve_completely(callee_ty.clone()).kind(Interner) - { - self.add_current_closure_dependency(*c); - self.deferred_closures.entry(*c).or_default().push(( - derefed_callee.clone(), - callee_ty.clone(), - params.clone(), - tgt_expr, - )); - } + let mut adjustments = derefs.adjust_steps(); if let Some(fn_x) = func { self.write_fn_trait_method_resolution( fn_x, - &derefed_callee, + derefed_callee, &mut adjustments, - &callee_ty, + callee_ty, ¶ms, tgt_expr, ); } + if let TyKind::Closure(c, _) = self.table.resolve_completely(callee_ty).kind() { + self.add_current_closure_dependency(c.into()); + self.deferred_closures.entry(c.into()).or_default().push(( + derefed_callee, + callee_ty, + params.clone(), + tgt_expr, + )); + } self.write_expr_adj(callee, adjustments.into_boxed_slice()); (params, ret_ty) } None => { self.push_diagnostic(InferenceDiagnostic::ExpectedFunction { call_expr: tgt_expr, - found: callee_ty.clone(), + found: callee_ty, }); - (Vec::new(), self.err_ty()) + (Vec::new(), Ty::new_error(interner, ErrorGuaranteed)) } }; let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args); @@ -1788,30 +1798,25 @@ impl InferenceContext<'_> { &mut self, tgt_expr: ExprId, args: &[ExprId], - callee_ty: Ty, - param_tys: &[Ty], - ret_ty: Ty, + callee_ty: Ty<'db>, + param_tys: &[Ty<'db>], + ret_ty: Ty<'db>, indices_to_skip: &[u32], is_varargs: bool, - expected: &Expectation, - ) -> Ty { - self.register_obligations_for_call(&callee_ty); - - let expected_inputs = self.expected_inputs_for_expected_output( - expected, - ret_ty.clone(), - param_tys.to_owned(), - ); + expected: &Expectation<'db>, + ) -> Ty<'db> { + self.register_obligations_for_call(callee_ty); self.check_call_arguments( tgt_expr, - args, - &expected_inputs, param_tys, + ret_ty, + expected, + args, indices_to_skip, is_varargs, ); - self.normalize_associated_types_in(ret_ty) + self.table.normalize_associated_types_in(ret_ty) } fn infer_method_call( @@ -1820,11 +1825,23 @@ impl InferenceContext<'_> { receiver: ExprId, args: &[ExprId], method_name: &Name, - generic_args: Option<&GenericArgs>, - expected: &Expectation, - ) -> Ty { + generic_args: Option<&HirGenericArgs>, + expected: &Expectation<'db>, + ) -> Ty<'db> { let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes); - let canonicalized_receiver = self.canonicalize(receiver_ty.clone()); + let receiver_ty = self.table.try_structurally_resolve_type(receiver_ty); + + if matches!(receiver_ty.kind(), TyKind::Error(_) | TyKind::Infer(InferTy::TyVar(_))) { + // Don't probe on error type, or on a fully unresolved infer var. + // FIXME: Emit an error if we're probing on an infer var (type annotations needed). + for &arg in args { + // Make sure we infer and record the arguments. + self.infer_expr_no_expect(arg, ExprIsRead::Yes); + } + return receiver_ty; + } + + let canonicalized_receiver = self.canonicalize(receiver_ty); let resolved = method_resolution::lookup_method( self.db, @@ -1846,13 +1863,16 @@ impl InferenceContext<'_> { let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty); self.write_expr_adj(receiver, adjustments.into_boxed_slice()); - let substs = self.substs_for_method_call(tgt_expr, func.into(), generic_args); - self.write_method_resolution(tgt_expr, func, substs.clone()); + let gen_args = self.substs_for_method_call(tgt_expr, func.into(), generic_args); + self.write_method_resolution(tgt_expr, func, gen_args); + let interner = DbInterner::new_with(self.db, None, None); self.check_method_call( tgt_expr, args, - self.db.value_ty(func.into()).expect("we have a function def"), - substs, + self.db + .value_ty(func.into()) + .expect("we have a function def") + .instantiate(interner, gen_args), ty, expected, ) @@ -1860,7 +1880,7 @@ impl InferenceContext<'_> { // Failed to resolve, report diagnostic and try to resolve as call to field access or // assoc function None => { - let field_with_same_name_exists = match self.lookup_field(&receiver_ty, method_name) + let field_with_same_name_exists = match self.lookup_field(receiver_ty, method_name) { Some((ty, field_id, adjustments, _public)) => { self.write_expr_adj(receiver, adjustments.into_boxed_slice()); @@ -1888,41 +1908,50 @@ impl InferenceContext<'_> { self.push_diagnostic(InferenceDiagnostic::UnresolvedMethodCall { expr: tgt_expr, - receiver: receiver_ty.clone(), + receiver: receiver_ty, name: method_name.clone(), - field_with_same_name: field_with_same_name_exists.clone(), + field_with_same_name: field_with_same_name_exists, assoc_func_with_same_name, }); let recovered = match assoc_func_with_same_name { Some(f) => { - let substs = self.substs_for_method_call(tgt_expr, f.into(), generic_args); + let args = self.substs_for_method_call(tgt_expr, f.into(), generic_args); + let interner = DbInterner::new_with(self.db, None, None); let f = self .db .value_ty(f.into()) .expect("we have a function def") - .substitute(Interner, &substs); - let sig = f.callable_sig(self.db).expect("we have a function def"); + .instantiate(interner, args); + let sig = f.callable_sig(self.interner()).expect("we have a function def"); Some((f, sig, true)) } None => field_with_same_name_exists.and_then(|field_ty| { - let callable_sig = field_ty.callable_sig(self.db)?; + let callable_sig = field_ty.callable_sig(self.interner())?; Some((field_ty, callable_sig, false)) }), }; match recovered { - Some((callee_ty, sig, strip_first)) => self.check_call( - tgt_expr, - args, - callee_ty, - sig.params().get(strip_first as usize..).unwrap_or(&[]), - sig.ret().clone(), - &[], - true, - expected, - ), + Some((callee_ty, sig, strip_first)) => { + let sig = sig.skip_binder(); + self.check_call( + tgt_expr, + args, + callee_ty, + sig.inputs_and_output + .inputs() + .get(strip_first as usize..) + .unwrap_or(&[]), + sig.output(), + &[], + true, + expected, + ) + } None => { - self.check_call_arguments(tgt_expr, args, &[], &[], &[], true); + for &arg in args.iter() { + self.infer_expr_no_expect(arg, ExprIsRead::Yes); + } self.err_ty() } } @@ -1934,168 +1963,254 @@ impl InferenceContext<'_> { &mut self, tgt_expr: ExprId, args: &[ExprId], - method_ty: Binders<Ty>, - substs: Substitution, - receiver_ty: Ty, - expected: &Expectation, - ) -> Ty { - let method_ty = method_ty.substitute(Interner, &substs); - self.register_obligations_for_call(&method_ty); + method_ty: Ty<'db>, + receiver_ty: Ty<'db>, + expected: &Expectation<'db>, + ) -> Ty<'db> { + self.register_obligations_for_call(method_ty); let ((formal_receiver_ty, param_tys), ret_ty, is_varargs) = - match method_ty.callable_sig(self.db) { - Some(sig) => ( - if !sig.params().is_empty() { - (sig.params()[0].clone(), sig.params()[1..].to_vec()) - } else { - (self.err_ty(), Vec::new()) - }, - sig.ret().clone(), - sig.is_varargs, - ), - None => ((self.err_ty(), Vec::new()), self.err_ty(), true), + match method_ty.callable_sig(self.interner()) { + Some(sig) => { + let sig = sig.skip_binder(); + ( + if !sig.inputs_and_output.inputs().is_empty() { + ( + sig.inputs_and_output.as_slice()[0], + sig.inputs_and_output.inputs()[1..].to_vec(), + ) + } else { + (self.types.error, Vec::new()) + }, + sig.output(), + sig.c_variadic, + ) + } + None => { + let formal_receiver_ty = self.table.next_ty_var(); + let ret_ty = self.table.next_ty_var(); + ((formal_receiver_ty, Vec::new()), ret_ty, true) + } }; - self.unify(&formal_receiver_ty, &receiver_ty); + self.table.unify(formal_receiver_ty, receiver_ty); - let expected_inputs = - self.expected_inputs_for_expected_output(expected, ret_ty.clone(), param_tys.clone()); - - self.check_call_arguments(tgt_expr, args, &expected_inputs, ¶m_tys, &[], is_varargs); - self.normalize_associated_types_in(ret_ty) + self.check_call_arguments(tgt_expr, ¶m_tys, ret_ty, expected, args, &[], is_varargs); + self.table.normalize_associated_types_in(ret_ty) } - fn expected_inputs_for_expected_output( + /// Generic function that factors out common logic from function calls, + /// method calls and overloaded operators. + pub(in super::super) fn check_call_arguments( &mut self, - expected_output: &Expectation, - output: Ty, - inputs: Vec<Ty>, - ) -> Vec<Ty> { - if let Some(expected_ty) = expected_output.only_has_type(&mut self.table) { - self.table.fudge_inference(|table| { - if table.try_unify(&expected_ty, &output).is_ok() { - table.resolve_with_fallback(inputs, &|var, kind, _, _| match kind { - chalk_ir::VariableKind::Ty(tk) => var.to_ty(Interner, tk).cast(Interner), - chalk_ir::VariableKind::Lifetime => { - var.to_lifetime(Interner).cast(Interner) - } - chalk_ir::VariableKind::Const(ty) => { - var.to_const(Interner, ty).cast(Interner) + call_expr: ExprId, + // Types (as defined in the *signature* of the target function) + formal_input_tys: &[Ty<'db>], + formal_output: Ty<'db>, + // Expected output from the parent expression or statement + expectation: &Expectation<'db>, + // The expressions for each provided argument + provided_args: &[ExprId], + skip_indices: &[u32], + // Whether the function is variadic, for example when imported from C + c_variadic: bool, + ) { + // First, let's unify the formal method signature with the expectation eagerly. + // We use this to guide coercion inference; it's output is "fudged" which means + // any remaining type variables are assigned to new, unrelated variables. This + // is because the inference guidance here is only speculative. + let formal_output = self.table.resolve_vars_with_obligations(formal_output); + let expected_input_tys: Option<Vec<_>> = expectation + .only_has_type(&mut self.table) + .and_then(|expected_output| { + self.table + .infer_ctxt + .fudge_inference_if_ok(|| { + let mut ocx = ObligationCtxt::new(&self.table.infer_ctxt); + + // Attempt to apply a subtyping relationship between the formal + // return type (likely containing type variables if the function + // is polymorphic) and the expected return type. + // No argument expectations are produced if unification fails. + let origin = ObligationCause::new(); + ocx.sup(&origin, self.table.trait_env.env, expected_output, formal_output)?; + if !ocx.try_evaluate_obligations().is_empty() { + return Err(TypeError::Mismatch); } + + // Record all the argument types, with the args + // produced from the above subtyping unification. + Ok(Some( + formal_input_tys + .iter() + .map(|&ty| self.table.infer_ctxt.resolve_vars_if_possible(ty)) + .collect(), + )) }) - } else { - Vec::new() - } + .ok() }) + .unwrap_or_default(); + + // If there are no external expectations at the call site, just use the types from the function defn + let expected_input_tys = if let Some(expected_input_tys) = &expected_input_tys { + assert_eq!(expected_input_tys.len(), formal_input_tys.len()); + expected_input_tys } else { - Vec::new() - } - } + formal_input_tys + }; - fn check_call_arguments( - &mut self, - expr: ExprId, - args: &[ExprId], - expected_inputs: &[Ty], - param_tys: &[Ty], - skip_indices: &[u32], - ignore_arg_param_mismatch: bool, - ) { - let arg_count_mismatch = - !ignore_arg_param_mismatch && args.len() != param_tys.len() + skip_indices.len(); - if arg_count_mismatch { + let minimum_input_count = expected_input_tys.len(); + let provided_arg_count = provided_args.len() - skip_indices.len(); + + // Keep track of whether we *could possibly* be satisfied, i.e. whether we're on the happy path + // if the wrong number of arguments were supplied, we CAN'T be satisfied, + // and if we're c_variadic, the supplied arguments must be >= the minimum count from the function + // otherwise, they need to be identical, because rust doesn't currently support variadic functions + let args_count_matches = if c_variadic { + provided_arg_count >= minimum_input_count + } else { + provided_arg_count == minimum_input_count + }; + + if !args_count_matches { self.push_diagnostic(InferenceDiagnostic::MismatchedArgCount { - call_expr: expr, - expected: param_tys.len() + skip_indices.len(), - found: args.len(), + call_expr, + expected: expected_input_tys.len() + skip_indices.len(), + found: provided_args.len(), }); + } + + // We introduce a helper function to demand that a given argument satisfy a given input + // This is more complicated than just checking type equality, as arguments could be coerced + // This version writes those types back so further type checking uses the narrowed types + let demand_compatible = |this: &mut InferenceContext<'_, 'db>, idx| { + let formal_input_ty: Ty<'db> = formal_input_tys[idx]; + let expected_input_ty: Ty<'db> = expected_input_tys[idx]; + let provided_arg = provided_args[idx]; + + debug!("checking argument {}: {:?} = {:?}", idx, provided_arg, formal_input_ty); + + // We're on the happy path here, so we'll do a more involved check and write back types + // To check compatibility, we'll do 3 things: + // 1. Unify the provided argument with the expected type + let expectation = Expectation::rvalue_hint(this, expected_input_ty); + + let checked_ty = this.infer_expr_inner(provided_arg, &expectation, ExprIsRead::Yes); + + // 2. Coerce to the most detailed type that could be coerced + // to, which is `expected_ty` if `rvalue_hint` returns an + // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. + let coerced_ty = expectation.only_has_type(&mut this.table).unwrap_or(formal_input_ty); + + // Cause selection errors caused by resolving a single argument to point at the + // argument and not the call. This lets us customize the span pointed to in the + // fulfillment error to be more accurate. + let coerced_ty = this.table.resolve_vars_with_obligations(coerced_ty); + + let coerce_never = if this + .expr_guaranteed_to_constitute_read_for_never(provided_arg, ExprIsRead::Yes) + { + CoerceNever::Yes + } else { + CoerceNever::No + }; + let coerce_error = this + .coerce( + provided_arg.into(), + checked_ty, + coerced_ty, + AllowTwoPhase::Yes, + coerce_never, + ) + .err(); + if coerce_error.is_some() { + return Err((coerce_error, coerced_ty, checked_ty)); + } + + // 3. Check if the formal type is actually equal to the checked one + // and register any such obligations for future type checks. + let formal_ty_error = this + .table + .infer_ctxt + .at(&ObligationCause::new(), this.table.trait_env.env) + .eq(formal_input_ty, coerced_ty); + + // If neither check failed, the types are compatible + match formal_ty_error { + Ok(InferOk { obligations, value: () }) => { + this.table.register_predicates(obligations); + Ok(()) + } + Err(err) => Err((Some(err), coerced_ty, checked_ty)), + } }; - // Quoting https://github.com/rust-lang/rust/blob/6ef275e6c3cb1384ec78128eceeb4963ff788dca/src/librustc_typeck/check/mod.rs#L3325 -- + // Check the arguments. // We do this in a pretty awful way: first we type-check any arguments // that are not closures, then we type-check the closures. This is so // that we have more information about the types of arguments when we // type-check the functions. This isn't really the right way to do this. for check_closures in [false, true] { - let mut skip_indices = skip_indices.iter().copied().fuse().peekable(); - let param_iter = param_tys.iter().cloned().chain(repeat(self.err_ty())); - let expected_iter = expected_inputs - .iter() - .cloned() - .chain(param_iter.clone().skip(expected_inputs.len())); - for (idx, ((&arg, param_ty), expected_ty)) in - args.iter().zip(param_iter).zip(expected_iter).enumerate() - { - let is_closure = matches!(&self.body[arg], Expr::Closure { .. }); - if is_closure != check_closures { + // More awful hacks: before we check argument types, try to do + // an "opportunistic" trait resolution of any trait bounds on + // the call. This helps coercions. + if check_closures { + self.table.select_obligations_where_possible(); + } + + let mut skip_indices = skip_indices.iter().copied(); + // Check each argument, to satisfy the input it was provided for + // Visually, we're traveling down the diagonal of the compatibility matrix + for (idx, arg) in provided_args.iter().enumerate() { + if skip_indices.clone().next() == Some(idx as u32) { + skip_indices.next(); continue; } - while skip_indices.peek().is_some_and(|&i| i < idx as u32) { - skip_indices.next(); + // For this check, we do *not* want to treat async coroutine closures (async blocks) + // as proper closures. Doing so would regress type inference when feeding + // the return value of an argument-position async block to an argument-position + // closure wrapped in a block. + // See <https://github.com/rust-lang/rust/issues/112225>. + let is_closure = if let Expr::Closure { closure_kind, .. } = self.body[*arg] { + !matches!(closure_kind, ClosureKind::Coroutine(_)) + } else { + false + }; + if is_closure != check_closures { + continue; } - if skip_indices.peek().copied() == Some(idx as u32) { + + if idx >= minimum_input_count { + // Make sure we've checked this expr at least once. + self.infer_expr_no_expect(*arg, ExprIsRead::Yes); continue; } - // the difference between param_ty and expected here is that - // expected is the parameter when the expected *return* type is - // taken into account. So in `let _: &[i32] = identity(&[1, 2])` - // the expected type is already `&[i32]`, whereas param_ty is - // still an unbound type variable. We don't always want to force - // the parameter to coerce to the expected type (for example in - // `coerce_unsize_expected_type_4`). - let param_ty = self.normalize_associated_types_in(param_ty); - let expected_ty = self.normalize_associated_types_in(expected_ty); - let expected = Expectation::rvalue_hint(self, expected_ty); - // infer with the expected type we have... - let ty = self.infer_expr_inner(arg, &expected, ExprIsRead::Yes); - - // then coerce to either the expected type or just the formal parameter type - let coercion_target = if let Some(ty) = expected.only_has_type(&mut self.table) { - // if we are coercing to the expectation, unify with the - // formal parameter type to connect everything - self.unify(&ty, ¶m_ty); - ty - } else { - param_ty - }; - // The function signature may contain some unknown types, so we need to insert - // type vars here to avoid type mismatch false positive. - let coercion_target = self.insert_type_vars(coercion_target); - - // Any expression that produces a value of type `!` must have diverged, - // unless it's a place expression that isn't being read from, in which case - // diverging would be unsound since we may never actually read the `!`. - // e.g. `let _ = *never_ptr;` with `never_ptr: *const !`. - let coerce_never = - if self.expr_guaranteed_to_constitute_read_for_never(arg, ExprIsRead::Yes) { - CoerceNever::Yes - } else { - CoerceNever::No - }; - if self.coerce(Some(arg), &ty, &coercion_target, coerce_never).is_err() - && !arg_count_mismatch + if let Err((_error, expected, found)) = demand_compatible(self, idx) + && args_count_matches { - self.result.type_mismatches.insert( - arg.into(), - TypeMismatch { expected: coercion_target, actual: ty.clone() }, - ); + // Don't report type mismatches if there is a mismatch in args count. + self.result + .type_mismatches + .insert((*arg).into(), TypeMismatch { expected, actual: found }); } } } + + if !args_count_matches {} } fn substs_for_method_call( &mut self, expr: ExprId, def: GenericDefId, - generic_args: Option<&GenericArgs>, - ) -> Substitution { - struct LowererCtx<'a, 'b> { - ctx: &'a mut InferenceContext<'b>, + generic_args: Option<&HirGenericArgs>, + ) -> GenericArgs<'db> { + struct LowererCtx<'a, 'b, 'db> { + ctx: &'a mut InferenceContext<'b, 'db>, expr: ExprId, } - impl GenericArgsLowerer for LowererCtx<'_, '_> { + impl<'db> GenericArgsLowerer<'db> for LowererCtx<'_, '_, 'db> { fn report_len_mismatch( &mut self, def: GenericDefId, @@ -2130,21 +2245,22 @@ impl InferenceContext<'_> { &mut self, param_id: GenericParamId, param: GenericParamDataRef<'_>, - arg: &GenericArg, - ) -> crate::GenericArg { + arg: &HirGenericArg, + ) -> GenericArg<'db> { match (param, arg) { - (GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => { - self.ctx.make_body_lifetime(*lifetime).cast(Interner) - } - (GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => { - self.ctx.make_body_ty(*type_ref).cast(Interner) + ( + GenericParamDataRef::LifetimeParamData(_), + HirGenericArg::Lifetime(lifetime), + ) => self.ctx.make_body_lifetime(*lifetime).into(), + (GenericParamDataRef::TypeParamData(_), HirGenericArg::Type(type_ref)) => { + self.ctx.make_body_ty(*type_ref).into() } - (GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => { + (GenericParamDataRef::ConstParamData(_), HirGenericArg::Const(konst)) => { let GenericParamId::ConstParamId(const_id) = param_id else { unreachable!("non-const param ID for const param"); }; - let const_ty = self.ctx.db.const_param_ty(const_id); - self.ctx.make_body_const(*konst, const_ty).cast(Interner) + let const_ty = self.ctx.db.const_param_ty_ns(const_id); + self.ctx.make_body_const(*konst, const_ty).into() } _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), } @@ -2152,12 +2268,12 @@ impl InferenceContext<'_> { fn provided_type_like_const( &mut self, - const_ty: Ty, + const_ty: Ty<'db>, arg: TypeLikeConst<'_>, - ) -> crate::Const { + ) -> Const<'db> { match arg { TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path, const_ty), - TypeLikeConst::Infer => self.ctx.table.new_const_var(const_ty), + TypeLikeConst::Infer => self.ctx.table.next_const_var(), } } @@ -2167,35 +2283,15 @@ impl InferenceContext<'_> { param_id: GenericParamId, _param: GenericParamDataRef<'_>, _infer_args: bool, - _preceding_args: &[crate::GenericArg], - ) -> crate::GenericArg { + _preceding_args: &[GenericArg<'db>], + ) -> GenericArg<'db> { // Always create an inference var, even when `infer_args == false`. This helps with diagnostics, // and I think it's also required in the presence of `impl Trait` (that must be inferred). - match param_id { - GenericParamId::TypeParamId(_) => self.ctx.table.new_type_var().cast(Interner), - GenericParamId::ConstParamId(const_id) => self - .ctx - .table - .new_const_var(self.ctx.db.const_param_ty(const_id)) - .cast(Interner), - GenericParamId::LifetimeParamId(_) => { - self.ctx.table.new_lifetime_var().cast(Interner) - } - } + self.ctx.table.next_var_for_param(param_id) } - fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg { - match param_id { - GenericParamId::TypeParamId(_) => self.ctx.table.new_type_var().cast(Interner), - GenericParamId::ConstParamId(const_id) => self - .ctx - .table - .new_const_var(self.ctx.db.const_param_ty(const_id)) - .cast(Interner), - GenericParamId::LifetimeParamId(_) => { - self.ctx.table.new_lifetime_var().cast(Interner) - } - } + fn parent_arg(&mut self, param_id: GenericParamId) -> GenericArg<'db> { + self.ctx.table.next_var_for_param(param_id) } fn report_elided_lifetimes_in_path( @@ -2229,36 +2325,34 @@ impl InferenceContext<'_> { ) } - fn register_obligations_for_call(&mut self, callable_ty: &Ty) { - let callable_ty = self.resolve_ty_shallow(callable_ty); - if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(Interner) { - let def: CallableDefId = from_chalk(self.db, *fn_def); + fn register_obligations_for_call(&mut self, callable_ty: Ty<'db>) { + let callable_ty = self.table.try_structurally_resolve_type(callable_ty); + if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind() { let generic_predicates = - self.db.generic_predicates(GenericDefId::from_callable(self.db, def)); - for predicate in generic_predicates.iter() { - let (predicate, binders) = predicate - .clone() - .substitute(Interner, parameters) - .into_value_and_skipped_binders(); - always!(binders.len(Interner) == 0); // quantified where clauses not yet handled - self.push_obligation(predicate.cast(Interner)); + self.db.generic_predicates(GenericDefId::from_callable(self.db, fn_def.0)); + if let Some(predicates) = generic_predicates.instantiate(self.interner(), parameters) { + let interner = self.interner(); + let param_env = self.table.trait_env.env; + self.table.register_predicates(predicates.map(|predicate| { + Obligation::new(interner, ObligationCause::new(), param_env, predicate) + })); } // add obligation for trait implementation, if this is a trait method - match def { + match fn_def.0 { CallableDefId::FunctionId(f) => { if let ItemContainerId::TraitId(trait_) = f.lookup(self.db).container { // construct a TraitRef let trait_params_len = generics(self.db, trait_.into()).len(); - let substs = Substitution::from_iter( - Interner, - // The generic parameters for the trait come after those for the - // function. - ¶meters.as_slice(Interner)[..trait_params_len], - ); - self.push_obligation( - TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs } - .cast(Interner), + let substs = GenericArgs::new_from_iter( + self.interner(), + parameters.as_slice()[..trait_params_len].iter().copied(), ); + self.table.register_predicate(Obligation::new( + self.interner(), + ObligationCause::new(), + self.table.trait_env.env, + TraitRef::new(self.interner(), trait_.into(), substs), + )); } } CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {} @@ -2267,11 +2361,10 @@ impl InferenceContext<'_> { } /// Returns the argument indices to skip. - fn check_legacy_const_generics(&mut self, callee: Ty, args: &[ExprId]) -> Box<[u32]> { - let (func, subst) = match callee.kind(Interner) { - TyKind::FnDef(fn_id, subst) => { - let callable = CallableDefId::from_chalk(self.db, *fn_id); - let func = match callable { + fn check_legacy_const_generics(&mut self, callee: Ty<'db>, args: &[ExprId]) -> Box<[u32]> { + let (func, _subst) = match callee.kind() { + TyKind::FnDef(callable, subst) => { + let func = match callable.0 { CallableDefId::FunctionId(f) => f, _ => return Default::default(), }; @@ -2299,15 +2392,10 @@ impl InferenceContext<'_> { } // check legacy const parameters - for (subst_idx, arg_idx) in legacy_const_generics_indices.iter().copied().enumerate() { - let arg = match subst.at(Interner, subst_idx).constant(Interner) { - Some(c) => c, - None => continue, // not a const parameter? - }; + for arg_idx in legacy_const_generics_indices.iter().copied() { if arg_idx >= args.len() as u32 { continue; } - let _ty = arg.data(Interner).ty.clone(); let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly self.infer_expr(args[arg_idx as usize], &expected, ExprIsRead::Yes); // FIXME: evaluate and unify with the const @@ -2318,17 +2406,19 @@ impl InferenceContext<'_> { } /// Dereferences a single level of immutable referencing. - fn deref_ty_if_possible(&mut self, ty: &Ty) -> Ty { - let ty = self.resolve_ty_shallow(ty); - match ty.kind(Interner) { - TyKind::Ref(Mutability::Not, _, inner) => self.resolve_ty_shallow(inner), + fn deref_ty_if_possible(&mut self, ty: Ty<'db>) -> Ty<'db> { + let ty = self.table.try_structurally_resolve_type(ty); + match ty.kind() { + TyKind::Ref(_, inner, Mutability::Not) => { + self.table.try_structurally_resolve_type(inner) + } _ => ty, } } /// Enforces expectations on lhs type and rhs type depending on the operator and returns the /// output type of the binary op. - fn enforce_builtin_binop_types(&mut self, lhs: &Ty, rhs: &Ty, op: BinaryOp) -> Ty { + fn enforce_builtin_binop_types(&mut self, lhs: Ty<'db>, rhs: Ty<'db>, op: BinaryOp) -> Ty<'db> { // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work (See rust-lang/rust#57447). let lhs = self.deref_ty_if_possible(lhs); let rhs = self.deref_ty_if_possible(rhs); @@ -2340,9 +2430,9 @@ impl InferenceContext<'_> { let output_ty = match op { BinaryOp::LogicOp(_) => { - let bool_ = self.result.standard_types.bool_.clone(); - self.unify(&lhs, &bool_); - self.unify(&rhs, &bool_); + let bool_ = self.types.bool; + self.unify(lhs, bool_); + self.unify(rhs, bool_); bool_ } @@ -2353,14 +2443,14 @@ impl InferenceContext<'_> { BinaryOp::ArithOp(_) => { // LHS, RHS, and result will have the same type - self.unify(&lhs, &rhs); + self.unify(lhs, rhs); lhs } BinaryOp::CmpOp(_) => { // LHS and RHS will have the same type - self.unify(&lhs, &rhs); - self.result.standard_types.bool_.clone() + self.unify(lhs, rhs); + self.types.bool } BinaryOp::Assignment { op: None } => { @@ -2371,10 +2461,10 @@ impl InferenceContext<'_> { BinaryOp::Assignment { .. } => unreachable!("handled above"), }; - if is_assign { self.result.standard_types.unit.clone() } else { output_ty } + if is_assign { self.types.unit } else { output_ty } } - fn is_builtin_binop(&mut self, lhs: &Ty, rhs: &Ty, op: BinaryOp) -> bool { + fn is_builtin_binop(&mut self, lhs: Ty<'db>, rhs: Ty<'db>, op: BinaryOp) -> bool { // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work (See rust-lang/rust#57447). let lhs = self.deref_ty_if_possible(lhs); let rhs = self.deref_ty_if_possible(rhs); @@ -2401,27 +2491,25 @@ impl InferenceContext<'_> { BinaryOp::ArithOp(ArithOp::BitAnd | ArithOp::BitOr | ArithOp::BitXor) => { lhs.is_integral() && rhs.is_integral() || lhs.is_floating_point() && rhs.is_floating_point() - || matches!( - (lhs.kind(Interner), rhs.kind(Interner)), - (TyKind::Scalar(Scalar::Bool), TyKind::Scalar(Scalar::Bool)) - ) + || matches!((lhs.kind(), rhs.kind()), (TyKind::Bool, TyKind::Bool)) } BinaryOp::CmpOp(_) => { let is_scalar = |kind| { matches!( kind, - &TyKind::Scalar(_) + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) | TyKind::FnDef(..) - | TyKind::Function(_) - | TyKind::Raw(..) - | TyKind::InferenceVar( - _, - TyVariableKind::Integer | TyVariableKind::Float - ) + | TyKind::FnPtr(..) + | TyKind::RawPtr(..) + | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) ) }; - is_scalar(lhs.kind(Interner)) && is_scalar(rhs.kind(Interner)) + is_scalar(lhs.kind()) && is_scalar(rhs.kind()) } BinaryOp::Assignment { op: None } => { @@ -2436,10 +2524,10 @@ impl InferenceContext<'_> { pub(super) fn with_breakable_ctx<T>( &mut self, kind: BreakableKind, - ty: Option<Ty>, + ty: Option<Ty<'db>>, label: Option<LabelId>, cb: impl FnOnce(&mut Self) -> T, - ) -> (Option<Ty>, T) { + ) -> (Option<Ty<'db>>, T) { self.breakables.push({ BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label } }); |