summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_typeck/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--compiler/rustc_hir_analysis/src/bounds.rs (renamed from compiler/rustc_typeck/src/bounds.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/check/dropck.rs (renamed from compiler/rustc_typeck/src/check/dropck.rs)14
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs (renamed from compiler/rustc_typeck/src/check/intrinsic.rs)60
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs (renamed from compiler/rustc_typeck/src/check/region.rs)51
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs (renamed from compiler/rustc_typeck/src/check/wfcheck.rs)367
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs (renamed from compiler/rustc_typeck/src/coherence/inherent_impls.rs)18
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs (renamed from compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs)58
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/mod.rs (renamed from compiler/rustc_typeck/src/coherence/mod.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs (renamed from compiler/rustc_typeck/src/coherence/orphan.rs)38
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs (renamed from compiler/rustc_typeck/src/collect/item_bounds.rs)16
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs (renamed from compiler/rustc_typeck/src/collect/type_of.rs)265
-rw-r--r--compiler/rustc_hir_analysis/src/constrained_generic_params.rs (renamed from compiler/rustc_typeck/src/constrained_generic_params.rs)10
-rw-r--r--compiler/rustc_hir_analysis/src/hir_wf_check.rs (renamed from compiler/rustc_typeck/src/hir_wf_check.rs)68
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check.rs (renamed from compiler/rustc_typeck/src/impl_wf_check.rs)43
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs (renamed from compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs)83
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/explicit.rs (renamed from compiler/rustc_typeck/src/outlives/explicit.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs (renamed from compiler/rustc_typeck/src/outlives/implicit_infer.rs)4
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors.rs (renamed from compiler/rustc_typeck/src/structured_errors.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs (renamed from compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs (renamed from compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs (renamed from compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs)255
-rw-r--r--compiler/rustc_hir_analysis/src/variance/constraints.rs (renamed from compiler/rustc_typeck/src/variance/constraints.rs)14
-rw-r--r--compiler/rustc_hir_analysis/src/variance/mod.rs (renamed from compiler/rustc_typeck/src/variance/mod.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/variance/solve.rs (renamed from compiler/rustc_typeck/src/variance/solve.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/variance/terms.rs (renamed from compiler/rustc_typeck/src/variance/terms.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/variance/xform.rs (renamed from compiler/rustc_typeck/src/variance/xform.rs)0
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs (renamed from compiler/rustc_typeck/src/check/_match.rs)131
-rw-r--r--compiler/rustc_hir_typeck/src/autoderef.rs (renamed from compiler/rustc_typeck/src/check/autoderef.rs)0
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs (renamed from compiler/rustc_typeck/src/check/cast.rs)139
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs (renamed from compiler/rustc_typeck/src/check/coercion.rs)172
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs (renamed from compiler/rustc_typeck/src/check/demand.rs)218
-rw-r--r--compiler/rustc_hir_typeck/src/diverges.rs (renamed from compiler/rustc_typeck/src/check/diverges.rs)0
-rw-r--r--compiler/rustc_hir_typeck/src/expectation.rs (renamed from compiler/rustc_typeck/src/check/expectation.rs)0
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs (renamed from compiler/rustc_typeck/src/expr_use_visitor.rs)20
-rw-r--r--compiler/rustc_hir_typeck/src/fallback.rs (renamed from compiler/rustc_typeck/src/check/fallback.rs)4
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs (renamed from compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs)198
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs (renamed from compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs)25
-rw-r--r--compiler/rustc_hir_typeck/src/gather_locals.rs (renamed from compiler/rustc_typeck/src/check/gather_locals.rs)3
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs (renamed from compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs)7
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs (renamed from compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs)0
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs (renamed from compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs)0
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs (renamed from compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs)19
-rw-r--r--compiler/rustc_hir_typeck/src/mem_categorization.rs (renamed from compiler/rustc_typeck/src/mem_categorization.rs)24
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs (renamed from compiler/rustc_typeck/src/check/method/confirm.rs)20
-rw-r--r--compiler/rustc_hir_typeck/src/method/prelude2021.rs (renamed from compiler/rustc_typeck/src/check/method/prelude2021.rs)122
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs (renamed from compiler/rustc_typeck/src/check/method/probe.rs)180
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs (renamed from compiler/rustc_typeck/src/check/method/suggest.rs)663
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs (renamed from compiler/rustc_typeck/src/check/pat.rs)77
-rw-r--r--compiler/rustc_hir_typeck/src/place_op.rs (renamed from compiler/rustc_typeck/src/check/place_op.rs)4
-rw-r--r--compiler/rustc_hir_typeck/src/rvalue_scopes.rs (renamed from compiler/rustc_typeck/src/check/rvalue_scopes.rs)0
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs (renamed from compiler/rustc_typeck/src/check/upvar.rs)34
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs (renamed from compiler/rustc_typeck/src/check/writeback.rs)118
-rw-r--r--compiler/rustc_typeck/src/astconv/errors.rs410
-rw-r--r--compiler/rustc_typeck/src/astconv/generics.rs664
-rw-r--r--compiler/rustc_typeck/src/astconv/mod.rs3091
-rw-r--r--compiler/rustc_typeck/src/check/callee.rs675
-rw-r--r--compiler/rustc_typeck/src/check/check.rs1712
-rw-r--r--compiler/rustc_typeck/src/check/closure.rs805
-rw-r--r--compiler/rustc_typeck/src/check/compare_method.rs1547
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs2824
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/checks.rs1900
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/mod.rs296
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs912
-rw-r--r--compiler/rustc_typeck/src/check/generator_interior.rs632
-rw-r--r--compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs309
-rw-r--r--compiler/rustc_typeck/src/check/inherited.rs183
-rw-r--r--compiler/rustc_typeck/src/check/intrinsicck.rs530
-rw-r--r--compiler/rustc_typeck/src/check/method/mod.rs658
-rw-r--r--compiler/rustc_typeck/src/check/mod.rs970
-rw-r--r--compiler/rustc_typeck/src/check/op.rs1076
-rw-r--r--compiler/rustc_typeck/src/check/regionck.rs47
-rw-r--r--compiler/rustc_typeck/src/check_unused.rs196
-rw-r--r--compiler/rustc_typeck/src/coherence/builtin.rs603
-rw-r--r--compiler/rustc_typeck/src/coherence/unsafety.rs66
-rw-r--r--compiler/rustc_typeck/src/collect.rs3361
-rw-r--r--compiler/rustc_typeck/src/errors.rs326
-rw-r--r--compiler/rustc_typeck/src/lib.rs579
-rw-r--r--compiler/rustc_typeck/src/outlives/mod.rs130
-rw-r--r--compiler/rustc_typeck/src/outlives/outlives_bounds.rs90
-rw-r--r--compiler/rustc_typeck/src/outlives/test.rs21
-rw-r--r--compiler/rustc_typeck/src/outlives/utils.rs175
-rw-r--r--compiler/rustc_typeck/src/variance/test.rs14
82 files changed, 2244 insertions, 26100 deletions
diff --git a/compiler/rustc_typeck/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs
index 6a28bb16a..6a28bb16a 100644
--- a/compiler/rustc_typeck/src/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/bounds.rs
diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs
index 321064ec0..a74016e22 100644
--- a/compiler/rustc_typeck/src/check/dropck.rs
+++ b/compiler/rustc_hir_analysis/src/check/dropck.rs
@@ -1,4 +1,4 @@
-// FIXME(@lcnr): Move this module out of `rustc_typeck`.
+// FIXME(@lcnr): Move this module out of `rustc_hir_analysis`.
//
// We don't do any drop checking during hir typeck.
use crate::hir::def_id::{DefId, LocalDefId};
@@ -144,6 +144,8 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs);
let assumptions_in_impl_context = assumptions_in_impl_context.predicates;
+ debug!(?assumptions_in_impl_context, ?dtor_predicates.predicates);
+
let self_param_env = tcx.param_env(self_type_did);
// An earlier version of this code attempted to do this checking
@@ -182,13 +184,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
let p = p.kind();
match (predicate.skip_binder(), p.skip_binder()) {
(ty::PredicateKind::Trait(a), ty::PredicateKind::Trait(b)) => {
- // Since struct predicates cannot have ~const, project the impl predicate
- // onto one that ignores the constness. This is equivalent to saying that
- // we match a `Trait` bound on the struct with a `Trait` or `~const Trait`
- // in the impl.
- let non_const_a =
- ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..a };
- relator.relate(predicate.rebind(non_const_a), p.rebind(b)).is_ok()
+ relator.relate(predicate.rebind(a), p.rebind(b)).is_ok()
}
(ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => {
relator.relate(predicate.rebind(a), p.rebind(b)).is_ok()
@@ -196,7 +192,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
(
ty::PredicateKind::ConstEvaluatable(a),
ty::PredicateKind::ConstEvaluatable(b),
- ) => tcx.try_unify_abstract_consts(self_param_env.and((a, b))),
+ ) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(),
(
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)),
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)),
diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 3f2a0da8d..609095c9c 100644
--- a/compiler/rustc_typeck/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -7,10 +7,10 @@ use crate::errors::{
};
use crate::require_same_types;
-use rustc_errors::struct_span_err;
+use hir::def_id::DefId;
+use rustc_errors::{struct_span_err, DiagnosticMessage};
use rustc_hir as hir;
use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
-use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_target::spec::abi::Abi;
@@ -26,7 +26,7 @@ fn equate_intrinsic_type<'tcx>(
) {
let (own_counts, span) = match &it.kind {
hir::ForeignItemKind::Fn(.., generics) => {
- let own_counts = tcx.generics_of(it.def_id.to_def_id()).own_counts();
+ let own_counts = tcx.generics_of(it.owner_id.to_def_id()).own_counts();
(own_counts, generics.span)
}
_ => {
@@ -57,18 +57,25 @@ fn equate_intrinsic_type<'tcx>(
{
let fty = tcx.mk_fn_ptr(sig);
let cause = ObligationCause::new(it.span, it.hir_id(), ObligationCauseCode::IntrinsicType);
- require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(it.def_id)), fty);
+ require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(it.owner_id)), fty);
}
}
/// Returns the unsafety of the given intrinsic.
-pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety {
- match intrinsic {
+pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir::Unsafety {
+ let has_safe_attr = match tcx.has_attr(intrinsic_id, sym::rustc_safe_intrinsic) {
+ true => hir::Unsafety::Normal,
+ false => hir::Unsafety::Unsafe,
+ };
+ let is_in_list = match tcx.item_name(intrinsic_id) {
// When adding a new intrinsic to this list,
// it's usually worth updating that intrinsic's documentation
// to note that it's safe to call, since
// safe extern fns are otherwise unprecedented.
sym::abort
+ | sym::assert_inhabited
+ | sym::assert_zero_valid
+ | sym::assert_uninit_valid
| sym::size_of
| sym::min_align_of
| sym::needs_drop
@@ -92,8 +99,7 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety {
| sym::type_id
| sym::likely
| sym::unlikely
- | sym::ptr_guaranteed_eq
- | sym::ptr_guaranteed_ne
+ | sym::ptr_guaranteed_cmp
| sym::minnumf32
| sym::minnumf64
| sym::maxnumf32
@@ -102,16 +108,29 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety {
| sym::type_name
| sym::forget
| sym::black_box
- | sym::variant_count => hir::Unsafety::Normal,
+ | sym::variant_count
+ | sym::ptr_mask => hir::Unsafety::Normal,
_ => hir::Unsafety::Unsafe,
+ };
+
+ if has_safe_attr != is_in_list {
+ tcx.sess.struct_span_err(
+ tcx.def_span(intrinsic_id),
+ DiagnosticMessage::Str(format!(
+ "intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `{}`",
+ tcx.item_name(intrinsic_id)
+ ))).emit();
}
+
+ is_in_list
}
/// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`,
/// and in `library/core/src/intrinsics.rs`.
pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
let param = |n| tcx.mk_ty_param(n, Symbol::intern(&format!("P{}", n)));
- let intrinsic_name = tcx.item_name(it.def_id.to_def_id());
+ let intrinsic_id = it.owner_id.to_def_id();
+ let intrinsic_name = tcx.item_name(intrinsic_id);
let name_str = intrinsic_name.as_str();
let bound_vars = tcx.mk_bound_variable_kinds(
@@ -158,7 +177,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
};
(n_tps, 0, inputs, output, hir::Unsafety::Unsafe)
} else {
- let unsafety = intrinsic_operation_unsafety(intrinsic_name);
+ let unsafety = intrinsic_operation_unsafety(tcx, intrinsic_id);
let (n_tps, inputs, output) = match intrinsic_name {
sym::abort => (0, Vec::new(), tcx.types.never),
sym::unreachable => (0, Vec::new(), tcx.types.never),
@@ -200,6 +219,15 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
],
tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
),
+ sym::ptr_mask => (
+ 1,
+ vec![
+ tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+ tcx.types.usize,
+ ],
+ tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+ ),
+
sym::copy | sym::copy_nonoverlapping => (
1,
vec![
@@ -289,8 +317,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
(1, vec![param(0), param(0)], tcx.intern_tup(&[param(0), tcx.types.bool]))
}
- sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
- (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.bool)
+ sym::ptr_guaranteed_cmp => {
+ (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.u8)
}
sym::const_allocate => {
@@ -465,7 +493,11 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
sym::simd_scatter => (3, vec![param(0), param(1), param(2)], tcx.mk_unit()),
sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)),
sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)),
- sym::simd_cast | sym::simd_as => (2, vec![param(0)], param(1)),
+ sym::simd_cast
+ | sym::simd_as
+ | sym::simd_cast_ptr
+ | sym::simd_expose_addr
+ | sym::simd_from_exposed_addr => (2, vec![param(0)], param(1)),
sym::simd_bitmask => (2, vec![param(0)], param(1)),
sym::simd_select | sym::simd_select_bitmask => {
(2, vec![param(0), param(1), param(1)], param(1))
diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 0081e9049..ff32329e4 100644
--- a/compiler/rustc_typeck/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -126,6 +126,29 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
for (i, statement) in blk.stmts.iter().enumerate() {
match statement.kind {
+ hir::StmtKind::Local(hir::Local { els: Some(els), .. }) => {
+ // Let-else has a special lexical structure for variables.
+ // First we take a checkpoint of the current scope context here.
+ let mut prev_cx = visitor.cx;
+
+ visitor.enter_scope(Scope {
+ id: blk.hir_id.local_id,
+ data: ScopeData::Remainder(FirstStatementIndex::new(i)),
+ });
+ visitor.cx.var_parent = visitor.cx.parent;
+ visitor.visit_stmt(statement);
+ // We need to back out temporarily to the last enclosing scope
+ // for the `else` block, so that even the temporaries receiving
+ // extended lifetime will be dropped inside this block.
+ // We are visiting the `else` block in this order so that
+ // the sequence of visits agree with the order in the default
+ // `hir::intravisit` visitor.
+ mem::swap(&mut prev_cx, &mut visitor.cx);
+ visitor.terminating_scopes.insert(els.hir_id.local_id);
+ visitor.visit_block(els);
+ // From now on, we continue normally.
+ visitor.cx = prev_cx;
+ }
hir::StmtKind::Local(..) | hir::StmtKind::Item(..) => {
// Each declaration introduces a subscope for bindings
// introduced by the declaration; this subscope covers a
@@ -138,10 +161,10 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
data: ScopeData::Remainder(FirstStatementIndex::new(i)),
});
visitor.cx.var_parent = visitor.cx.parent;
+ visitor.visit_stmt(statement)
}
- hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {}
+ hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement),
}
- visitor.visit_stmt(statement)
}
walk_list!(visitor, visit_expr, &blk.expr);
}
@@ -229,9 +252,13 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
) => {
// For shortcircuiting operators, mark the RHS as a terminating
// scope since it only executes conditionally.
- terminating(r.hir_id.local_id);
- }
+ // `Let` expressions (in a let-chain) shouldn't be terminating, as their temporaries
+ // should live beyond the immediate expression
+ if !matches!(r.kind, hir::ExprKind::Let(_)) {
+ terminating(r.hir_id.local_id);
+ }
+ }
hir::ExprKind::If(_, ref then, Some(ref otherwise)) => {
terminating(then.hir_id.local_id);
terminating(otherwise.hir_id.local_id);
@@ -460,7 +487,6 @@ fn resolve_local<'tcx>(
visitor: &mut RegionResolutionVisitor<'tcx>,
pat: Option<&'tcx hir::Pat<'tcx>>,
init: Option<&'tcx hir::Expr<'tcx>>,
- els: Option<&'tcx hir::Block<'tcx>>,
) {
debug!("resolve_local(pat={:?}, init={:?})", pat, init);
@@ -547,9 +573,6 @@ fn resolve_local<'tcx>(
if let Some(pat) = pat {
visitor.visit_pat(pat);
}
- if let Some(els) = els {
- visitor.visit_block(els);
- }
/// Returns `true` if `pat` match the `P&` non-terminal.
///
@@ -587,8 +610,7 @@ fn resolve_local<'tcx>(
// & expression, and its lifetime would be extended to the end of the block (due
// to a different rule, not the below code).
match pat.kind {
- PatKind::Binding(hir::BindingAnnotation::Ref, ..)
- | PatKind::Binding(hir::BindingAnnotation::RefMut, ..) => true,
+ PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes, _), ..) => true,
PatKind::Struct(_, ref field_pats, _) => {
field_pats.iter().any(|fp| is_binding_pat(&fp.pat))
@@ -607,10 +629,7 @@ fn resolve_local<'tcx>(
PatKind::Box(ref subpat) => is_binding_pat(&subpat),
PatKind::Ref(_, _)
- | PatKind::Binding(
- hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable,
- ..,
- )
+ | PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), ..)
| PatKind::Wild
| PatKind::Path(_)
| PatKind::Lit(_)
@@ -770,7 +789,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
// (i.e., `'static`), which means that after `g` returns, it drops,
// and all the associated destruction scope rules apply.
self.cx.var_parent = None;
- resolve_local(self, None, Some(&body.value), None);
+ resolve_local(self, None, Some(&body.value));
}
if body.generator_kind.is_some() {
@@ -797,7 +816,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
resolve_expr(self, ex);
}
fn visit_local(&mut self, l: &'tcx Local<'tcx>) {
- resolve_local(self, Some(&l.pat), l.init, l.els)
+ resolve_local(self, Some(&l.pat), l.init)
}
}
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index d0334cd0d..a23575004 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1,5 +1,5 @@
-use crate::check::regionck::OutlivesEnvironmentExt;
use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter};
+use hir::def::DefKind;
use rustc_ast as ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
@@ -10,22 +10,21 @@ use rustc_hir::ItemKind;
use rustc_infer::infer::outlives::env::{OutlivesEnvironment, RegionBoundPairs};
use rustc_infer::infer::outlives::obligations::TypeOutlives;
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
-use rustc_infer::traits::Normalized;
+use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst};
use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::{
self, AdtKind, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable,
TypeSuperVisitable, TypeVisitable, TypeVisitor,
};
+use rustc_middle::ty::{GenericArgKind, InternalSubsts};
use rustc_session::parse::feature_err;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::autoderef::Autoderef;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_trait_selection::traits::query::normalize::AtExt;
-use rustc_trait_selection::traits::query::NoSolution;
use rustc_trait_selection::traits::{
self, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc,
};
@@ -72,9 +71,11 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
) {
let cause =
traits::ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc));
+ // for a type to be WF, we do not need to check if const trait predicates satisfy.
+ let param_env = self.param_env.without_const();
self.ocx.register_obligation(traits::Obligation::new(
cause,
- self.param_env,
+ param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx()),
));
}
@@ -86,31 +87,35 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
body_def_id: LocalDefId,
f: F,
) where
- F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>) -> FxHashSet<Ty<'tcx>>,
+ F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>),
{
let param_env = tcx.param_env(body_def_id);
let body_id = tcx.hir().local_def_id_to_hir_id(body_def_id);
- tcx.infer_ctxt().enter(|ref infcx| {
- let ocx = ObligationCtxt::new(infcx);
- let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env };
+ let infcx = &tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(infcx);
- if !tcx.features().trivial_bounds {
- wfcx.check_false_global_bounds()
- }
- let wf_tys = f(&mut wfcx);
- let errors = wfcx.select_all_or_error();
- if !errors.is_empty() {
- infcx.report_fulfillment_errors(&errors, None, false);
- return;
- }
+ let assumed_wf_types = ocx.assumed_wf_types(param_env, span, body_def_id);
- let mut outlives_environment = OutlivesEnvironment::new(param_env);
- outlives_environment.add_implied_bounds(infcx, wf_tys, body_id);
- infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment);
- })
+ let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env };
+
+ if !tcx.features().trivial_bounds {
+ wfcx.check_false_global_bounds()
+ }
+ f(&mut wfcx);
+ let errors = wfcx.select_all_or_error();
+ if !errors.is_empty() {
+ infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ return;
+ }
+
+ let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types);
+ let outlives_environment =
+ OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds);
+
+ infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment);
}
-fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
+fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) {
let node = tcx.hir().expect_owner(def_id);
match node {
hir::OwnerNode::Crate(_) => {}
@@ -142,10 +147,10 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
/// the types first.
#[instrument(skip(tcx), level = "debug")]
fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
- let def_id = item.def_id;
+ let def_id = item.owner_id.def_id;
debug!(
- ?item.def_id,
+ ?item.owner_id,
item.name = ? tcx.def_path_str(def_id.to_def_id())
);
@@ -169,7 +174,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
// for `T`
hir::ItemKind::Impl(ref impl_) => {
let is_auto = tcx
- .impl_trait_ref(item.def_id)
+ .impl_trait_ref(def_id)
.map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.def_id));
if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) {
let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span);
@@ -205,13 +210,13 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
}
}
hir::ItemKind::Fn(ref sig, ..) => {
- check_item_fn(tcx, item.def_id, item.ident, item.span, sig.decl);
+ check_item_fn(tcx, def_id, item.ident, item.span, sig.decl);
}
hir::ItemKind::Static(ty, ..) => {
- check_item_type(tcx, item.def_id, ty.span, false);
+ check_item_type(tcx, def_id, ty.span, false);
}
hir::ItemKind::Const(ty, ..) => {
- check_item_type(tcx, item.def_id, ty.span, false);
+ check_item_type(tcx, def_id, ty.span, false);
}
hir::ItemKind::Struct(ref struct_def, ref ast_generics) => {
check_type_defn(tcx, item, false, |wfcx| vec![wfcx.non_enum_variant(struct_def)]);
@@ -241,24 +246,24 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
}
fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) {
- let def_id = item.def_id;
+ let def_id = item.owner_id.def_id;
debug!(
- ?item.def_id,
+ ?item.owner_id,
item.name = ? tcx.def_path_str(def_id.to_def_id())
);
match item.kind {
hir::ForeignItemKind::Fn(decl, ..) => {
- check_item_fn(tcx, item.def_id, item.ident, item.span, decl)
+ check_item_fn(tcx, def_id, item.ident, item.span, decl)
}
- hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, item.def_id, ty.span, true),
+ hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, def_id, ty.span, true),
hir::ForeignItemKind::Type => (),
}
}
fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) {
- let def_id = trait_item.def_id;
+ let def_id = trait_item.owner_id.def_id;
let (method_sig, span) = match trait_item.kind {
hir::TraitItemKind::Fn(ref sig, _) => (Some(sig), trait_item.span),
@@ -266,11 +271,11 @@ fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) {
_ => (None, trait_item.span),
};
check_object_unsafe_self_trait_by_name(tcx, trait_item);
- check_associated_item(tcx, trait_item.def_id, span, method_sig);
+ check_associated_item(tcx, def_id, span, method_sig);
let encl_trait_def_id = tcx.local_parent(def_id);
let encl_trait = tcx.hir().expect_item(encl_trait_def_id);
- let encl_trait_def_id = encl_trait.def_id.to_def_id();
+ let encl_trait_def_id = encl_trait.owner_id.to_def_id();
let fn_lang_item_name = if Some(encl_trait_def_id) == tcx.lang_items().fn_trait() {
Some("fn")
} else if Some(encl_trait_def_id) == tcx.lang_items().fn_mut_trait() {
@@ -343,7 +348,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
loop {
let mut should_continue = false;
for gat_item in associated_items {
- let gat_def_id = gat_item.id.def_id;
+ let gat_def_id = gat_item.id.owner_id;
let gat_item = tcx.associated_item(gat_def_id);
// If this item is not an assoc ty, or has no substs, then it's not a GAT
if gat_item.kind != ty::AssocKind::Type {
@@ -360,7 +365,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
// constrains the GAT with individually.
let mut new_required_bounds: Option<FxHashSet<ty::Predicate<'_>>> = None;
for item in associated_items {
- let item_def_id = item.id.def_id;
+ let item_def_id = item.id.owner_id;
// Skip our own GAT, since it does not constrain itself at all.
if item_def_id == gat_def_id {
continue;
@@ -383,11 +388,11 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
tcx,
param_env,
item_hir_id,
- sig.output(),
+ sig.inputs_and_output,
// We also assume that all of the function signature's parameter types
// are well formed.
&sig.inputs().iter().copied().collect(),
- gat_def_id,
+ gat_def_id.def_id,
gat_generics,
)
}
@@ -410,7 +415,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
.copied()
.collect::<Vec<_>>(),
&FxHashSet::default(),
- gat_def_id,
+ gat_def_id.def_id,
gat_generics,
)
}
@@ -450,7 +455,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
}
for (gat_def_id, required_bounds) in required_bounds_by_item {
- let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id);
+ let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id.def_id);
debug!(?required_bounds);
let param_env = tcx.param_env(gat_def_id);
let gat_hir = gat_item_hir.hir_id();
@@ -658,7 +663,7 @@ fn ty_known_to_outlive<'tcx>(
resolve_regions_with_wf_tys(tcx, id, param_env, &wf_tys, |infcx, region_bound_pairs| {
let origin = infer::RelateParamBound(DUMMY_SP, ty, None);
let outlives = &mut TypeOutlives::new(infcx, tcx, region_bound_pairs, None, param_env);
- outlives.type_must_outlive(origin, ty, region);
+ outlives.type_must_outlive(origin, ty, region, ConstraintCategory::BoringNoLocation);
})
}
@@ -676,7 +681,12 @@ fn region_known_to_outlive<'tcx>(
use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate;
let origin = infer::RelateRegionParamBound(DUMMY_SP);
// `region_a: region_b` -> `region_b <= region_a`
- infcx.push_sub_region_constraint(origin, region_b, region_a);
+ infcx.push_sub_region_constraint(
+ origin,
+ region_b,
+ region_a,
+ ConstraintCategory::BoringNoLocation,
+ );
})
}
@@ -688,26 +698,32 @@ fn resolve_regions_with_wf_tys<'tcx>(
id: hir::HirId,
param_env: ty::ParamEnv<'tcx>,
wf_tys: &FxHashSet<Ty<'tcx>>,
- add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'a, 'tcx>, &'a RegionBoundPairs<'tcx>),
+ add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'tcx>, &'a RegionBoundPairs<'tcx>),
) -> bool {
// Unfortunately, we have to use a new `InferCtxt` each call, because
// region constraints get added and solved there and we need to test each
// call individually.
- tcx.infer_ctxt().enter(|infcx| {
- let mut outlives_environment = OutlivesEnvironment::new(param_env);
- outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id);
- let region_bound_pairs = outlives_environment.region_bound_pairs();
+ let infcx = tcx.infer_ctxt().build();
+ let outlives_environment = OutlivesEnvironment::with_bounds(
+ param_env,
+ Some(&infcx),
+ infcx.implied_bounds_tys(param_env, id, wf_tys.clone()),
+ );
+ let region_bound_pairs = outlives_environment.region_bound_pairs();
- add_constraints(&infcx, region_bound_pairs);
+ add_constraints(&infcx, region_bound_pairs);
- let errors = infcx.resolve_regions(&outlives_environment);
+ infcx.process_registered_region_obligations(
+ outlives_environment.region_bound_pairs(),
+ param_env,
+ );
+ let errors = infcx.resolve_regions(&outlives_environment);
- debug!(?errors, "errors");
+ debug!(?errors, "errors");
- // If we were able to prove that the type outlives the region without
- // an error, it must be because of the implied or explicit bounds...
- errors.is_empty()
- })
+ // If we were able to prove that the type outlives the region without
+ // an error, it must be because of the implied or explicit bounds...
+ errors.is_empty()
}
/// TypeVisitor that looks for uses of GATs like
@@ -761,7 +777,7 @@ impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> {
fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
match ty.kind {
hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments {
- [s] => s.res.and_then(|r| r.opt_def_id()) == Some(trait_def_id.to_def_id()),
+ [s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()),
_ => false,
},
_ => false,
@@ -772,9 +788,9 @@ fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
/// When this is done, suggest using `Self` instead.
fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem<'_>) {
let (trait_name, trait_def_id) =
- match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(item.hir_id())) {
+ match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(item.hir_id()).def_id) {
hir::Node::Item(item) => match item.kind {
- hir::ItemKind::Trait(..) => (item.ident, item.def_id),
+ hir::ItemKind::Trait(..) => (item.ident, item.owner_id),
_ => return,
},
_ => return,
@@ -782,18 +798,18 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem
let mut trait_should_be_self = vec![];
match &item.kind {
hir::TraitItemKind::Const(ty, _) | hir::TraitItemKind::Type(_, Some(ty))
- if could_be_self(trait_def_id, ty) =>
+ if could_be_self(trait_def_id.def_id, ty) =>
{
trait_should_be_self.push(ty.span)
}
hir::TraitItemKind::Fn(sig, _) => {
for ty in sig.decl.inputs {
- if could_be_self(trait_def_id, ty) {
+ if could_be_self(trait_def_id.def_id, ty) {
trait_should_be_self.push(ty.span);
}
}
match sig.decl.output {
- hir::FnRetTy::Return(ty) if could_be_self(trait_def_id, ty) => {
+ hir::FnRetTy::Return(ty) if could_be_self(trait_def_id.def_id, ty) => {
trait_should_be_self.push(ty.span);
}
_ => {}
@@ -822,16 +838,14 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem
}
fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) {
- let def_id = impl_item.def_id;
-
let (method_sig, span) = match impl_item.kind {
hir::ImplItemKind::Fn(ref sig, _) => (Some(sig), impl_item.span),
// Constrain binding and overflow error spans to `<Ty>` in `type foo = <Ty>`.
- hir::ImplItemKind::TyAlias(ty) if ty.span != DUMMY_SP => (None, ty.span),
+ hir::ImplItemKind::Type(ty) if ty.span != DUMMY_SP => (None, ty.span),
_ => (None, impl_item.span),
};
- check_associated_item(tcx, def_id, span, method_sig);
+ check_associated_item(tcx, impl_item.owner_id.def_id, span, method_sig);
}
fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
@@ -965,7 +979,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
}
}
-#[tracing::instrument(level = "debug", skip(tcx, span, sig_if_method))]
+#[instrument(level = "debug", skip(tcx, span, sig_if_method))]
fn check_associated_item(
tcx: TyCtxt<'_>,
item_id: LocalDefId,
@@ -976,15 +990,9 @@ fn check_associated_item(
enter_wf_checking_ctxt(tcx, span, item_id, |wfcx| {
let item = tcx.associated_item(item_id);
- let (mut implied_bounds, self_ty) = match item.container {
- ty::TraitContainer => (FxHashSet::default(), tcx.types.self_param),
- ty::ImplContainer => {
- let def_id = item.container_id(tcx);
- (
- impl_implied_bounds(tcx, wfcx.param_env, def_id.expect_local(), span),
- tcx.type_of(def_id),
- )
- }
+ let self_ty = match item.container {
+ ty::TraitContainer => tcx.types.self_param,
+ ty::ImplContainer => tcx.type_of(item.container_id(tcx)),
};
match item.kind {
@@ -1002,7 +1010,6 @@ fn check_associated_item(
sig,
hir_sig.decl,
item.def_id.expect_local(),
- &mut implied_bounds,
);
check_method_receiver(wfcx, hir_sig, item, self_ty);
}
@@ -1017,8 +1024,6 @@ fn check_associated_item(
}
}
}
-
- implied_bounds
})
}
@@ -1040,9 +1045,11 @@ fn check_type_defn<'tcx, F>(
) where
F: FnMut(&WfCheckingCtxt<'_, 'tcx>) -> Vec<AdtVariant<'tcx>>,
{
- enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| {
+ let _ = tcx.representability(item.owner_id.def_id);
+
+ enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| {
let variants = lookup_fields(wfcx);
- let packed = tcx.adt_def(item.def_id).repr().packed();
+ let packed = tcx.adt_def(item.owner_id).repr().packed();
for variant in &variants {
// All field types must be well-formed.
@@ -1066,7 +1073,7 @@ fn check_type_defn<'tcx, F>(
// Just treat unresolved type expression as if it needs drop.
true
} else {
- ty.needs_drop(tcx, tcx.param_env(item.def_id))
+ ty.needs_drop(tcx, tcx.param_env(item.owner_id))
}
}
};
@@ -1098,8 +1105,6 @@ fn check_type_defn<'tcx, F>(
// Explicit `enum` discriminant values must const-evaluate successfully.
if let Some(discr_def_id) = variant.explicit_discr {
- let discr_substs = InternalSubsts::identity_for_item(tcx, discr_def_id.to_def_id());
-
let cause = traits::ObligationCause::new(
tcx.def_span(discr_def_id),
wfcx.body_id,
@@ -1108,31 +1113,28 @@ fn check_type_defn<'tcx, F>(
wfcx.register_obligation(traits::Obligation::new(
cause,
wfcx.param_env,
- ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ty::Unevaluated::new(
- ty::WithOptConstParam::unknown(discr_def_id.to_def_id()),
- discr_substs,
- )))
+ ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(
+ ty::Const::from_anon_const(tcx, discr_def_id),
+ ))
.to_predicate(tcx),
));
}
}
- check_where_clauses(wfcx, item.span, item.def_id);
-
- // No implied bounds in a struct definition.
- FxHashSet::default()
+ check_where_clauses(wfcx, item.span, item.owner_id.def_id);
});
}
#[instrument(skip(tcx, item))]
fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
- debug!(?item.def_id);
+ debug!(?item.owner_id);
- let trait_def = tcx.trait_def(item.def_id);
+ let def_id = item.owner_id.def_id;
+ let trait_def = tcx.trait_def(def_id);
if trait_def.is_marker
|| matches!(trait_def.specialization_kind, TraitSpecializationKind::Marker)
{
- for associated_def_id in &*tcx.associated_item_def_ids(item.def_id) {
+ for associated_def_id in &*tcx.associated_item_def_ids(def_id) {
struct_span_err!(
tcx.sess,
tcx.def_span(*associated_def_id),
@@ -1143,10 +1145,8 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
}
}
- enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| {
- check_where_clauses(wfcx, item.span, item.def_id);
-
- FxHashSet::default()
+ enter_wf_checking_ctxt(tcx, item.span, def_id, |wfcx| {
+ check_where_clauses(wfcx, item.span, def_id)
});
// Only check traits, don't check trait aliases
@@ -1186,9 +1186,7 @@ fn check_item_fn(
) {
enter_wf_checking_ctxt(tcx, span, def_id, |wfcx| {
let sig = tcx.fn_sig(def_id);
- let mut implied_bounds = FxHashSet::default();
- check_fn_or_method(wfcx, ident.span, sig, decl, def_id, &mut implied_bounds);
- implied_bounds
+ check_fn_or_method(wfcx, ident.span, sig, decl, def_id);
})
}
@@ -1231,13 +1229,10 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_fo
tcx.require_lang_item(LangItem::Sync, Some(ty_span)),
);
}
-
- // No implied bounds in a const, etc.
- FxHashSet::default()
});
}
-#[tracing::instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))]
+#[instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))]
fn check_impl<'tcx>(
tcx: TyCtxt<'tcx>,
item: &'tcx hir::Item<'tcx>,
@@ -1245,13 +1240,13 @@ fn check_impl<'tcx>(
ast_trait_ref: &Option<hir::TraitRef<'_>>,
constness: hir::Constness,
) {
- enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| {
+ enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| {
match *ast_trait_ref {
Some(ref ast_trait_ref) => {
// `#[rustc_reservation_impl]` impls are not real impls and
// therefore don't need to be WF (the trait's `Self: Trait` predicate
// won't hold).
- let trait_ref = tcx.impl_trait_ref(item.def_id).unwrap();
+ let trait_ref = tcx.impl_trait_ref(item.owner_id).unwrap();
let trait_ref = wfcx.normalize(ast_trait_ref.path.span, None, trait_ref);
let trait_pred = ty::TraitPredicate {
trait_ref,
@@ -1273,19 +1268,21 @@ fn check_impl<'tcx>(
wfcx.register_obligations(obligations);
}
None => {
- let self_ty = tcx.type_of(item.def_id);
- let self_ty = wfcx.normalize(item.span, None, self_ty);
+ let self_ty = tcx.type_of(item.owner_id);
+ let self_ty = wfcx.normalize(
+ item.span,
+ Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)),
+ self_ty,
+ );
wfcx.register_wf_obligation(
ast_self_ty.span,
- Some(WellFormedLoc::Ty(item.hir_id().expect_owner())),
+ Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)),
self_ty.into(),
);
}
}
- check_where_clauses(wfcx, item.span, item.def_id);
-
- impl_implied_bounds(tcx, wfcx.param_env, item.def_id, item.span)
+ check_where_clauses(wfcx, item.span, item.owner_id.def_id);
});
}
@@ -1321,7 +1318,11 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
// parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
// be sure if it will error or not as user might always specify the other.
if !ty.needs_subst() {
- wfcx.register_wf_obligation(tcx.def_span(param.def_id), None, ty.into());
+ wfcx.register_wf_obligation(
+ tcx.def_span(param.def_id),
+ Some(WellFormedLoc::Ty(param.def_id.expect_local())),
+ ty.into(),
+ );
}
}
}
@@ -1426,9 +1427,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
let substituted_pred = predicates.rebind(pred).subst(tcx, substs);
// Don't check non-defaulted params, dependent defaults (including lifetimes)
// or preds with multiple params.
- if substituted_pred.has_param_types_or_consts()
- || param_count.params.len() > 1
- || has_region
+ if substituted_pred.has_non_region_param() || param_count.params.len() > 1 || has_region
{
None
} else if predicates.0.predicates.iter().any(|&(p, _)| p == substituted_pred) {
@@ -1465,21 +1464,26 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
assert_eq!(predicates.predicates.len(), predicates.spans.len());
let wf_obligations =
iter::zip(&predicates.predicates, &predicates.spans).flat_map(|(&p, &sp)| {
- traits::wf::predicate_obligations(infcx, wfcx.param_env, wfcx.body_id, p, sp)
+ traits::wf::predicate_obligations(
+ infcx,
+ wfcx.param_env.without_const(),
+ wfcx.body_id,
+ p,
+ sp,
+ )
});
let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect();
wfcx.register_obligations(obligations);
}
-#[tracing::instrument(level = "debug", skip(wfcx, span, hir_decl))]
+#[instrument(level = "debug", skip(wfcx, span, hir_decl))]
fn check_fn_or_method<'tcx>(
wfcx: &WfCheckingCtxt<'_, 'tcx>,
span: Span,
sig: ty::PolyFnSig<'tcx>,
hir_decl: &hir::FnDecl<'_>,
def_id: LocalDefId,
- implied_bounds: &mut FxHashSet<Ty<'tcx>>,
) {
let tcx = wfcx.tcx();
let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig);
@@ -1521,23 +1525,66 @@ fn check_fn_or_method<'tcx>(
);
}
- implied_bounds.extend(sig.inputs());
-
- wfcx.register_wf_obligation(hir_decl.output.span(), None, sig.output().into());
+ wfcx.register_wf_obligation(
+ hir_decl.output.span(),
+ Some(WellFormedLoc::Param {
+ function: def_id,
+ param_idx: sig.inputs().len().try_into().unwrap(),
+ }),
+ sig.output().into(),
+ );
- // FIXME(#27579) return types should not be implied bounds
- implied_bounds.insert(sig.output());
+ check_where_clauses(wfcx, span, def_id);
- debug!(?implied_bounds);
+ check_return_position_impl_trait_in_trait_bounds(
+ tcx,
+ wfcx,
+ def_id,
+ sig.output(),
+ hir_decl.output.span(),
+ );
+}
- check_where_clauses(wfcx, span, def_id);
+/// Basically `check_associated_type_bounds`, but separated for now and should be
+/// deduplicated when RPITITs get lowered into real associated items.
+fn check_return_position_impl_trait_in_trait_bounds<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ wfcx: &WfCheckingCtxt<'_, 'tcx>,
+ fn_def_id: LocalDefId,
+ fn_output: Ty<'tcx>,
+ span: Span,
+) {
+ if let Some(assoc_item) = tcx.opt_associated_item(fn_def_id.to_def_id())
+ && assoc_item.container == ty::AssocItemContainer::TraitContainer
+ {
+ for arg in fn_output.walk() {
+ if let ty::GenericArgKind::Type(ty) = arg.unpack()
+ && let ty::Projection(proj) = ty.kind()
+ && tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder
+ && tcx.impl_trait_in_trait_parent(proj.item_def_id) == fn_def_id.to_def_id()
+ {
+ let bounds = wfcx.tcx().explicit_item_bounds(proj.item_def_id);
+ let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| {
+ let normalized_bound = wfcx.normalize(span, None, bound);
+ traits::wf::predicate_obligations(
+ wfcx.infcx,
+ wfcx.param_env,
+ wfcx.body_id,
+ normalized_bound,
+ bound_span,
+ )
+ });
+ wfcx.register_obligations(wf_obligations);
+ }
+ }
+ }
}
const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, \
`self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one \
of the previous types except `Self`)";
-#[tracing::instrument(level = "debug", skip(wfcx))]
+#[instrument(level = "debug", skip(wfcx))]
fn check_method_receiver<'tcx>(
wfcx: &WfCheckingCtxt<'_, 'tcx>,
fn_sig: &hir::FnSig<'_>,
@@ -1629,7 +1676,7 @@ fn receiver_is_valid<'tcx>(
// `self: Self` is always valid.
if can_eq_self(receiver_ty) {
if let Err(err) = wfcx.equate_types(&cause, wfcx.param_env, self_ty, receiver_ty) {
- infcx.report_mismatched_types(&cause, self_ty, receiver_ty, err).emit();
+ infcx.err_ctxt().report_mismatched_types(&cause, self_ty, receiver_ty, err).emit();
}
return true;
}
@@ -1661,7 +1708,10 @@ fn receiver_is_valid<'tcx>(
if let Err(err) =
wfcx.equate_types(&cause, wfcx.param_env, self_ty, potential_self_ty)
{
- infcx.report_mismatched_types(&cause, self_ty, potential_self_ty, err).emit();
+ infcx
+ .err_ctxt()
+ .report_mismatched_types(&cause, self_ty, potential_self_ty, err)
+ .emit();
}
break;
@@ -1728,14 +1778,14 @@ fn check_variances_for_type_defn<'tcx>(
item: &hir::Item<'tcx>,
hir_generics: &hir::Generics<'_>,
) {
- let ty = tcx.type_of(item.def_id);
+ let ty = tcx.type_of(item.owner_id);
if tcx.has_error_field(ty) {
return;
}
- let ty_predicates = tcx.predicates_of(item.def_id);
+ let ty_predicates = tcx.predicates_of(item.owner_id);
assert_eq!(ty_predicates.parent, None);
- let variances = tcx.variances_of(item.def_id);
+ let variances = tcx.variances_of(item.owner_id);
let mut constrained_parameters: FxHashSet<_> = variances
.iter()
@@ -1748,7 +1798,7 @@ fn check_variances_for_type_defn<'tcx>(
// Lazily calculated because it is only needed in case of an error.
let explicitly_bounded_params = LazyCell::new(|| {
- let icx = crate::collect::ItemCtxt::new(tcx, item.def_id.to_def_id());
+ let icx = crate::collect::ItemCtxt::new(tcx, item.owner_id.to_def_id());
hir_generics
.predicates
.iter()
@@ -1817,6 +1867,7 @@ fn report_bivariance(
impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
/// Feature gates RFC 2056 -- trivial bounds, checking for global bounds that
/// aren't true.
+ #[instrument(level = "debug", skip(self))]
fn check_false_global_bounds(&mut self) {
let tcx = self.ocx.infcx.tcx;
let mut span = self.span;
@@ -1868,10 +1919,10 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalDefId) {
let items = tcx.hir_module_items(module);
- items.par_items(|item| tcx.ensure().check_well_formed(item.def_id));
- items.par_impl_items(|item| tcx.ensure().check_well_formed(item.def_id));
- items.par_trait_items(|item| tcx.ensure().check_well_formed(item.def_id));
- items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.def_id));
+ items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id));
+ items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id));
+ items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id));
+ items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id));
}
///////////////////////////////////////////////////////////////////////////
@@ -1924,40 +1975,6 @@ impl<'a, 'tcx> WfCheckingCtxt<'a, 'tcx> {
}
}
-pub fn impl_implied_bounds<'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- impl_def_id: LocalDefId,
- span: Span,
-) -> FxHashSet<Ty<'tcx>> {
- // We completely ignore any obligations caused by normalizing the types
- // we assume to be well formed. Considering that the user of the implied
- // bounds will also normalize them, we leave it to them to emit errors
- // which should result in better causes and spans.
- tcx.infer_ctxt().enter(|infcx| {
- let cause = ObligationCause::misc(span, tcx.hir().local_def_id_to_hir_id(impl_def_id));
- match tcx.impl_trait_ref(impl_def_id) {
- Some(trait_ref) => {
- // Trait impl: take implied bounds from all types that
- // appear in the trait reference.
- match infcx.at(&cause, param_env).normalize(trait_ref) {
- Ok(Normalized { value, obligations: _ }) => value.substs.types().collect(),
- Err(NoSolution) => FxHashSet::default(),
- }
- }
-
- None => {
- // Inherent impl: take implied bounds from the `self` type.
- let self_ty = tcx.type_of(impl_def_id);
- match infcx.at(&cause, param_env).normalize(self_ty) {
- Ok(Normalized { value, obligations: _ }) => FxHashSet::from_iter([value]),
- Err(NoSolution) => FxHashSet::default(),
- }
- }
- }
- })
-}
-
fn error_392(
tcx: TyCtxt<'_>,
span: Span,
diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
index 52aad636f..2890c149b 100644
--- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
@@ -58,7 +58,7 @@ const ADD_ATTR: &str =
impl<'tcx> InherentCollect<'tcx> {
fn check_def_id(&mut self, item: &hir::Item<'_>, self_ty: Ty<'tcx>, def_id: DefId) {
- let impl_def_id = item.def_id;
+ let impl_def_id = item.owner_id;
if let Some(def_id) = def_id.as_local() {
// Add the implementation to the mapping from implementation to base
// type def ID, if there is a base type for this implementation and
@@ -89,7 +89,7 @@ impl<'tcx> InherentCollect<'tcx> {
for impl_item in items {
if !self
.tcx
- .has_attr(impl_item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl)
+ .has_attr(impl_item.id.owner_id.to_def_id(), sym::rustc_allow_incoherent_impl)
{
struct_span_err!(
self.tcx.sess,
@@ -105,7 +105,7 @@ impl<'tcx> InherentCollect<'tcx> {
}
if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) {
- self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id);
+ self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id.def_id);
} else {
bug!("unexpected self type: {:?}", self_ty);
}
@@ -135,7 +135,7 @@ impl<'tcx> InherentCollect<'tcx> {
for item in items {
if !self
.tcx
- .has_attr(item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl)
+ .has_attr(item.id.owner_id.to_def_id(), sym::rustc_allow_incoherent_impl)
{
struct_span_err!(
self.tcx.sess,
@@ -177,7 +177,7 @@ impl<'tcx> InherentCollect<'tcx> {
}
fn check_item(&mut self, id: hir::ItemId) {
- if !matches!(self.tcx.def_kind(id.def_id), DefKind::Impl) {
+ if !matches!(self.tcx.def_kind(id.owner_id), DefKind::Impl) {
return;
}
@@ -186,7 +186,7 @@ impl<'tcx> InherentCollect<'tcx> {
return;
};
- let self_ty = self.tcx.type_of(item.def_id);
+ let self_ty = self.tcx.type_of(item.owner_id);
match *self_ty.kind() {
ty::Adt(def, _) => {
self.check_def_id(item, self_ty, def.did());
@@ -220,7 +220,9 @@ impl<'tcx> InherentCollect<'tcx> {
| ty::Ref(..)
| ty::Never
| ty::FnPtr(_)
- | ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span),
+ | ty::Tuple(..) => {
+ self.check_primitive_impl(item.owner_id.def_id, self_ty, items, ty.span)
+ }
ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => {
let mut err = struct_span_err!(
self.tcx.sess,
@@ -241,7 +243,7 @@ impl<'tcx> InherentCollect<'tcx> {
| ty::Bound(..)
| ty::Placeholder(_)
| ty::Infer(_) => {
- bug!("unexpected impl self type of impl: {:?} {:?}", item.def_id, self_ty);
+ bug!("unexpected impl self type of impl: {:?} {:?}", item.owner_id, self_ty);
}
ty::Error(_) => {}
}
diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
index 03e076bf5..972769eb1 100644
--- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
@@ -58,6 +58,37 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
== item2.ident(self.tcx).normalize_to_macros_2_0()
}
+ fn check_for_duplicate_items_in_impl(&self, impl_: DefId) {
+ let impl_items = self.tcx.associated_items(impl_);
+
+ let mut seen_items = FxHashMap::default();
+ for impl_item in impl_items.in_definition_order() {
+ let span = self.tcx.def_span(impl_item.def_id);
+ let ident = impl_item.ident(self.tcx);
+
+ let norm_ident = ident.normalize_to_macros_2_0();
+ match seen_items.entry(norm_ident) {
+ Entry::Occupied(entry) => {
+ let former = entry.get();
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0592,
+ "duplicate definitions with name `{}`",
+ ident,
+ );
+ err.span_label(span, format!("duplicate definitions for `{}`", ident));
+ err.span_label(*former, format!("other definition for `{}`", ident));
+
+ err.emit();
+ }
+ Entry::Vacant(entry) => {
+ entry.insert(span);
+ }
+ }
+ }
+ }
+
fn check_for_common_items_in_impls(
&self,
impl1: DefId,
@@ -117,29 +148,22 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
// inherent impls without warning.
SkipLeakCheck::Yes,
overlap_mode,
- |overlap| {
- self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
- false
- },
- || true,
- );
+ )
+ .map_or(true, |overlap| {
+ self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
+ false
+ });
}
fn check_item(&mut self, id: hir::ItemId) {
- let def_kind = self.tcx.def_kind(id.def_id);
+ let def_kind = self.tcx.def_kind(id.owner_id);
if !matches!(def_kind, DefKind::Enum | DefKind::Struct | DefKind::Trait | DefKind::Union) {
return;
}
- let impls = self.tcx.inherent_impls(id.def_id);
+ let impls = self.tcx.inherent_impls(id.owner_id);
- // If there is only one inherent impl block,
- // there is nothing to overlap check it with
- if impls.len() <= 1 {
- return;
- }
-
- let overlap_mode = OverlapMode::get(self.tcx, id.def_id.to_def_id());
+ let overlap_mode = OverlapMode::get(self.tcx, id.owner_id.to_def_id());
let impls_items = impls
.iter()
@@ -152,6 +176,8 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
const ALLOCATING_ALGO_THRESHOLD: usize = 500;
if impls.len() < ALLOCATING_ALGO_THRESHOLD {
for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() {
+ self.check_for_duplicate_items_in_impl(impl1_def_id);
+
for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] {
if self.impls_have_common_items(impl_items1, impl_items2) {
self.check_for_overlapping_inherent_impls(
@@ -290,6 +316,8 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
impl_blocks.sort_unstable();
for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() {
let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx];
+ self.check_for_duplicate_items_in_impl(impl1_def_id);
+
for &impl2_items_idx in impl_blocks[(i + 1)..].iter() {
let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx];
if self.impls_have_common_items(impl_items1, impl_items2) {
diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs
index ae9ebe590..ae9ebe590 100644
--- a/compiler/rustc_typeck/src/coherence/mod.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs
diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index 1608550aa..bb45c3823 100644
--- a/compiler/rustc_typeck/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -2,10 +2,9 @@
//! crate or pertains to a type defined in this crate.
use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::struct_span_err;
+use rustc_errors::{struct_span_err, DelayDm};
use rustc_errors::{Diagnostic, ErrorGuaranteed};
use rustc_hir as hir;
-use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::util::IgnoreRegions;
@@ -43,7 +42,7 @@ fn do_orphan_check_impl<'tcx>(
) -> Result<(), ErrorGuaranteed> {
let trait_def_id = trait_ref.def_id;
- let item = tcx.hir().item(hir::ItemId { def_id });
+ let item = tcx.hir().expect_item(def_id);
let hir::ItemKind::Impl(ref impl_) = item.kind else {
bug!("{:?} is not an impl: {:?}", def_id, item);
};
@@ -102,7 +101,7 @@ fn do_orphan_check_impl<'tcx>(
span_bug!(sp, "opaque type not found, but `has_opaque_types` is set")
}
- match traits::orphan_check(tcx, item.def_id.to_def_id()) {
+ match traits::orphan_check(tcx, item.owner_id.to_def_id()) {
Ok(()) => {}
Err(err) => emit_orphan_check_error(
tcx,
@@ -229,12 +228,8 @@ fn emit_orphan_check_error<'tcx>(
"only traits defined in the current crate {msg}"
);
err.span_label(sp, "impl doesn't use only types from inside the current crate");
- for (ty, is_target_ty) in &tys {
- let mut ty = *ty;
- tcx.infer_ctxt().enter(|infcx| {
- // Remove the lifetimes unnecessary for this error.
- ty = infcx.freshen(ty);
- });
+ for &(mut ty, is_target_ty) in &tys {
+ ty = tcx.erase_regions(ty);
ty = match ty.kind() {
// Remove the type arguments from the output, as they are not relevant.
// You can think of this as the reverse of `resolve_vars_if_possible`.
@@ -264,7 +259,7 @@ fn emit_orphan_check_error<'tcx>(
};
let msg = format!("{} is not defined in the current crate{}", ty, postfix);
- if *is_target_ty {
+ if is_target_ty {
// Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
err.span_label(self_ty_span, &msg);
} else {
@@ -417,30 +412,31 @@ fn lint_auto_trait_impl<'tcx>(
lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS,
tcx.hir().local_def_id_to_hir_id(impl_def_id),
tcx.def_span(impl_def_id),
- |err| {
- let item_span = tcx.def_span(self_type_did);
- let self_descr = tcx.def_kind(self_type_did).descr(self_type_did);
- let mut err = err.build(&format!(
+ DelayDm(|| {
+ format!(
"cross-crate traits with a default impl, like `{}`, \
should not be specialized",
tcx.def_path_str(trait_ref.def_id),
- ));
+ )
+ }),
+ |lint| {
+ let item_span = tcx.def_span(self_type_did);
+ let self_descr = tcx.def_kind(self_type_did).descr(self_type_did);
match arg {
ty::util::NotUniqueParam::DuplicateParam(arg) => {
- err.note(&format!("`{}` is mentioned multiple times", arg));
+ lint.note(&format!("`{}` is mentioned multiple times", arg));
}
ty::util::NotUniqueParam::NotParam(arg) => {
- err.note(&format!("`{}` is not a generic parameter", arg));
+ lint.note(&format!("`{}` is not a generic parameter", arg));
}
}
- err.span_note(
+ lint.span_note(
item_span,
&format!(
"try using the same sequence of generic parameters as the {} definition",
self_descr,
),
- );
- err.emit();
+ )
},
);
}
diff --git a/compiler/rustc_typeck/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index 0d2b75d33..0d34a8bfe 100644
--- a/compiler/rustc_typeck/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -53,20 +53,28 @@ fn associated_type_bounds<'tcx>(
/// impl trait it isn't possible to write a suitable predicate on the
/// containing function and for type-alias impl trait we don't have a backwards
/// compatibility issue.
+#[instrument(level = "trace", skip(tcx), ret)]
fn opaque_type_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
opaque_def_id: DefId,
ast_bounds: &'tcx [hir::GenericBound<'tcx>],
span: Span,
+ in_trait: bool,
) -> &'tcx [(ty::Predicate<'tcx>, Span)] {
ty::print::with_no_queries!({
- let item_ty =
- tcx.mk_opaque(opaque_def_id, InternalSubsts::identity_for_item(tcx, opaque_def_id));
+ let substs = InternalSubsts::identity_for_item(tcx, opaque_def_id);
+ let item_ty = if in_trait {
+ tcx.mk_projection(opaque_def_id, substs)
+ } else {
+ tcx.mk_opaque(opaque_def_id, substs)
+ };
let icx = ItemCtxt::new(tcx, opaque_def_id);
let mut bounds = <dyn AstConv<'_>>::compute_bounds(&icx, item_ty, ast_bounds);
// Opaque types are implicitly sized unless a `?Sized` bound is found
<dyn AstConv<'_>>::add_implicitly_sized(&icx, &mut bounds, ast_bounds, None, span);
+ debug!(?bounds);
+
tcx.arena.alloc_from_iter(bounds.predicates(tcx, item_ty))
})
}
@@ -83,10 +91,10 @@ pub(super) fn explicit_item_bounds(
..
}) => associated_type_bounds(tcx, def_id, bounds, *span),
hir::Node::Item(hir::Item {
- kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }),
+ kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait, .. }),
span,
..
- }) => opaque_type_bounds(tcx, def_id, bounds, *span),
+ }) => opaque_type_bounds(tcx, def_id, bounds, *span, *in_trait),
_ => bug!("item_bounds called on {:?}", def_id),
}
}
diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 534ddfa95..c29a645eb 100644
--- a/compiler/rustc_typeck/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -1,6 +1,5 @@
use rustc_errors::{Applicability, StashKey};
use rustc_hir as hir;
-use rustc_hir::def::Res;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit;
use rustc_hir::intravisit::Visitor;
@@ -19,7 +18,6 @@ use crate::errors::UnconstrainedOpaqueType;
/// Computes the relevant generic parameter for a potential generic const argument.
///
/// This should be called using the query `tcx.opt_const_param_of`.
-#[instrument(level = "debug", skip(tcx))]
pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> {
use hir::*;
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
@@ -67,8 +65,8 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
let ty = item_ctxt.ast_ty_to_ty(hir_ty);
// Iterate through the generics of the projection to find the one that corresponds to
- // the def_id that this query was called with. We filter to only const args here as a
- // precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't
+ // the def_id that this query was called with. We filter to only type and const args here
+ // as a precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't
// but it can't hurt to be safe ^^
if let ty::Projection(projection) = ty.kind() {
let generics = tcx.generics_of(projection.item_def_id);
@@ -79,7 +77,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
args.args
.iter()
.filter(|arg| arg.is_ty_or_const())
- .position(|arg| arg.id() == hir_id)
+ .position(|arg| arg.hir_id() == hir_id)
})
.unwrap_or_else(|| {
bug!("no arg matching AnonConst in segment");
@@ -112,7 +110,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
args.args
.iter()
.filter(|arg| arg.is_ty_or_const())
- .position(|arg| arg.id() == hir_id)
+ .position(|arg| arg.hir_id() == hir_id)
})
.unwrap_or_else(|| {
bug!("no arg matching AnonConst in segment");
@@ -166,7 +164,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
args.args
.iter()
.filter(|arg| arg.is_ty_or_const())
- .position(|arg| arg.id() == hir_id)
+ .position(|arg| arg.hir_id() == hir_id)
.map(|index| (index, seg)).or_else(|| args.bindings
.iter()
.filter_map(TypeBinding::opt_const)
@@ -180,15 +178,12 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
return None;
};
- // Try to use the segment resolution if it is valid, otherwise we
- // default to the path resolution.
- let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res);
- let generics = match tcx.res_generics_def_id(res) {
+ let generics = match tcx.res_generics_def_id(segment.res) {
Some(def_id) => tcx.generics_of(def_id),
None => {
tcx.sess.delay_span_bug(
tcx.def_span(def_id),
- &format!("unexpected anon const res {:?} in path: {:?}", res, path),
+ &format!("unexpected anon const res {:?} in path: {:?}", segment.res, path),
);
return None;
}
@@ -229,7 +224,7 @@ fn get_path_containing_arg_in_pat<'hir>(
.iter()
.filter_map(|seg| seg.args)
.flat_map(|args| args.args)
- .any(|arg| arg.id() == arg_id)
+ .any(|arg| arg.hir_id() == arg_id)
};
let mut arg_path = None;
pat.walk(|pat| match pat.kind {
@@ -289,7 +284,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
icx.to_ty(ty)
}
}
- ImplItemKind::TyAlias(ty) => {
+ ImplItemKind::Type(ty) => {
if tcx.impl_trait_ref(tcx.hir().get_parent_item(hir_id)).is_none() {
check_feature_inherent_assoc_ty(tcx, item.span);
}
@@ -324,7 +319,15 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
}
}
ItemKind::TyAlias(self_ty, _) => icx.to_ty(self_ty),
- ItemKind::Impl(hir::Impl { self_ty, .. }) => icx.to_ty(*self_ty),
+ ItemKind::Impl(hir::Impl { self_ty, .. }) => {
+ match self_ty.find_self_aliases() {
+ spans if spans.len() > 0 => {
+ tcx.sess.emit_err(crate::errors::SelfInImplSelf { span: spans.into(), note: (), });
+ tcx.ty_error()
+ },
+ _ => icx.to_ty(*self_ty),
+ }
+ },
ItemKind::Fn(..) => {
let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
tcx.mk_fn_def(def_id.to_def_id(), substs)
@@ -338,7 +341,15 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
find_opaque_ty_constraints_for_tait(tcx, def_id)
}
// Opaque types desugared from `impl Trait`.
- ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), .. }) => {
+ ItemKind::OpaqueTy(OpaqueTy {
+ origin:
+ hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner),
+ in_trait,
+ ..
+ }) => {
+ if in_trait {
+ assert!(tcx.impl_defaultness(owner).has_value());
+ }
find_opaque_ty_constraints_for_rpit(tcx, def_id, owner)
}
ItemKind::Trait(..)
@@ -379,7 +390,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
Node::Field(field) => icx.to_ty(field.ty),
- Node::Expr(&Expr { kind: ExprKind::Closure{..}, .. }) => tcx.typeck(def_id).node_type(hir_id),
+ Node::Expr(&Expr { kind: ExprKind::Closure { .. }, .. }) => {
+ tcx.typeck(def_id).node_type(hir_id)
+ }
Node::AnonConst(_) if let Some(param) = tcx.opt_const_param_of(def_id) => {
// We defer to `type_of` of the corresponding parameter
@@ -411,40 +424,93 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
| Node::Item(&Item { kind: ItemKind::GlobalAsm(asm), .. })
if asm.operands.iter().any(|(op, _op_sp)| match op {
hir::InlineAsmOperand::Const { anon_const }
- | hir::InlineAsmOperand::SymFn { anon_const } => anon_const.hir_id == hir_id,
+ | hir::InlineAsmOperand::SymFn { anon_const } => {
+ anon_const.hir_id == hir_id
+ }
_ => false,
}) =>
{
tcx.typeck(def_id).node_type(hir_id)
}
- Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => tcx
- .adt_def(tcx.hir().get_parent_item(hir_id))
- .repr()
- .discr_type()
- .to_ty(tcx),
+ Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => {
+ tcx.adt_def(tcx.hir().get_parent_item(hir_id)).repr().discr_type().to_ty(tcx)
+ }
- Node::TypeBinding(binding @ &TypeBinding { hir_id: binding_id, .. })
- if let Node::TraitRef(trait_ref) = tcx.hir().get(
- tcx.hir().get_parent_node(binding_id)
- ) =>
+ Node::TypeBinding(
+ binding @ &TypeBinding {
+ hir_id: binding_id,
+ kind: TypeBindingKind::Equality { term: Term::Const(ref e) },
+ ..
+ },
+ ) if let Node::TraitRef(trait_ref) =
+ tcx.hir().get(tcx.hir().get_parent_node(binding_id))
+ && e.hir_id == hir_id =>
{
- let Some(trait_def_id) = trait_ref.trait_def_id() else {
- return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait");
- };
- let assoc_items = tcx.associated_items(trait_def_id);
- let assoc_item = assoc_items.find_by_name_and_kind(
- tcx, binding.ident, ty::AssocKind::Const, def_id.to_def_id(),
- );
- if let Some(assoc_item) = assoc_item {
- tcx.type_of(assoc_item.def_id)
- } else {
- // FIXME(associated_const_equality): add a useful error message here.
- tcx.ty_error_with_message(
- DUMMY_SP,
- "Could not find associated const on trait",
- )
- }
+ let Some(trait_def_id) = trait_ref.trait_def_id() else {
+ return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait");
+ };
+ let assoc_items = tcx.associated_items(trait_def_id);
+ let assoc_item = assoc_items.find_by_name_and_kind(
+ tcx,
+ binding.ident,
+ ty::AssocKind::Const,
+ def_id.to_def_id(),
+ );
+ if let Some(assoc_item) = assoc_item {
+ tcx.type_of(assoc_item.def_id)
+ } else {
+ // FIXME(associated_const_equality): add a useful error message here.
+ tcx.ty_error_with_message(
+ DUMMY_SP,
+ "Could not find associated const on trait",
+ )
+ }
+ }
+
+ Node::TypeBinding(
+ binding @ &TypeBinding { hir_id: binding_id, gen_args, ref kind, .. },
+ ) if let Node::TraitRef(trait_ref) =
+ tcx.hir().get(tcx.hir().get_parent_node(binding_id))
+ && let Some((idx, _)) =
+ gen_args.args.iter().enumerate().find(|(_, arg)| {
+ if let GenericArg::Const(ct) = arg {
+ ct.value.hir_id == hir_id
+ } else {
+ false
+ }
+ }) =>
+ {
+ let Some(trait_def_id) = trait_ref.trait_def_id() else {
+ return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait");
+ };
+ let assoc_items = tcx.associated_items(trait_def_id);
+ let assoc_item = assoc_items.find_by_name_and_kind(
+ tcx,
+ binding.ident,
+ match kind {
+ // I think `<A: T>` type bindings requires that `A` is a type
+ TypeBindingKind::Constraint { .. }
+ | TypeBindingKind::Equality { term: Term::Ty(..) } => {
+ ty::AssocKind::Type
+ }
+ TypeBindingKind::Equality { term: Term::Const(..) } => {
+ ty::AssocKind::Const
+ }
+ },
+ def_id.to_def_id(),
+ );
+ if let Some(param)
+ = assoc_item.map(|item| &tcx.generics_of(item.def_id).params[idx]).filter(|param| param.kind.is_ty_or_const())
+ {
+ tcx.type_of(param.def_id)
+ } else {
+ // FIXME(associated_const_equality): add a useful error message here.
+ tcx.ty_error_with_message(
+ DUMMY_SP,
+ "Could not find associated const on trait",
+ )
+ }
}
Node::GenericParam(&GenericParam {
@@ -453,8 +519,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
..
}) if ct.hir_id == hir_id => tcx.type_of(tcx.hir().local_def_id(param_hir_id)),
- x =>
- tcx.ty_error_with_message(
+ x => tcx.ty_error_with_message(
DUMMY_SP,
&format!("unexpected const parent in type_of(): {x:?}"),
),
@@ -508,6 +573,11 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
/// checked against it (we also carry the span of that first
/// type).
found: Option<ty::OpaqueHiddenType<'tcx>>,
+
+ /// In the presence of dead code, typeck may figure out a hidden type
+ /// while borrowck will now. We collect these cases here and check at
+ /// the end that we actually found a type that matches (modulo regions).
+ typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>,
}
impl ConstraintLocator<'_> {
@@ -534,18 +604,23 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error() });
return;
}
- if !tables.concrete_opaque_types.contains_key(&self.def_id) {
+ let Some(&typeck_hidden_ty) = tables.concrete_opaque_types.get(&self.def_id) else {
debug!("no constraints in typeck results");
return;
+ };
+ if self.typeck_types.iter().all(|prev| prev.ty != typeck_hidden_ty.ty) {
+ self.typeck_types.push(typeck_hidden_ty);
}
+
// Use borrowck to get the type with unerased regions.
let concrete_opaque_types = &self.tcx.mir_borrowck(item_def_id).concrete_opaque_types;
debug!(?concrete_opaque_types);
if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) {
debug!(?concrete_type, "found constraint");
- if let Some(prev) = self.found {
- if concrete_type.ty != prev.ty && !(concrete_type, prev).references_error() {
+ if let Some(prev) = &mut self.found {
+ if concrete_type.ty != prev.ty && !(concrete_type, prev.ty).references_error() {
prev.report_mismatch(&concrete_type, self.tcx);
+ prev.ty = self.tcx.ty_error();
}
} else {
self.found = Some(concrete_type);
@@ -568,31 +643,31 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
intravisit::walk_expr(self, ex);
}
fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
- trace!(?it.def_id);
+ trace!(?it.owner_id);
// The opaque type itself or its children are not within its reveal scope.
- if it.def_id != self.def_id {
- self.check(it.def_id);
+ if it.owner_id.def_id != self.def_id {
+ self.check(it.owner_id.def_id);
intravisit::walk_item(self, it);
}
}
fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
- trace!(?it.def_id);
+ trace!(?it.owner_id);
// The opaque type itself or its children are not within its reveal scope.
- if it.def_id != self.def_id {
- self.check(it.def_id);
+ if it.owner_id.def_id != self.def_id {
+ self.check(it.owner_id.def_id);
intravisit::walk_impl_item(self, it);
}
}
fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
- trace!(?it.def_id);
- self.check(it.def_id);
+ trace!(?it.owner_id);
+ self.check(it.owner_id.def_id);
intravisit::walk_trait_item(self, it);
}
}
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let scope = tcx.hir().get_defining_scope(hir_id);
- let mut locator = ConstraintLocator { def_id: def_id, tcx, found: None };
+ let mut locator = ConstraintLocator { def_id: def_id, tcx, found: None, typeck_types: vec![] };
debug!(?scope);
@@ -622,16 +697,32 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
}
}
- match locator.found {
- Some(hidden) => hidden.ty,
- None => {
- tcx.sess.emit_err(UnconstrainedOpaqueType {
- span: tcx.def_span(def_id),
- name: tcx.item_name(tcx.local_parent(def_id).to_def_id()),
- });
- tcx.ty_error()
+ let Some(hidden) = locator.found else {
+ tcx.sess.emit_err(UnconstrainedOpaqueType {
+ span: tcx.def_span(def_id),
+ name: tcx.item_name(tcx.local_parent(def_id).to_def_id()),
+ what: match tcx.hir().get(scope) {
+ _ if scope == hir::CRATE_HIR_ID => "module",
+ Node::Item(hir::Item { kind: hir::ItemKind::Mod(_), .. }) => "module",
+ Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => "impl",
+ _ => "item",
+ },
+ });
+ return tcx.ty_error();
+ };
+
+ // Only check against typeck if we didn't already error
+ if !hidden.ty.references_error() {
+ for concrete_type in locator.typeck_types {
+ if tcx.erase_regions(concrete_type.ty) != tcx.erase_regions(hidden.ty)
+ && !(concrete_type, hidden).references_error()
+ {
+ hidden.report_mismatch(&concrete_type, tcx);
+ }
}
}
+
+ hidden.ty
}
fn find_opaque_ty_constraints_for_rpit(
@@ -687,24 +778,24 @@ fn find_opaque_ty_constraints_for_rpit(
intravisit::walk_expr(self, ex);
}
fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
- trace!(?it.def_id);
+ trace!(?it.owner_id);
// The opaque type itself or its children are not within its reveal scope.
- if it.def_id != self.def_id {
- self.check(it.def_id);
+ if it.owner_id.def_id != self.def_id {
+ self.check(it.owner_id.def_id);
intravisit::walk_item(self, it);
}
}
fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
- trace!(?it.def_id);
+ trace!(?it.owner_id);
// The opaque type itself or its children are not within its reveal scope.
- if it.def_id != self.def_id {
- self.check(it.def_id);
+ if it.owner_id.def_id != self.def_id {
+ self.check(it.owner_id.def_id);
intravisit::walk_impl_item(self, it);
}
}
fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
- trace!(?it.def_id);
- self.check(it.def_id);
+ trace!(?it.owner_id);
+ self.check(it.owner_id.def_id);
intravisit::walk_trait_item(self, it);
}
}
@@ -732,20 +823,15 @@ fn find_opaque_ty_constraints_for_rpit(
// the `concrete_opaque_types` table.
tcx.ty_error()
} else {
- table
- .concrete_opaque_types
- .get(&def_id)
- .copied()
- .unwrap_or_else(|| {
- // We failed to resolve the opaque type or it
- // resolves to itself. We interpret this as the
- // no values of the hidden type ever being constructed,
- // so we can just make the hidden type be `!`.
- // For backwards compatibility reasons, we fall back to
- // `()` until we the diverging default is changed.
- Some(tcx.mk_diverging_default())
- })
- .expect("RPIT always have a hidden type from typeck")
+ table.concrete_opaque_types.get(&def_id).map(|ty| ty.ty).unwrap_or_else(|| {
+ // We failed to resolve the opaque type or it
+ // resolves to itself. We interpret this as the
+ // no values of the hidden type ever being constructed,
+ // so we can just make the hidden type be `!`.
+ // For backwards compatibility reasons, we fall back to
+ // `()` until we the diverging default is changed.
+ tcx.mk_diverging_default()
+ })
}
})
}
@@ -801,6 +887,9 @@ fn infer_placeholder_type<'a>(
match tcx.sess.diagnostic().steal_diagnostic(span, StashKey::ItemNoType) {
Some(mut err) => {
if !ty.references_error() {
+ // Only suggest adding `:` if it was missing (and suggested by parsing diagnostic)
+ let colon = if span == item_ident.span.shrink_to_hi() { ":" } else { "" };
+
// The parser provided a sub-optimal `HasPlaceholders` suggestion for the type.
// We are typeck and have the real type, so remove that and suggest the actual type.
// FIXME(eddyb) this looks like it should be functionality on `Diagnostic`.
@@ -816,7 +905,7 @@ fn infer_placeholder_type<'a>(
err.span_suggestion(
span,
&format!("provide a type for the {item}", item = kind),
- format!("{}: {}", item_ident, sugg_ty),
+ format!("{colon} {sugg_ty}"),
Applicability::MachineApplicable,
);
} else {
diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
index 8428e4664..213b89fc7 100644
--- a/compiler/rustc_typeck/src/constrained_generic_params.rs
+++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
@@ -114,9 +114,9 @@ pub fn identify_constrained_generic_params<'tcx>(
/// ```
/// The impl's predicates are collected from left to right. Ignoring
/// the implicit `Sized` bounds, these are
-/// * T: Debug
-/// * U: Iterator
-/// * <U as Iterator>::Item = T -- a desugared ProjectionPredicate
+/// * `T: Debug`
+/// * `U: Iterator`
+/// * `<U as Iterator>::Item = T` -- a desugared ProjectionPredicate
///
/// When we, for example, try to go over the trait-reference
/// `IntoIter<u32> as Trait`, we substitute the impl parameters with fresh
@@ -132,12 +132,16 @@ pub fn identify_constrained_generic_params<'tcx>(
///
/// We *do* have to be somewhat careful when projection targets contain
/// projections themselves, for example in
+///
+/// ```ignore (illustrative)
/// impl<S,U,V,W> Trait for U where
/// /* 0 */ S: Iterator<Item = U>,
/// /* - */ U: Iterator,
/// /* 1 */ <U as Iterator>::Item: ToOwned<Owned=(W,<V as Iterator>::Item)>
/// /* 2 */ W: Iterator<Item = V>
/// /* 3 */ V: Debug
+/// ```
+///
/// we have to evaluate the projections in the order I wrote them:
/// `V: Debug` requires `V` to be evaluated. The only projection that
/// *determines* `V` is 2 (1 contains it, but *does not determine it*,
diff --git a/compiler/rustc_typeck/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index 55c7a15f9..b0fdfcf38 100644
--- a/compiler/rustc_typeck/src/hir_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
@@ -3,11 +3,10 @@ use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{ForeignItem, ForeignItemKind, HirId};
use rustc_infer::infer::TyCtxtInferExt;
-use rustc_infer::traits::TraitEngine;
use rustc_infer::traits::{ObligationCause, WellFormedLoc};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Region, ToPredicate, TyCtxt, TypeFoldable, TypeFolder};
-use rustc_trait_selection::traits::{self, TraitEngineExt};
+use rustc_trait_selection::traits;
pub fn provide(providers: &mut Providers) {
*providers = Providers { diagnostic_hir_wf_check, ..*providers };
@@ -65,41 +64,36 @@ fn diagnostic_hir_wf_check<'tcx>(
impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
- self.tcx.infer_ctxt().enter(|infcx| {
- let mut fulfill = <dyn TraitEngine<'tcx>>::new(self.tcx);
- let tcx_ty =
- self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx });
- let cause = traits::ObligationCause::new(
- ty.span,
- self.hir_id,
- traits::ObligationCauseCode::WellFormed(None),
- );
- fulfill.register_predicate_obligation(
- &infcx,
- traits::Obligation::new(
- cause,
- self.param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(tcx_ty.into()))
- .to_predicate(self.tcx),
- ),
- );
-
- let errors = fulfill.select_all_or_error(&infcx);
- if !errors.is_empty() {
- debug!("Wf-check got errors for {:?}: {:?}", ty, errors);
- for error in errors {
- if error.obligation.predicate == self.predicate {
- // Save the cause from the greatest depth - this corresponds
- // to picking more-specific types (e.g. `MyStruct<u8>`)
- // over less-specific types (e.g. `Option<MyStruct<u8>>`)
- if self.depth >= self.cause_depth {
- self.cause = Some(error.obligation.cause);
- self.cause_depth = self.depth
- }
+ let infcx = self.tcx.infer_ctxt().build();
+ let tcx_ty = self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx });
+ let cause = traits::ObligationCause::new(
+ ty.span,
+ self.hir_id,
+ traits::ObligationCauseCode::WellFormed(None),
+ );
+ let errors = traits::fully_solve_obligation(
+ &infcx,
+ traits::Obligation::new(
+ cause,
+ self.param_env,
+ ty::Binder::dummy(ty::PredicateKind::WellFormed(tcx_ty.into()))
+ .to_predicate(self.tcx),
+ ),
+ );
+ if !errors.is_empty() {
+ debug!("Wf-check got errors for {:?}: {:?}", ty, errors);
+ for error in errors {
+ if error.obligation.predicate == self.predicate {
+ // Save the cause from the greatest depth - this corresponds
+ // to picking more-specific types (e.g. `MyStruct<u8>`)
+ // over less-specific types (e.g. `Option<MyStruct<u8>>`)
+ if self.depth >= self.cause_depth {
+ self.cause = Some(error.obligation.cause);
+ self.cause_depth = self.depth
}
}
}
- });
+ }
self.depth += 1;
intravisit::walk_ty(self, ty);
self.depth -= 1;
@@ -123,7 +117,7 @@ fn diagnostic_hir_wf_check<'tcx>(
let ty = match loc {
WellFormedLoc::Ty(_) => match hir.get(hir_id) {
hir::Node::ImplItem(item) => match item.kind {
- hir::ImplItemKind::TyAlias(ty) => Some(ty),
+ hir::ImplItemKind::Type(ty) => Some(ty),
hir::ImplItemKind::Const(ty, _) => Some(ty),
ref item => bug!("Unexpected ImplItem {:?}", item),
},
@@ -144,6 +138,10 @@ fn diagnostic_hir_wf_check<'tcx>(
hir::Node::ForeignItem(ForeignItem {
kind: ForeignItemKind::Static(ty, _), ..
}) => Some(*ty),
+ hir::Node::GenericParam(hir::GenericParam {
+ kind: hir::GenericParamKind::Type { default: Some(ty), .. },
+ ..
+ }) => Some(*ty),
ref node => bug!("Unexpected node {:?}", node),
},
WellFormedLoc::Param { function: _, param_idx } => {
diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
index 9fee1eaae..136f61999 100644
--- a/compiler/rustc_typeck/src/impl_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
@@ -11,7 +11,7 @@
use crate::constrained_generic_params as cgp;
use min_specialization::check_min_specialization;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::FxHashSet;
use rustc_errors::struct_span_err;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
@@ -19,8 +19,6 @@ use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
use rustc_span::{Span, Symbol};
-use std::collections::hash_map::Entry::{Occupied, Vacant};
-
mod min_specialization;
/// Checks that all the type/lifetime parameters on an impl also
@@ -57,11 +55,10 @@ fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
let min_specialization = tcx.features().min_specialization;
let module = tcx.hir_module_items(module_def_id);
for id in module.items() {
- if matches!(tcx.def_kind(id.def_id), DefKind::Impl) {
- enforce_impl_params_are_constrained(tcx, id.def_id);
- enforce_impl_items_are_distinct(tcx, id.def_id);
+ if matches!(tcx.def_kind(id.owner_id), DefKind::Impl) {
+ enforce_impl_params_are_constrained(tcx, id.owner_id.def_id);
if min_specialization {
- check_min_specialization(tcx, id.def_id);
+ check_min_specialization(tcx, id.owner_id.def_id);
}
}
}
@@ -194,35 +191,3 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol
}
err.emit();
}
-
-/// Enforce that we do not have two items in an impl with the same name.
-fn enforce_impl_items_are_distinct(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) {
- let mut seen_type_items = FxHashMap::default();
- let mut seen_value_items = FxHashMap::default();
- for &impl_item_ref in tcx.associated_item_def_ids(impl_def_id) {
- let impl_item = tcx.associated_item(impl_item_ref);
- let seen_items = match impl_item.kind {
- ty::AssocKind::Type => &mut seen_type_items,
- _ => &mut seen_value_items,
- };
- let span = tcx.def_span(impl_item_ref);
- let ident = impl_item.ident(tcx);
- match seen_items.entry(ident.normalize_to_macros_2_0()) {
- Occupied(entry) => {
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0201,
- "duplicate definitions with name `{}`:",
- ident
- );
- err.span_label(*entry.get(), format!("previous definition of `{}` here", ident));
- err.span_label(span, "duplicate definition");
- err.emit();
- }
- Vacant(entry) => {
- entry.insert(span);
- }
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index 74abb71a1..e806e9487 100644
--- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -65,27 +65,25 @@
//! cause use after frees with purely safe code in the same way as specializing
//! on traits with methods can.
-use crate::check::regionck::OutlivesEnvironmentExt;
-use crate::check::wfcheck::impl_implied_bounds;
use crate::constrained_generic_params as cgp;
use crate::errors::SubstsOnOverriddenImpl;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
-use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
+use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::specialization_graph::Node;
use rustc_middle::ty::subst::{GenericArg, InternalSubsts, SubstsRef};
use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
use rustc_span::Span;
-use rustc_trait_selection::traits::{self, translate_substs, wf};
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
+use rustc_trait_selection::traits::{self, translate_substs, wf, ObligationCtxt};
pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) {
if let Some(node) = parent_specialization_node(tcx, impl_def_id) {
- tcx.infer_ctxt().enter(|infcx| {
- check_always_applicable(&infcx, impl_def_id, node);
- });
+ check_always_applicable(tcx, impl_def_id, node);
}
}
@@ -105,16 +103,14 @@ fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Opti
}
/// Check that `impl1` is a sound specialization
-fn check_always_applicable(infcx: &InferCtxt<'_, '_>, impl1_def_id: LocalDefId, impl2_node: Node) {
- if let Some((impl1_substs, impl2_substs)) = get_impl_substs(infcx, impl1_def_id, impl2_node) {
+fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node) {
+ if let Some((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) {
let impl2_def_id = impl2_node.def_id();
debug!(
"check_always_applicable(\nimpl1_def_id={:?},\nimpl2_def_id={:?},\nimpl2_substs={:?}\n)",
impl1_def_id, impl2_def_id, impl2_substs
);
- let tcx = infcx.tcx;
-
let parent_substs = if impl2_node.is_from_trait() {
impl2_substs.to_vec()
} else {
@@ -124,7 +120,7 @@ fn check_always_applicable(infcx: &InferCtxt<'_, '_>, impl1_def_id: LocalDefId,
let span = tcx.def_span(impl1_def_id);
check_static_lifetimes(tcx, &parent_substs, span);
check_duplicate_params(tcx, impl1_substs, &parent_substs, span);
- check_predicates(infcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span);
+ check_predicates(tcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span);
}
}
@@ -134,30 +130,37 @@ fn check_always_applicable(infcx: &InferCtxt<'_, '_>, impl1_def_id: LocalDefId,
///
/// Example
///
+/// ```ignore (illustrative)
/// impl<A, B> Foo<A> for B { /* impl2 */ }
/// impl<C> Foo<Vec<C>> for C { /* impl1 */ }
+/// ```
///
/// Would return `S1 = [C]` and `S2 = [Vec<C>, C]`.
fn get_impl_substs<'tcx>(
- infcx: &InferCtxt<'_, 'tcx>,
+ tcx: TyCtxt<'tcx>,
impl1_def_id: LocalDefId,
impl2_node: Node,
) -> Option<(SubstsRef<'tcx>, SubstsRef<'tcx>)> {
- let tcx = infcx.tcx;
+ let infcx = &tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(infcx);
let param_env = tcx.param_env(impl1_def_id);
+ let impl1_hir_id = tcx.hir().local_def_id_to_hir_id(impl1_def_id);
+
+ let assumed_wf_types =
+ ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id);
let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id.to_def_id());
let impl2_substs =
translate_substs(infcx, param_env, impl1_def_id.to_def_id(), impl1_substs, impl2_node);
- let mut outlives_env = OutlivesEnvironment::new(param_env);
- let implied_bounds =
- impl_implied_bounds(infcx.tcx, param_env, impl1_def_id, tcx.def_span(impl1_def_id));
- outlives_env.add_implied_bounds(
- infcx,
- implied_bounds,
- tcx.hir().local_def_id_to_hir_id(impl1_def_id),
- );
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ return None;
+ }
+
+ let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_hir_id, assumed_wf_types);
+ let outlives_env = OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds);
infcx.check_region_obligations_and_report_errors(impl1_def_id, &outlives_env);
let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else {
let span = tcx.def_span(impl1_def_id);
@@ -224,13 +227,17 @@ fn unconstrained_parent_impl_substs<'tcx>(
///
/// For example forbid the following:
///
+/// ```ignore (illustrative)
/// impl<A> Tr for A { }
/// impl<B> Tr for (B, B) { }
+/// ```
///
/// Note that only consider the unconstrained parameters of the base impl:
///
+/// ```ignore (illustrative)
/// impl<S, I: IntoIterator<Item = S>> Tr<S> for I { }
/// impl<T> Tr<T> for Vec<T> { }
+/// ```
///
/// The substs for the parent impl here are `[T, Vec<T>]`, which repeats `T`,
/// but `S` is constrained in the parent impl, so `parent_substs` is only
@@ -255,8 +262,10 @@ fn check_duplicate_params<'tcx>(
///
/// For example forbid the following:
///
+/// ```ignore (illustrative)
/// impl<A> Tr for A { }
/// impl Tr for &'static i32 { }
+/// ```
fn check_static_lifetimes<'tcx>(
tcx: TyCtxt<'tcx>,
parent_substs: &Vec<GenericArg<'tcx>>,
@@ -279,14 +288,13 @@ fn check_static_lifetimes<'tcx>(
/// * a well-formed predicate of a type argument of the trait being implemented,
/// including the `Self`-type.
fn check_predicates<'tcx>(
- infcx: &InferCtxt<'_, 'tcx>,
+ tcx: TyCtxt<'tcx>,
impl1_def_id: LocalDefId,
impl1_substs: SubstsRef<'tcx>,
impl2_node: Node,
impl2_substs: SubstsRef<'tcx>,
span: Span,
) {
- let tcx = infcx.tcx;
let instantiated = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs);
let impl1_predicates: Vec<_> = traits::elaborate_predicates_with_span(
tcx,
@@ -343,19 +351,21 @@ fn check_predicates<'tcx>(
// Include the well-formed predicates of the type parameters of the impl.
for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().substs {
- if let Some(obligations) = wf::obligations(
+ let infcx = &tcx.infer_ctxt().build();
+ let obligations = wf::obligations(
infcx,
tcx.param_env(impl1_def_id),
tcx.hir().local_def_id_to_hir_id(impl1_def_id),
0,
arg,
span,
- ) {
- impl2_predicates.extend(
- traits::elaborate_obligations(tcx, obligations)
- .map(|obligation| obligation.predicate),
- )
- }
+ )
+ .unwrap();
+
+ assert!(!obligations.needs_infer());
+ impl2_predicates.extend(
+ traits::elaborate_obligations(tcx, obligations).map(|obligation| obligation.predicate),
+ )
}
impl2_predicates.extend(
traits::elaborate_predicates_with_span(tcx, always_applicable_traits)
@@ -418,13 +428,10 @@ fn trait_predicate_kind<'tcx>(
predicate: ty::Predicate<'tcx>,
) -> Option<TraitSpecializationKind> {
match predicate.kind().skip_binder() {
- ty::PredicateKind::Trait(ty::TraitPredicate {
- trait_ref,
- constness: ty::BoundConstness::NotConst,
- polarity: _,
- }) => Some(tcx.trait_def(trait_ref.def_id).specialization_kind),
- ty::PredicateKind::Trait(_)
- | ty::PredicateKind::RegionOutlives(_)
+ ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity: _ }) => {
+ Some(tcx.trait_def(trait_ref.def_id).specialization_kind)
+ }
+ ty::PredicateKind::RegionOutlives(_)
| ty::PredicateKind::TypeOutlives(_)
| ty::PredicateKind::Projection(_)
| ty::PredicateKind::WellFormed(_)
diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs
index 7534482cc..7534482cc 100644
--- a/compiler/rustc_typeck/src/outlives/explicit.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs
diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
index 3b779280e..90c6edb65 100644
--- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
@@ -1,8 +1,8 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
-use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt};
+use rustc_middle::ty::{GenericArg, GenericArgKind};
use rustc_span::Span;
use super::explicit::ExplicitPredicatesMap;
@@ -29,7 +29,7 @@ pub(super) fn infer_predicates<'tcx>(
// Visit all the crates and infer predicates
for id in tcx.hir().items() {
- let item_did = id.def_id;
+ let item_did = id.owner_id;
debug!("InferVisitor::visit_item(item={:?})", item_did);
diff --git a/compiler/rustc_typeck/src/structured_errors.rs b/compiler/rustc_hir_analysis/src/structured_errors.rs
index 0b46fce17..0b46fce17 100644
--- a/compiler/rustc_typeck/src/structured_errors.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors.rs
diff --git a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs
index 324df313e..324df313e 100644
--- a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs
diff --git a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs
index bb6088054..bb6088054 100644
--- a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs
diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
index 99729391e..435912464 100644
--- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
@@ -4,7 +4,6 @@ use rustc_errors::{
MultiSpan,
};
use rustc_hir as hir;
-use rustc_middle::hir::map::fn_sig;
use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt};
use rustc_session::Session;
use rustc_span::def_id::DefId;
@@ -292,62 +291,60 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
// Creates lifetime name suggestions from the lifetime parameter names
fn get_lifetime_args_suggestions_from_param_names(
&self,
- path_hir_id: Option<hir::HirId>,
+ path_hir_id: hir::HirId,
num_params_to_take: usize,
) -> String {
debug!(?path_hir_id);
- if let Some(path_hir_id) = path_hir_id {
- let mut ret = Vec::new();
- for (id, node) in self.tcx.hir().parent_iter(path_hir_id) {
- debug!(?id);
- let params = if let Some(generics) = node.generics() {
- generics.params
- } else if let hir::Node::Ty(ty) = node
- && let hir::TyKind::BareFn(bare_fn) = ty.kind
- {
- bare_fn.generic_params
- } else {
- &[]
- };
- ret.extend(params.iter().filter_map(|p| {
- let hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }
- = p.kind
- else { return None };
- let hir::ParamName::Plain(name) = p.name else { return None };
- Some(name.to_string())
- }));
- // Suggest `'static` when in const/static item-like.
- if let hir::Node::Item(hir::Item {
- kind: hir::ItemKind::Static { .. } | hir::ItemKind::Const { .. },
- ..
- })
- | hir::Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Const { .. },
- ..
- })
- | hir::Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Const { .. },
- ..
- })
- | hir::Node::ForeignItem(hir::ForeignItem {
- kind: hir::ForeignItemKind::Static { .. },
- ..
- })
- | hir::Node::AnonConst(..) = node
- {
- ret.extend(
- std::iter::repeat("'static".to_owned())
- .take(num_params_to_take.saturating_sub(ret.len())),
- );
- }
- if ret.len() >= num_params_to_take {
- return ret[..num_params_to_take].join(", ");
- }
- // We cannot refer to lifetimes defined in an outer function.
- if let hir::Node::Item(_) = node {
- break;
- }
+ let mut ret = Vec::new();
+ for (id, node) in self.tcx.hir().parent_iter(path_hir_id) {
+ debug!(?id);
+ let params = if let Some(generics) = node.generics() {
+ generics.params
+ } else if let hir::Node::Ty(ty) = node
+ && let hir::TyKind::BareFn(bare_fn) = ty.kind
+ {
+ bare_fn.generic_params
+ } else {
+ &[]
+ };
+ ret.extend(params.iter().filter_map(|p| {
+ let hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }
+ = p.kind
+ else { return None };
+ let hir::ParamName::Plain(name) = p.name else { return None };
+ Some(name.to_string())
+ }));
+ // Suggest `'static` when in const/static item-like.
+ if let hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::Static { .. } | hir::ItemKind::Const { .. },
+ ..
+ })
+ | hir::Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Const { .. },
+ ..
+ })
+ | hir::Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Const { .. },
+ ..
+ })
+ | hir::Node::ForeignItem(hir::ForeignItem {
+ kind: hir::ForeignItemKind::Static { .. },
+ ..
+ })
+ | hir::Node::AnonConst(..) = node
+ {
+ ret.extend(
+ std::iter::repeat("'static".to_owned())
+ .take(num_params_to_take.saturating_sub(ret.len())),
+ );
+ }
+ if ret.len() >= num_params_to_take {
+ return ret[..num_params_to_take].join(", ");
+ }
+ // We cannot refer to lifetimes defined in an outer function.
+ if let hir::Node::Item(_) = node {
+ break;
}
}
@@ -368,7 +365,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
&self,
num_params_to_take: usize,
) -> String {
- let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(fn_sig);
+ let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(hir::Node::fn_sig);
let is_used_in_input = |def_id| {
fn_sig.map_or(false, |fn_sig| {
fn_sig.decl.inputs.iter().any(|ty| match ty.kind {
@@ -524,6 +521,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
if self.not_enough_args_provided() {
self.suggest_adding_args(err);
} else if self.too_many_args_provided() {
+ self.suggest_moving_args_from_assoc_fn_to_trait(err);
self.suggest_removing_args_or_generics(err);
} else {
unreachable!();
@@ -654,6 +652,144 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
}
}
+ /// Suggests moving redundant argument(s) of an associate function to the
+ /// trait it belongs to.
+ ///
+ /// ```compile_fail
+ /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
+ /// ```
+ fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diagnostic) {
+ let trait_ = match self.tcx.trait_of_item(self.def_id) {
+ Some(def_id) => def_id,
+ None => return,
+ };
+
+ // Skip suggestion when the associated function is itself generic, it is unclear
+ // how to split the provided parameters between those to suggest to the trait and
+ // those to remain on the associated type.
+ let num_assoc_fn_expected_args =
+ self.num_expected_type_or_const_args() + self.num_expected_lifetime_args();
+ if num_assoc_fn_expected_args > 0 {
+ return;
+ }
+
+ let num_assoc_fn_excess_args =
+ self.num_excess_type_or_const_args() + self.num_excess_lifetime_args();
+
+ let trait_generics = self.tcx.generics_of(trait_);
+ let num_trait_generics_except_self =
+ trait_generics.count() - if trait_generics.has_self { 1 } else { 0 };
+
+ let msg = format!(
+ "consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}",
+ these = pluralize!("this", num_assoc_fn_excess_args),
+ s = pluralize!(num_assoc_fn_excess_args),
+ name = self.tcx.item_name(trait_),
+ num = num_trait_generics_except_self,
+ );
+
+ if let Some(parent_node) = self.tcx.hir().find_parent_node(self.path_segment.hir_id)
+ && let Some(parent_node) = self.tcx.hir().find(parent_node)
+ && let hir::Node::Expr(expr) = parent_node {
+ match expr.kind {
+ hir::ExprKind::Path(ref qpath) => {
+ self.suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
+ err,
+ qpath,
+ msg,
+ num_assoc_fn_excess_args,
+ num_trait_generics_except_self
+ )
+ },
+ hir::ExprKind::MethodCall(..) => {
+ self.suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
+ err,
+ trait_,
+ expr,
+ msg,
+ num_assoc_fn_excess_args,
+ num_trait_generics_except_self
+ )
+ },
+ _ => return,
+ }
+ }
+ }
+
+ fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
+ &self,
+ err: &mut Diagnostic,
+ qpath: &'tcx hir::QPath<'tcx>,
+ msg: String,
+ num_assoc_fn_excess_args: usize,
+ num_trait_generics_except_self: usize,
+ ) {
+ if let hir::QPath::Resolved(_, path) = qpath
+ && let Some(trait_path_segment) = path.segments.get(0) {
+ let num_generic_args_supplied_to_trait = trait_path_segment.args().num_generic_params();
+
+ if num_assoc_fn_excess_args == num_trait_generics_except_self - num_generic_args_supplied_to_trait {
+ if let Some(span) = self.gen_args.span_ext()
+ && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+ let sugg = vec![
+ (self.path_segment.ident.span, format!("{}::{}", snippet, self.path_segment.ident)),
+ (span.with_lo(self.path_segment.ident.span.hi()), "".to_owned())
+ ];
+
+ err.multipart_suggestion(
+ msg,
+ sugg,
+ Applicability::MaybeIncorrect
+ );
+ }
+ }
+ }
+ }
+
+ fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
+ &self,
+ err: &mut Diagnostic,
+ trait_def_id: DefId,
+ expr: &'tcx hir::Expr<'tcx>,
+ msg: String,
+ num_assoc_fn_excess_args: usize,
+ num_trait_generics_except_self: usize,
+ ) {
+ let sm = self.tcx.sess.source_map();
+ let hir::ExprKind::MethodCall(_, rcvr, args, _) = expr.kind else { return; };
+ if num_assoc_fn_excess_args != num_trait_generics_except_self {
+ return;
+ }
+ let Some(gen_args) = self.gen_args.span_ext() else { return; };
+ let Ok(generics) = sm.span_to_snippet(gen_args) else { return; };
+ let Ok(rcvr) = sm.span_to_snippet(
+ rcvr.span.find_ancestor_inside(expr.span).unwrap_or(rcvr.span)
+ ) else { return; };
+ let Ok(rest) =
+ (match args {
+ [] => Ok(String::new()),
+ [arg] => sm.span_to_snippet(
+ arg.span.find_ancestor_inside(expr.span).unwrap_or(arg.span),
+ ),
+ [first, .., last] => {
+ let first_span =
+ first.span.find_ancestor_inside(expr.span).unwrap_or(first.span);
+ let last_span =
+ last.span.find_ancestor_inside(expr.span).unwrap_or(last.span);
+ sm.span_to_snippet(first_span.to(last_span))
+ }
+ }) else { return; };
+ let comma = if args.len() > 0 { ", " } else { "" };
+ let trait_path = self.tcx.def_path_str(trait_def_id);
+ let method_name = self.tcx.item_name(self.def_id);
+ err.span_suggestion(
+ expr.span,
+ msg,
+ format!("{trait_path}::{generics}::{method_name}({rcvr}{comma}{rest})"),
+ Applicability::MaybeIncorrect,
+ );
+ }
+
/// Suggests to remove redundant argument(s):
///
/// ```text
@@ -763,16 +899,13 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
// If there is a single unbound associated type and a single excess generic param
// suggest replacing the generic param with the associated type bound
if provided_args_matches_unbound_traits && !unbound_types.is_empty() {
- let mut suggestions = vec![];
let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..];
- for (potential, name) in iter::zip(unused_generics, &unbound_types) {
- if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(potential.span()) {
- suggestions.push((potential.span(), format!("{} = {}", name, snippet)));
- }
- }
+ let suggestions = iter::zip(unused_generics, &unbound_types)
+ .map(|(potential, name)| (potential.span().shrink_to_lo(), format!("{name} = ")))
+ .collect::<Vec<_>>();
if !suggestions.is_empty() {
- err.multipart_suggestion(
+ err.multipart_suggestion_verbose(
&format!(
"replace the generic bound{s} with the associated type{s}",
s = pluralize!(unbound_types.len())
diff --git a/compiler/rustc_typeck/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs
index d79450e1a..eaf0310d5 100644
--- a/compiler/rustc_typeck/src/variance/constraints.rs
+++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs
@@ -257,7 +257,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
self.add_constraints_from_invariant_substs(current, substs, variance);
}
- ty::Dynamic(data, r) => {
+ ty::Dynamic(data, r, _) => {
// The type `Foo<T+'a>` is contravariant w/r/t `'a`:
let contra = self.contravariant(variance);
self.add_constraints_from_region(current, r, contra);
@@ -271,11 +271,11 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
}
for projection in data.projection_bounds() {
- match projection.skip_binder().term {
- ty::Term::Ty(ty) => {
+ match projection.skip_binder().term.unpack() {
+ ty::TermKind::Ty(ty) => {
self.add_constraints_from_ty(current, ty, self.invariant);
}
- ty::Term::Const(c) => {
+ ty::TermKind::Const(c) => {
self.add_constraints_from_const(current, c, self.invariant)
}
}
@@ -411,11 +411,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
// way early-bound regions do, so we skip them here.
}
- ty::ReFree(..)
- | ty::ReVar(..)
- | ty::RePlaceholder(..)
- | ty::ReEmpty(_)
- | ty::ReErased => {
+ ty::ReFree(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReErased => {
// We don't expect to see anything but 'static or bound
// regions when visiting member types or method types.
bug!(
diff --git a/compiler/rustc_typeck/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs
index 82103c5a0..82103c5a0 100644
--- a/compiler/rustc_typeck/src/variance/mod.rs
+++ b/compiler/rustc_hir_analysis/src/variance/mod.rs
diff --git a/compiler/rustc_typeck/src/variance/solve.rs b/compiler/rustc_hir_analysis/src/variance/solve.rs
index 97aca621a..97aca621a 100644
--- a/compiler/rustc_typeck/src/variance/solve.rs
+++ b/compiler/rustc_hir_analysis/src/variance/solve.rs
diff --git a/compiler/rustc_typeck/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs
index 1f763011e..1f763011e 100644
--- a/compiler/rustc_typeck/src/variance/terms.rs
+++ b/compiler/rustc_hir_analysis/src/variance/terms.rs
diff --git a/compiler/rustc_typeck/src/variance/xform.rs b/compiler/rustc_hir_analysis/src/variance/xform.rs
index 027f0859f..027f0859f 100644
--- a/compiler/rustc_typeck/src/variance/xform.rs
+++ b/compiler/rustc_hir_analysis/src/variance/xform.rs
diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index 1b13c98e4..2b15d4dcd 100644
--- a/compiler/rustc_typeck/src/check/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -1,10 +1,10 @@
-use crate::check::coercion::{AsCoercionSite, CoerceMany};
-use crate::check::{Diverges, Expectation, FnCtxt, Needs};
+use crate::coercion::{AsCoercionSite, CoerceMany};
+use crate::{Diverges, Expectation, FnCtxt, Needs};
use rustc_errors::{Applicability, MultiSpan};
use rustc_hir::{self as hir, ExprKind};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::traits::Obligation;
-use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable};
+use rustc_middle::ty::{self, ToPredicate, Ty};
use rustc_span::Span;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::{
@@ -12,7 +12,7 @@ use rustc_trait_selection::traits::{
};
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- #[instrument(skip(self), level = "debug")]
+ #[instrument(skip(self), level = "debug", ret)]
pub fn check_match(
&self,
expr: &'tcx hir::Expr<'tcx>,
@@ -94,7 +94,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let arm_ty = self.check_expr_with_expectation(&arm.body, expected);
all_arms_diverge &= self.diverges.get();
- let opt_suggest_box_span = self.opt_suggest_box_span(arm_ty, orig_expected);
+ let opt_suggest_box_span = prior_arm.and_then(|(_, prior_arm_ty, _)| {
+ self.opt_suggest_box_span(prior_arm_ty, arm_ty, orig_expected)
+ });
let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind {
(Some(blk.hir_id), self.find_block_span(blk))
@@ -135,9 +137,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Some(&arm.body),
arm_ty,
Some(&mut |err| {
- let Some(ret) = self.ret_type_span else {
- return;
- };
+ let Some(ret) = self
+ .tcx
+ .hir()
+ .find_by_def_id(self.body_id.owner.def_id)
+ .and_then(|owner| owner.fn_decl())
+ .map(|decl| decl.output.span())
+ else { return; };
let Expectation::IsLast(stmt) = orig_expected else {
return
};
@@ -210,9 +216,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// We won't diverge unless the scrutinee or all arms diverge.
self.diverges.set(scrut_diverges | all_arms_diverge);
- let match_ty = coercion.complete(self);
- debug!(?match_ty);
- match_ty
+ coercion.complete(self)
}
/// When the previously checked expression (the scrutinee) diverges,
@@ -255,7 +259,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.help("consider adding an `else` block that evaluates to the expected type");
error = true;
},
- ret_reason.is_none(),
+ false,
);
error
}
@@ -468,53 +472,80 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- // When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
- // we check if the different arms would work with boxed trait objects instead and
- // provide a structured suggestion in that case.
+ /// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
+ /// we check if the different arms would work with boxed trait objects instead and
+ /// provide a structured suggestion in that case.
pub(crate) fn opt_suggest_box_span(
&self,
- outer_ty: Ty<'tcx>,
+ first_ty: Ty<'tcx>,
+ second_ty: Ty<'tcx>,
orig_expected: Expectation<'tcx>,
) -> Option<Span> {
+ // FIXME(compiler-errors): This really shouldn't need to be done during the
+ // "good" path of typeck, but here we are.
match orig_expected {
- Expectation::ExpectHasType(expected)
- if self.in_tail_expr
- && self.ret_coercion.as_ref()?.borrow().merged_ty().has_opaque_types()
- && self.can_coerce(outer_ty, expected) =>
- {
- let obligations = self.fulfillment_cx.borrow().pending_obligations();
- let mut suggest_box = !obligations.is_empty();
- for o in obligations {
- match o.predicate.kind().skip_binder() {
- ty::PredicateKind::Trait(t) => {
- let pred =
- ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate {
- trait_ref: ty::TraitRef {
- def_id: t.def_id(),
- substs: self.tcx.mk_substs_trait(outer_ty, &[]),
- },
- constness: t.constness,
- polarity: t.polarity,
- }));
- let obl = Obligation::new(
- o.cause.clone(),
- self.param_env,
- pred.to_predicate(self.tcx),
- );
- suggest_box &= self.predicate_must_hold_modulo_regions(&obl);
- if !suggest_box {
- // We've encountered some obligation that didn't hold, so the
- // return expression can't just be boxed. We don't need to
- // evaluate the rest of the obligations.
- break;
+ Expectation::ExpectHasType(expected) => {
+ let TypeVariableOrigin {
+ span,
+ kind: TypeVariableOriginKind::OpaqueTypeInference(rpit_def_id),
+ ..
+ } = self.type_var_origin(expected)? else { return None; };
+
+ let sig = *self
+ .typeck_results
+ .borrow()
+ .liberated_fn_sigs()
+ .get(hir::HirId::make_owner(self.body_id.owner.def_id))?;
+
+ let substs = sig.output().walk().find_map(|arg| {
+ if let ty::GenericArgKind::Type(ty) = arg.unpack()
+ && let ty::Opaque(def_id, substs) = *ty.kind()
+ && def_id == rpit_def_id
+ {
+ Some(substs)
+ } else {
+ None
+ }
+ })?;
+ let opaque_ty = self.tcx.mk_opaque(rpit_def_id, substs);
+
+ if !self.can_coerce(first_ty, expected) || !self.can_coerce(second_ty, expected) {
+ return None;
+ }
+
+ for ty in [first_ty, second_ty] {
+ for (pred, _) in self
+ .tcx
+ .bound_explicit_item_bounds(rpit_def_id)
+ .subst_iter_copied(self.tcx, substs)
+ {
+ let pred = match pred.kind().skip_binder() {
+ ty::PredicateKind::Trait(mut trait_pred) => {
+ assert_eq!(trait_pred.trait_ref.self_ty(), opaque_ty);
+ trait_pred.trait_ref.substs =
+ self.tcx.mk_substs_trait(ty, &trait_pred.trait_ref.substs[1..]);
+ pred.kind().rebind(trait_pred).to_predicate(self.tcx)
}
+ ty::PredicateKind::Projection(mut proj_pred) => {
+ assert_eq!(proj_pred.projection_ty.self_ty(), opaque_ty);
+ proj_pred.projection_ty.substs = self
+ .tcx
+ .mk_substs_trait(ty, &proj_pred.projection_ty.substs[1..]);
+ pred.kind().rebind(proj_pred).to_predicate(self.tcx)
+ }
+ _ => continue,
+ };
+ if !self.predicate_must_hold_modulo_regions(&Obligation::new(
+ ObligationCause::misc(span, self.body_id),
+ self.param_env,
+ pred,
+ )) {
+ return None;
}
- _ => {}
}
}
- // If all the obligations hold (or there are no obligations) the tail expression
- // we can suggest to return a boxed trait object instead of an opaque type.
- if suggest_box { self.ret_type_span } else { None }
+
+ Some(span)
}
_ => None,
}
diff --git a/compiler/rustc_typeck/src/check/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs
index 59c366ad7..59c366ad7 100644
--- a/compiler/rustc_typeck/src/check/autoderef.rs
+++ b/compiler/rustc_hir_typeck/src/autoderef.rs
diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 7aaddc2bd..d1dab0540 100644
--- a/compiler/rustc_typeck/src/check/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -30,35 +30,38 @@
use super::FnCtxt;
-use crate::hir::def_id::DefId;
use crate::type_error_struct;
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
-use rustc_hir::lang_items::LangItem;
use rustc_middle::mir::Mutability;
use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::cast::{CastKind, CastTy};
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable};
+use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable, VariantDef};
use rustc_session::lint;
use rustc_session::Session;
+use rustc_span::def_id::{DefId, LOCAL_CRATE};
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits;
use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
/// Reifies a cast check to be checked once we have full type information for
/// a function context.
#[derive(Debug)]
pub struct CastCheck<'tcx> {
+ /// The expression whose value is being casted
expr: &'tcx hir::Expr<'tcx>,
+ /// The source type for the cast expression
expr_ty: Ty<'tcx>,
expr_span: Span,
+ /// The target type. That is, the type we are casting to.
cast_ty: Ty<'tcx>,
cast_span: Span,
span: Span,
+ /// whether the cast is made in a const context or not.
+ pub constness: hir::Constness,
}
/// The kind of pointer and associated metadata (thin, length or vtable) - we
@@ -96,13 +99,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return Err(reported);
}
- if self.type_is_known_to_be_sized_modulo_regions(t, span) {
+ if self.type_is_sized_modulo_regions(self.param_env, t, span) {
return Ok(Some(PointerKind::Thin));
}
Ok(match *t.kind() {
ty::Slice(_) | ty::Str => Some(PointerKind::Length),
- ty::Dynamic(ref tty, ..) => Some(PointerKind::VTable(tty.principal_def_id())),
+ ty::Dynamic(ref tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())),
ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() {
None => Some(PointerKind::Thin),
Some(f) => {
@@ -139,6 +142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ty::Generator(..)
| ty::Adt(..)
| ty::Never
+ | ty::Dynamic(_, _, ty::DynStar)
| ty::Error(_) => {
let reported = self
.tcx
@@ -173,6 +177,7 @@ pub enum CastError {
/// or "a length". If this argument is None, then the metadata is unknown, for example,
/// when we're typechecking a type parameter with a ?Sized bound.
IntToFatCast(Option<&'static str>),
+ ForeignNonExhaustiveAdt,
}
impl From<ErrorGuaranteed> for CastError {
@@ -207,15 +212,16 @@ impl<'a, 'tcx> CastCheck<'tcx> {
cast_ty: Ty<'tcx>,
cast_span: Span,
span: Span,
+ constness: hir::Constness,
) -> Result<CastCheck<'tcx>, ErrorGuaranteed> {
let expr_span = expr.span.find_ancestor_inside(span).unwrap_or(expr.span);
- let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span };
+ let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span, constness };
// For better error messages, check for some obviously unsized
// cases now. We do a more thorough check at the end, once
// inference is more completely known.
match cast_ty.kind() {
- ty::Dynamic(..) | ty::Slice(..) => {
+ ty::Dynamic(_, _, ty::Dyn) | ty::Slice(..) => {
let reported = check.report_cast_to_unsized_type(fcx);
Err(reported)
}
@@ -523,7 +529,9 @@ impl<'a, 'tcx> CastCheck<'tcx> {
err.emit();
}
CastError::SizedUnsizedCast => {
- use crate::structured_errors::{SizedUnsizedCast, StructuredDiagnostic};
+ use rustc_hir_analysis::structured_errors::{
+ SizedUnsizedCast, StructuredDiagnostic,
+ };
SizedUnsizedCast {
sess: &fcx.tcx.sess,
@@ -591,6 +599,17 @@ impl<'a, 'tcx> CastCheck<'tcx> {
}
err.emit();
}
+ CastError::ForeignNonExhaustiveAdt => {
+ make_invalid_casting_error(
+ fcx.tcx.sess,
+ self.span,
+ self.expr_ty,
+ self.cast_ty,
+ fcx,
+ )
+ .note("cannot cast an enum with a non-exhaustive variant when it's defined in another crate")
+ .emit();
+ }
}
}
@@ -670,19 +689,25 @@ impl<'a, 'tcx> CastCheck<'tcx> {
} else {
("", lint::builtin::TRIVIAL_CASTS)
};
- fcx.tcx.struct_span_lint_hir(lint, self.expr.hir_id, self.span, |err| {
- err.build(&format!(
- "trivial {}cast: `{}` as `{}`",
- adjective,
- fcx.ty_to_string(t_expr),
- fcx.ty_to_string(t_cast)
- ))
- .help(&format!(
- "cast can be replaced by coercion; this might \
- require {type_asc_or}a temporary variable"
- ))
- .emit();
- });
+ fcx.tcx.struct_span_lint_hir(
+ lint,
+ self.expr.hir_id,
+ self.span,
+ DelayDm(|| {
+ format!(
+ "trivial {}cast: `{}` as `{}`",
+ adjective,
+ fcx.ty_to_string(t_expr),
+ fcx.ty_to_string(t_cast)
+ )
+ }),
+ |lint| {
+ lint.help(format!(
+ "cast can be replaced by coercion; this might \
+ require {type_asc_or}a temporary variable"
+ ))
+ },
+ );
}
#[instrument(skip(fcx), level = "debug")]
@@ -692,7 +717,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty);
- if !fcx.type_is_known_to_be_sized_modulo_regions(self.cast_ty, self.span)
+ if !fcx.type_is_sized_modulo_regions(fcx.param_env, self.cast_ty, self.span)
&& !self.cast_ty.has_infer_types()
{
self.report_cast_to_unsized_type(fcx);
@@ -789,6 +814,14 @@ impl<'a, 'tcx> CastCheck<'tcx> {
_ => return Err(CastError::NonScalar),
};
+ if let ty::Adt(adt_def, _) = *self.expr_ty.kind() {
+ if adt_def.did().krate != LOCAL_CRATE {
+ if adt_def.variants().iter().any(VariantDef::is_field_list_non_exhaustive) {
+ return Err(CastError::ForeignNonExhaustiveAdt);
+ }
+ }
+ }
+
match (t_from, t_cast) {
// These types have invariants! can't cast into them.
(_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar),
@@ -835,6 +868,14 @@ impl<'a, 'tcx> CastCheck<'tcx> {
(Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast),
(Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast),
+
+ (_, DynStar) | (DynStar, _) => {
+ if fcx.tcx.features().dyn_star {
+ bug!("should be handled by `try_coerce`")
+ } else {
+ Err(CastError::IllegalCast)
+ }
+ }
}
}
@@ -976,12 +1017,12 @@ impl<'a, 'tcx> CastCheck<'tcx> {
lint::builtin::CENUM_IMPL_DROP_CAST,
self.expr.hir_id,
self.span,
- |err| {
- err.build(&format!(
- "cannot cast enum `{}` into integer `{}` because it implements `Drop`",
- self.expr_ty, self.cast_ty
- ))
- .emit();
+ DelayDm(|| format!(
+ "cannot cast enum `{}` into integer `{}` because it implements `Drop`",
+ self.expr_ty, self.cast_ty
+ )),
+ |lint| {
+ lint
},
);
}
@@ -992,12 +1033,11 @@ impl<'a, 'tcx> CastCheck<'tcx> {
lint::builtin::LOSSY_PROVENANCE_CASTS,
self.expr.hir_id,
self.span,
- |err| {
- let mut err = err.build(&format!(
+ DelayDm(|| format!(
"under strict provenance it is considered bad style to cast pointer `{}` to integer `{}`",
self.expr_ty, self.cast_ty
- ));
-
+ )),
+ |lint| {
let msg = "use `.addr()` to obtain the address of a pointer";
let expr_prec = self.expr.precedence().order();
@@ -1016,9 +1056,9 @@ impl<'a, 'tcx> CastCheck<'tcx> {
(cast_span, format!(").addr(){scalar_cast}")),
];
- err.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect);
+ lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect);
} else {
- err.span_suggestion(
+ lint.span_suggestion(
cast_span,
msg,
format!(".addr(){scalar_cast}"),
@@ -1026,12 +1066,12 @@ impl<'a, 'tcx> CastCheck<'tcx> {
);
}
- err.help(
+ lint.help(
"if you can't comply with strict provenance and need to expose the pointer \
provenance you can use `.expose_addr()` instead"
);
- err.emit();
+ lint
},
);
}
@@ -1041,32 +1081,25 @@ impl<'a, 'tcx> CastCheck<'tcx> {
lint::builtin::FUZZY_PROVENANCE_CASTS,
self.expr.hir_id,
self.span,
- |err| {
- let mut err = err.build(&format!(
- "strict provenance disallows casting integer `{}` to pointer `{}`",
- self.expr_ty, self.cast_ty
- ));
+ DelayDm(|| format!(
+ "strict provenance disallows casting integer `{}` to pointer `{}`",
+ self.expr_ty, self.cast_ty
+ )),
+ |lint| {
let msg = "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address";
let suggestions = vec![
(self.expr_span.shrink_to_lo(), String::from("(...).with_addr(")),
(self.expr_span.shrink_to_hi().to(self.cast_span), String::from(")")),
];
- err.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect);
- err.help(
+ lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect);
+ lint.help(
"if you can't comply with strict provenance and don't have a pointer with \
the correct provenance you can use `std::ptr::from_exposed_addr()` instead"
);
- err.emit();
+ lint
},
);
}
}
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- fn type_is_known_to_be_sized_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool {
- let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
- traits::type_known_to_meet_bound_modulo_regions(self, self.param_env, ty, lang_item, span)
- }
-}
diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 2ed5f569b..86597a703 100644
--- a/compiler/rustc_typeck/src/check/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -35,13 +35,15 @@
//! // and are then unable to coerce `&7i32` to `&mut i32`.
//! ```
-use crate::astconv::AstConv;
-use crate::check::FnCtxt;
+use crate::FnCtxt;
use rustc_errors::{
- struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+ struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::Expr;
+use rustc_hir_analysis::astconv::AstConv;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{Coercion, InferOk, InferResult};
use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt};
@@ -59,7 +61,7 @@ use rustc_span::symbol::sym;
use rustc_span::{self, BytePos, DesugaringKind, Span};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
use smallvec::{smallvec, SmallVec};
@@ -87,6 +89,19 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {
type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>;
+struct CollectRetsVisitor<'tcx> {
+ ret_exprs: Vec<&'tcx hir::Expr<'tcx>>,
+}
+
+impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> {
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ if let hir::ExprKind::Ret(_) = expr.kind {
+ self.ret_exprs.push(expr);
+ }
+ intravisit::walk_expr(self, expr);
+ }
+}
+
/// Coercing a mutable reference to an immutable works, while
/// coercing `&T` to `&mut T` should be forbidden.
fn coerce_mutbls<'tcx>(
@@ -201,6 +216,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
ty::Ref(r_b, _, mutbl_b) => {
return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b);
}
+ ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star => {
+ return self.coerce_dyn_star(a, b, predicates, region);
+ }
_ => {}
}
@@ -687,7 +705,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// Object safety violations or miscellaneous.
Err(err) => {
- self.report_selection_error(obligation.clone(), &obligation, &err, false);
+ self.err_ctxt().report_selection_error(
+ obligation.clone(),
+ &obligation,
+ &err,
+ false,
+ );
// Treat this like an obligation and follow through
// with the unsizing - the lack of a coercion should
// be silent, as it causes a type mismatch later.
@@ -725,6 +748,63 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
Ok(coercion)
}
+ fn coerce_dyn_star(
+ &self,
+ a: Ty<'tcx>,
+ b: Ty<'tcx>,
+ predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
+ b_region: ty::Region<'tcx>,
+ ) -> CoerceResult<'tcx> {
+ if !self.tcx.features().dyn_star {
+ return Err(TypeError::Mismatch);
+ }
+
+ if let ty::Dynamic(a_data, _, _) = a.kind()
+ && let ty::Dynamic(b_data, _, _) = b.kind()
+ {
+ if a_data.principal_def_id() == b_data.principal_def_id() {
+ return self.unify_and(a, b, |_| vec![]);
+ } else if !self.tcx().features().trait_upcasting {
+ let mut err = feature_err(
+ &self.tcx.sess.parse_sess,
+ sym::trait_upcasting,
+ self.cause.span,
+ &format!(
+ "cannot cast `{a}` to `{b}`, trait upcasting coercion is experimental"
+ ),
+ );
+ err.emit();
+ }
+ }
+
+ // Check the obligations of the cast -- for example, when casting
+ // `usize` to `dyn* Clone + 'static`:
+ let obligations = predicates
+ .iter()
+ .map(|predicate| {
+ // For each existential predicate (e.g., `?Self: Clone`) substitute
+ // the type of the expression (e.g., `usize` in our example above)
+ // and then require that the resulting predicate (e.g., `usize: Clone`)
+ // holds (it does).
+ let predicate = predicate.with_self_ty(self.tcx, a);
+ Obligation::new(self.cause.clone(), self.param_env, predicate)
+ })
+ // Enforce the region bound (e.g., `usize: 'static`, in our example).
+ .chain([Obligation::new(
+ self.cause.clone(),
+ self.param_env,
+ self.tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::TypeOutlives(
+ ty::OutlivesPredicate(a, b_region),
+ ))),
+ )])
+ .collect();
+
+ Ok(InferOk {
+ value: (vec![Adjustment { kind: Adjust::DynStar, target: b }], b),
+ obligations,
+ })
+ }
+
fn coerce_from_safe_fn<F, G>(
&self,
a: Ty<'tcx>,
@@ -1464,23 +1544,29 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
}
}
Err(coercion_error) => {
+ // Mark that we've failed to coerce the types here to suppress
+ // any superfluous errors we might encounter while trying to
+ // emit or provide suggestions on how to fix the initial error.
+ fcx.set_tainted_by_errors();
let (expected, found) = if label_expression_as_expected {
// In the case where this is a "forced unit", like
// `break`, we want to call the `()` "expected"
// since it is implied by the syntax.
// (Note: not all force-units work this way.)"
- (expression_ty, self.final_ty.unwrap_or(self.expected_ty))
+ (expression_ty, self.merged_ty())
} else {
// Otherwise, the "expected" type for error
// reporting is the current unification type,
// which is basically the LUB of the expressions
// we've seen so far (combined with the expected
// type)
- (self.final_ty.unwrap_or(self.expected_ty), expression_ty)
+ (self.merged_ty(), expression_ty)
};
+ let (expected, found) = fcx.resolve_vars_if_possible((expected, found));
let mut err;
let mut unsized_return = false;
+ let mut visitor = CollectRetsVisitor { ret_exprs: vec![] };
match *cause.code() {
ObligationCauseCode::ReturnNoExpression => {
err = struct_span_err!(
@@ -1506,6 +1592,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
if !fcx.tcx.features().unsized_locals {
unsized_return = self.is_return_ty_unsized(fcx, blk_id);
}
+ if let Some(expression) = expression
+ && let hir::ExprKind::Loop(loop_blk, ..) = expression.kind {
+ intravisit::walk_block(& mut visitor, loop_blk);
+ }
}
ObligationCauseCode::ReturnValue(id) => {
err = self.report_return_mismatched_types(
@@ -1524,7 +1614,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
}
}
_ => {
- err = fcx.report_mismatched_types(
+ err = fcx.err_ctxt().report_mismatched_types(
cause,
expected,
found,
@@ -1551,12 +1641,47 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
);
}
+ if visitor.ret_exprs.len() > 0 && let Some(expr) = expression {
+ self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs);
+ }
err.emit_unless(unsized_return);
self.final_ty = Some(fcx.tcx.ty_error());
}
}
}
+ fn note_unreachable_loop_return(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'tcx>,
+ ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>,
+ ) {
+ let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else { return;};
+ let mut span: MultiSpan = vec![loop_span].into();
+ span.push_span_label(loop_span, "this might have zero elements to iterate on");
+ const MAXITER: usize = 3;
+ let iter = ret_exprs.iter().take(MAXITER);
+ for ret_expr in iter {
+ span.push_span_label(
+ ret_expr.span,
+ "if the loop doesn't execute, this value would never get returned",
+ );
+ }
+ err.span_note(
+ span,
+ "the function expects a value to always be returned, but loops might run zero times",
+ );
+ if MAXITER < ret_exprs.len() {
+ err.note(&format!(
+ "if the loop doesn't execute, {} other values would never get returned",
+ ret_exprs.len() - MAXITER
+ ));
+ }
+ err.help(
+ "return a value for the case when the loop has zero elements to iterate on, or \
+ consider changing the return type to account for that possibility",
+ );
+ }
fn report_return_mismatched_types<'a>(
&self,
@@ -1569,7 +1694,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
expression: Option<&'tcx hir::Expr<'tcx>>,
blk_id: Option<hir::HirId>,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
- let mut err = fcx.report_mismatched_types(cause, expected, found, ty_err);
+ let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err);
let mut pointing_at_return_type = false;
let mut fn_output = None;
@@ -1623,7 +1748,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
expected,
found,
can_suggest,
- fcx.tcx.hir().local_def_id_to_hir_id(fcx.tcx.hir().get_parent_item(id)),
+ fcx.tcx.hir().get_parent_item(id).into(),
);
}
if !pointing_at_return_type {
@@ -1632,7 +1757,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
}
let parent_id = fcx.tcx.hir().get_parent_item(id);
- let parent_item = fcx.tcx.hir().get_by_def_id(parent_id);
+ let parent_item = fcx.tcx.hir().get_by_def_id(parent_id.def_id);
if let (Some(expr), Some(_), Some((fn_decl, _, _))) =
(expression, blk_id, fcx.get_node_fn_decl(parent_item))
@@ -1644,13 +1769,34 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
expected,
found,
id,
- fcx.tcx.hir().local_def_id_to_hir_id(parent_id),
+ parent_id.into(),
+ );
+ }
+
+ let ret_coercion_span = fcx.ret_coercion_span.get();
+
+ if let Some(sp) = ret_coercion_span
+ // If the closure has an explicit return type annotation, or if
+ // the closure's return type has been inferred from outside
+ // requirements (such as an Fn* trait bound), then a type error
+ // may occur at the first return expression we see in the closure
+ // (if it conflicts with the declared return type). Skip adding a
+ // note in this case, since it would be incorrect.
+ && !fcx.return_type_pre_known
+ {
+ err.span_note(
+ sp,
+ &format!(
+ "return type inferred to be `{}` here",
+ expected
+ ),
);
}
- if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.get(), fn_output) {
+ if let (Some(sp), Some(fn_output)) = (ret_coercion_span, fn_output) {
self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output);
}
+
err
}
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 4de48dc5b..16febfc46 100644
--- a/compiler/rustc_typeck/src/check/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -1,21 +1,20 @@
-use crate::check::FnCtxt;
-use rustc_infer::infer::InferOk;
-use rustc_middle::middle::stability::EvalResult;
-use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::ObligationCause;
-
+use crate::FnCtxt;
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{is_range_literal, Node};
+use rustc_infer::infer::InferOk;
use rustc_middle::lint::in_external_macro;
+use rustc_middle::middle::stability::EvalResult;
use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{BytePos, Span};
+use rustc_trait_selection::infer::InferCtxtExt as _;
+use rustc_trait_selection::traits::ObligationCause;
use super::method::probe;
@@ -32,20 +31,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
error: Option<TypeError<'tcx>>,
) {
self.annotate_expected_due_to_let_ty(err, expr, error);
- self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr);
- self.suggest_compatible_variants(err, expr, expected, expr_ty);
- self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty);
- if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
- return;
- }
- self.suggest_no_capture_closure(err, expected, expr_ty);
- self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
- self.suggest_missing_parentheses(err, expr);
- self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
+
+ // Use `||` to give these suggestions a precedence
+ let _ = self.suggest_missing_parentheses(err, expr)
+ || self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
+ || self.suggest_compatible_variants(err, expr, expected, expr_ty)
+ || self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty)
+ || self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty)
+ || self.suggest_no_capture_closure(err, expected, expr_ty)
+ || self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty)
+ || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
+ || self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
+ || self.suggest_into(err, expr, expr_ty, expected);
+
self.note_type_is_not_clone(err, expected, expr_ty, expr);
self.note_need_for_fn_pointer(err, expected, expr_ty);
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
- self.report_closure_inferred_return_type(err, expected);
}
// Requires that the two types unify, and prints an error message if
@@ -77,7 +78,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.register_predicates(obligations);
None
}
- Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)),
+ Err(e) => Some(self.err_ctxt().report_mismatched_types(&cause, expected, actual, e)),
}
}
@@ -107,7 +108,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.register_predicates(obligations);
None
}
- Err(e) => Some(self.report_mismatched_types(cause, expected, actual, e)),
+ Err(e) => Some(self.err_ctxt().report_mismatched_types(cause, expected, actual, e)),
}
}
@@ -131,7 +132,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
///
/// N.B., this code relies on `self.diverges` to be accurate. In particular, assignments to `!`
/// will be permitted if the diverges flag is currently "always".
- #[tracing::instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))]
+ #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))]
pub fn demand_coerce_diag(
&self,
expr: &hir::Expr<'tcx>,
@@ -151,7 +152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let expr = expr.peel_drop_temps();
let cause = self.misc(expr.span);
let expr_ty = self.resolve_vars_with_obligations(checked_ty);
- let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e.clone());
+ let mut err = self.err_ctxt().report_mismatched_types(&cause, expected, expr_ty, e.clone());
let is_insufficiently_polymorphic =
matches!(e, TypeError::RegionsInsufficientlyPolymorphic(..));
@@ -286,7 +287,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &hir::Expr<'_>,
expected: Ty<'tcx>,
expr_ty: Ty<'tcx>,
- ) {
+ ) -> bool {
if let ty::Adt(expected_adt, substs) = expected.kind() {
if let hir::ExprKind::Field(base, ident) = expr.kind {
let base_ty = self.typeck_results.borrow().expr_ty(base);
@@ -299,7 +300,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"",
Applicability::MaybeIncorrect,
);
- return
+ return true;
}
}
@@ -338,7 +339,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) {
vec!["None", "Some(())"]
} else {
- return;
+ return false;
};
if let Some(indent) =
self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
@@ -358,7 +359,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MaybeIncorrect,
);
}
- return;
+ return true;
}
}
}
@@ -375,7 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let field_is_local = sole_field.did.is_local();
let field_is_accessible =
- sole_field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx)
+ sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx)
// Skip suggestions for unstable public fields (for example `Pin::pointer`)
&& matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked);
@@ -417,6 +418,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
hir::def::CtorKind::Const => unreachable!(),
};
+ // Suggest constructor as deep into the block tree as possible.
+ // This fixes https://github.com/rust-lang/rust/issues/101065,
+ // and also just helps make the most minimal suggestions.
+ let mut expr = expr;
+ while let hir::ExprKind::Block(block, _) = &expr.kind
+ && let Some(expr_) = &block.expr
+ {
+ expr = expr_
+ }
+
vec![
(expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")),
(expr.span.shrink_to_hi(), close.to_owned()),
@@ -435,6 +446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
suggestions_for(&**variant, *ctor_kind, *field_name),
Applicability::MaybeIncorrect,
);
+ return true;
}
_ => {
// More than one matching variant.
@@ -450,9 +462,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
),
Applicability::MaybeIncorrect,
);
+ return true;
}
}
}
+
+ false
}
fn suggest_non_zero_new_unwrap(
@@ -461,19 +476,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &hir::Expr<'_>,
expected: Ty<'tcx>,
expr_ty: Ty<'tcx>,
- ) {
+ ) -> bool {
let tcx = self.tcx;
let (adt, unwrap) = match expected.kind() {
// In case Option<NonZero*> is wanted, but * is provided, suggest calling new
ty::Adt(adt, substs) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
// Unwrap option
- let ty::Adt(adt, _) = substs.type_at(0).kind() else { return };
+ let ty::Adt(adt, _) = substs.type_at(0).kind() else { return false; };
(adt, "")
}
// In case NonZero* is wanted, but * is provided also add `.unwrap()` to satisfy types
ty::Adt(adt, _) => (adt, ".unwrap()"),
- _ => return,
+ _ => return false,
};
let map = [
@@ -492,7 +507,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let Some((s, _)) = map
.iter()
.find(|&&(s, t)| self.tcx.is_diagnostic_item(s, adt.did()) && self.can_coerce(expr_ty, t))
- else { return };
+ else { return false; };
let path = self.tcx.def_path_str(adt.non_enum_variant().def_id);
@@ -504,6 +519,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
],
Applicability::MaybeIncorrect,
);
+
+ true
}
pub fn get_conversion_methods(
@@ -513,24 +530,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
checked_ty: Ty<'tcx>,
hir_id: hir::HirId,
) -> Vec<AssocItem> {
- let mut methods =
- self.probe_for_return_type(span, probe::Mode::MethodCall, expected, checked_ty, hir_id);
- methods.retain(|m| {
- self.has_only_self_parameter(m)
- && self
- .tcx
- // This special internal attribute is used to permit
- // "identity-like" conversion methods to be suggested here.
- //
- // FIXME (#46459 and #46460): ideally
- // `std::convert::Into::into` and `std::borrow:ToOwned` would
- // also be `#[rustc_conversion_suggestion]`, if not for
- // method-probing false-positives and -negatives (respectively).
- //
- // FIXME? Other potential candidate methods: `as_ref` and
- // `as_mut`?
- .has_attr(m.def_id, sym::rustc_conversion_suggestion)
- });
+ let methods = self.probe_for_return_type(
+ span,
+ probe::Mode::MethodCall,
+ expected,
+ checked_ty,
+ hir_id,
+ |m| {
+ self.has_only_self_parameter(m)
+ && self
+ .tcx
+ // This special internal attribute is used to permit
+ // "identity-like" conversion methods to be suggested here.
+ //
+ // FIXME (#46459 and #46460): ideally
+ // `std::convert::Into::into` and `std::borrow:ToOwned` would
+ // also be `#[rustc_conversion_suggestion]`, if not for
+ // method-probing false-positives and -negatives (respectively).
+ //
+ // FIXME? Other potential candidate methods: `as_ref` and
+ // `as_mut`?
+ .has_attr(m.def_id, sym::rustc_conversion_suggestion)
+ },
+ );
methods
}
@@ -590,7 +612,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let closure_params_len = closure_fn_decl.inputs.len();
let (
Some(Node::Expr(hir::Expr {
- kind: hir::ExprKind::MethodCall(method_path, method_expr, _),
+ kind: hir::ExprKind::MethodCall(method_path, receiver, ..),
..
})),
1,
@@ -598,14 +620,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
};
- let self_ty = self.typeck_results.borrow().expr_ty(&method_expr[0]);
- let self_ty = format!("{:?}", self_ty);
+ let self_ty = self.typeck_results.borrow().expr_ty(receiver);
let name = method_path.ident.name;
- let is_as_ref_able = (self_ty.starts_with("&std::option::Option")
- || self_ty.starts_with("&std::result::Result")
- || self_ty.starts_with("std::option::Option")
- || self_ty.starts_with("std::result::Result"))
- && (name == sym::map || name == sym::and_then);
+ let is_as_ref_able = match self_ty.peel_refs().kind() {
+ ty::Adt(def, _) => {
+ (self.tcx.is_diagnostic_item(sym::Option, def.did())
+ || self.tcx.is_diagnostic_item(sym::Result, def.did()))
+ && (name == sym::map || name == sym::and_then)
+ }
+ _ => false,
+ };
match (is_as_ref_able, self.sess().source_map().span_to_snippet(method_path.ident.span)) {
(true, Ok(src)) => {
let suggestion = format!("as_ref().{}", src);
@@ -637,11 +661,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}?;
match hir.find(hir.get_parent_node(expr.hir_id))? {
- Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) => {
- for field in *fields {
- if field.ident.name == local.name && field.is_shorthand {
- return Some(local.name);
- }
+ Node::ExprField(field) => {
+ if field.ident.name == local.name && field.is_shorthand {
+ return Some(local.name);
}
}
_ => {}
@@ -697,7 +719,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &hir::Expr<'tcx>,
checked_ty: Ty<'tcx>,
expected: Ty<'tcx>,
- ) -> Option<(Span, String, String, Applicability, bool /* verbose */)> {
+ ) -> Option<(
+ Span,
+ String,
+ String,
+ Applicability,
+ bool, /* verbose */
+ bool, /* suggest `&` or `&mut` type annotation */
+ )> {
let sess = self.sess();
let sp = expr.span;
@@ -729,6 +758,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
String::new(),
Applicability::MachineApplicable,
true,
+ false,
));
}
}
@@ -743,6 +773,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"b".to_string(),
Applicability::MachineApplicable,
true,
+ false,
));
}
}
@@ -767,22 +798,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
if self.can_coerce(ref_ty, expected) {
let mut sugg_sp = sp;
- if let hir::ExprKind::MethodCall(ref segment, ref args, _) = expr.kind {
+ if let hir::ExprKind::MethodCall(ref segment, receiver, args, _) = expr.kind {
let clone_trait =
self.tcx.require_lang_item(LangItem::Clone, Some(segment.ident.span));
- if let ([arg], Some(true), sym::clone) = (
- &args[..],
- self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map(
+ if args.is_empty()
+ && self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map(
|did| {
let ai = self.tcx.associated_item(did);
ai.trait_container(self.tcx) == Some(clone_trait)
},
- ),
- segment.ident.name,
- ) {
+ ) == Some(true)
+ && segment.ident.name == sym::clone
+ {
// If this expression had a clone call when suggesting borrowing
// we want to suggest removing it because it'd now be unnecessary.
- sugg_sp = arg.span;
+ sugg_sp = receiver.span;
}
}
if let Ok(src) = sm.span_to_snippet(sugg_sp) {
@@ -793,7 +823,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ if is_range_literal(expr) => true,
_ => false,
};
- let sugg_expr = if needs_parens { format!("({src})") } else { src };
if let Some(sugg) = self.can_use_as_ref(expr) {
return Some((
@@ -802,6 +831,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
sugg.2,
Applicability::MachineApplicable,
false,
+ false,
));
}
@@ -821,6 +851,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
+ let sugg_expr = if needs_parens { format!("({src})") } else { src };
return Some(match mutability {
hir::Mutability::Mut => (
sp,
@@ -828,6 +859,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
format!("{prefix}&mut {sugg_expr}"),
Applicability::MachineApplicable,
false,
+ false,
),
hir::Mutability::Not => (
sp,
@@ -835,6 +867,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
format!("{prefix}&{sugg_expr}"),
Applicability::MachineApplicable,
false,
+ false,
),
});
}
@@ -864,6 +897,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
String::new(),
Applicability::MachineApplicable,
true,
+ true
));
}
return None;
@@ -877,6 +911,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
String::new(),
Applicability::MachineApplicable,
true,
+ true,
));
}
}
@@ -943,6 +978,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
src,
applicability,
true,
+ false,
));
}
}
@@ -983,6 +1019,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MachineApplicable
},
true,
+ false,
));
}
@@ -1034,6 +1071,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
suggestion,
Applicability::MachineApplicable,
true,
+ false,
));
}
}
@@ -1072,21 +1110,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut sugg = vec![];
- if let Some(hir::Node::Expr(hir::Expr {
- kind: hir::ExprKind::Struct(_, fields, _), ..
- })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
+ if let Some(hir::Node::ExprField(field)) =
+ self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
{
// `expr` is a literal field for a struct, only suggest if appropriate
- match (*fields)
- .iter()
- .find(|field| field.expr.hir_id == expr.hir_id && field.is_shorthand)
- {
+ if field.is_shorthand {
// This is a field literal
- Some(field) => {
- sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident)));
- }
+ sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident)));
+ } else {
// Likely a field was meant, but this field wasn't found. Do not suggest anything.
- None => return false,
+ return false;
}
};
@@ -1418,25 +1451,4 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => false,
}
}
-
- // Report the type inferred by the return statement.
- fn report_closure_inferred_return_type(&self, err: &mut Diagnostic, expected: Ty<'tcx>) {
- if let Some(sp) = self.ret_coercion_span.get()
- // If the closure has an explicit return type annotation, or if
- // the closure's return type has been inferred from outside
- // requirements (such as an Fn* trait bound), then a type error
- // may occur at the first return expression we see in the closure
- // (if it conflicts with the declared return type). Skip adding a
- // note in this case, since it would be incorrect.
- && !self.return_type_pre_known
- {
- err.span_note(
- sp,
- &format!(
- "return type inferred to be `{}` here",
- self.resolve_vars_if_possible(expected)
- ),
- );
- }
- }
}
diff --git a/compiler/rustc_typeck/src/check/diverges.rs b/compiler/rustc_hir_typeck/src/diverges.rs
index 963a93a95..963a93a95 100644
--- a/compiler/rustc_typeck/src/check/diverges.rs
+++ b/compiler/rustc_hir_typeck/src/diverges.rs
diff --git a/compiler/rustc_typeck/src/check/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs
index e9e810344..e9e810344 100644
--- a/compiler/rustc_typeck/src/check/expectation.rs
+++ b/compiler/rustc_hir_typeck/src/expectation.rs
diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index 74a5b6e42..fce2a5888 100644
--- a/compiler/rustc_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -89,15 +89,6 @@ enum ConsumeMode {
Move,
}
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum MutateMode {
- Init,
- /// Example: `x = y`
- JustWrite,
- /// Example: `x += y`
- WriteAndRead,
-}
-
/// The ExprUseVisitor type
///
/// This is the code that actually walks the tree.
@@ -134,7 +125,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
/// - `typeck_results` --- typeck results for the code being analyzed
pub fn new(
delegate: &'a mut (dyn Delegate<'tcx> + 'a),
- infcx: &'a InferCtxt<'a, 'tcx>,
+ infcx: &'a InferCtxt<'tcx>,
body_owner: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
@@ -233,8 +224,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
self.consume_exprs(args);
}
- hir::ExprKind::MethodCall(.., args, _) => {
+ hir::ExprKind::MethodCall(.., receiver, args, _) => {
// callee.m(args)
+ self.consume_expr(receiver);
self.consume_exprs(args);
}
@@ -497,7 +489,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
let expr_place = return_if_err!(self.mc.cat_expr(expr));
f(self);
if let Some(els) = els {
- // borrowing because we need to test the descriminant
+ // borrowing because we need to test the discriminant
self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter());
self.walk_block(els)
}
@@ -582,7 +574,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
for adjustment in adjustments {
debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment);
match adjustment.kind {
- adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) => {
+ adjustment::Adjust::NeverToAny
+ | adjustment::Adjust::Pointer(_)
+ | adjustment::Adjust::DynStar => {
// Creating a closure/fn-pointer or unsizing consumes
// the input and stores it into the resulting rvalue.
self.delegate_consume(&place_with_id, place_with_id.hir_id);
diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index 4059b3403..747ecb036 100644
--- a/compiler/rustc_typeck/src/check/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -1,4 +1,4 @@
-use crate::check::FnCtxt;
+use crate::FnCtxt;
use rustc_data_structures::{
fx::{FxHashMap, FxHashSet},
graph::WithSuccessors,
@@ -72,7 +72,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
//
// - Unconstrained ints are replaced with `i32`.
//
- // - Unconstrained floats are replaced with with `f64`.
+ // - Unconstrained floats are replaced with `f64`.
//
// - Non-numerics may get replaced with `()` or `!`, depending on
// how they were categorized by `calculate_diverging_fallback`
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 3a8093345..6a1cffe3e 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -1,12 +1,7 @@
-use crate::astconv::{
- AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
- GenericArgCountResult, IsMethodCall, PathSeg,
-};
-use crate::check::callee::{self, DeferredCallResolution};
-use crate::check::method::{self, MethodCallee, SelfSource};
-use crate::check::rvalue_scopes;
-use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy};
-
+use crate::callee::{self, DeferredCallResolution};
+use crate::method::{self, MethodCallee, SelfSource};
+use crate::rvalue_scopes;
+use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
@@ -15,26 +10,28 @@ use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, GenericArg, Node, QPath};
+use rustc_hir_analysis::astconv::{
+ AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
+ GenericArgCountResult, IsMethodCall, PathSeg,
+};
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
use rustc_infer::infer::{InferOk, InferResult};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::fold::TypeFoldable;
-use rustc_middle::ty::subst::{
- self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts,
-};
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{
self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, ToPolyTraitRef,
ToPredicate, Ty, UserType,
};
+use rustc_middle::ty::{GenericArgKind, InternalSubsts, SubstsRef, UserSelfTy, UserSubsts};
use rustc_session::lint;
use rustc_span::def_id::LocalDefId;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::{
self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt,
};
@@ -60,17 +57,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
- self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
- let msg = format!("unreachable {}", kind);
- lint.build(&msg)
- .span_label(span, &msg)
- .span_label(
+ let msg = format!("unreachable {}", kind);
+ self.tcx().struct_span_lint_hir(
+ lint::builtin::UNREACHABLE_CODE,
+ id,
+ span,
+ &msg,
+ |lint| {
+ lint.span_label(span, &msg).span_label(
orig_span,
custom_note
.unwrap_or("any code following this expression is unreachable"),
)
- .emit();
- })
+ },
+ )
}
}
}
@@ -83,21 +83,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {})
}
- #[instrument(skip(self, mutate_fulfillment_errors), level = "debug")]
+ #[instrument(skip(self, mutate_fulfillment_errors), level = "debug", ret)]
pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment(
&self,
mut ty: Ty<'tcx>,
mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
) -> Ty<'tcx> {
// No Infer()? Nothing needs doing.
- if !ty.has_infer_types_or_consts() {
+ if !ty.has_non_region_infer() {
debug!("no inference var, nothing needs doing");
return ty;
}
// If `ty` is a type variable, see whether we already know what it is.
ty = self.resolve_vars_if_possible(ty);
- if !ty.has_infer_types_or_consts() {
+ if !ty.has_non_region_infer() {
debug!(?ty);
return ty;
}
@@ -107,10 +107,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// indirect dependencies that don't seem worth tracking
// precisely.
self.select_obligations_where_possible(false, mutate_fulfillment_errors);
- ty = self.resolve_vars_if_possible(ty);
-
- debug!(?ty);
- ty
+ self.resolve_vars_if_possible(ty)
}
pub(in super::super) fn record_deferred_call_resolution(
@@ -412,7 +409,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
rhs_span: opt_input_expr.map(|expr| expr.span),
is_lit: opt_input_expr
.map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))),
- output_pred: None,
+ output_ty: None,
},
),
self.param_env,
@@ -492,21 +489,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> {
match length {
&hir::ArrayLen::Infer(_, span) => self.ct_infer(self.tcx.types.usize, None, span),
- hir::ArrayLen::Body(anon_const) => self.to_const(anon_const),
+ hir::ArrayLen::Body(anon_const) => {
+ let const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id);
+ let span = self.tcx.hir().span(anon_const.hir_id);
+ let c = ty::Const::from_anon_const(self.tcx, const_def_id);
+ self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None));
+ self.normalize_associated_types_in(span, c)
+ }
}
}
- pub fn to_const(&self, ast_c: &hir::AnonConst) -> ty::Const<'tcx> {
- let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id);
- let c = ty::Const::from_anon_const(self.tcx, const_def_id);
- self.register_wf_obligation(
- c.into(),
- self.tcx.hir().span(ast_c.hir_id),
- ObligationCauseCode::WellFormed(None),
- );
- c
- }
-
pub fn const_arg_to_const(
&self,
ast_c: &hir::AnonConst,
@@ -565,7 +557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Registers an obligation for checking later, during regionck, that `arg` is well-formed.
pub fn register_wf_obligation(
&self,
- arg: subst::GenericArg<'tcx>,
+ arg: ty::GenericArg<'tcx>,
span: Span,
code: traits::ObligationCauseCode<'tcx>,
) {
@@ -610,18 +602,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut generators = self.deferred_generator_interiors.borrow_mut();
for (body_id, interior, kind) in generators.drain(..) {
self.select_obligations_where_possible(false, |_| {});
- crate::check::generator_interior::resolve_interior(
- self, def_id, body_id, interior, kind,
- );
+ crate::generator_interior::resolve_interior(self, def_id, body_id, interior, kind);
}
}
#[instrument(skip(self), level = "debug")]
pub(in super::super) fn select_all_obligations_or_error(&self) {
- let errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self);
+ let mut errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self);
if !errors.is_empty() {
- self.report_fulfillment_errors(&errors, self.inh.body_id, false);
+ self.adjust_fulfillment_errors_for_expr_obligation(&mut errors);
+ self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id, false);
}
}
@@ -634,7 +625,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut result = self.fulfillment_cx.borrow_mut().select_where_possible(self);
if !result.is_empty() {
mutate_fulfillment_errors(&mut result);
- self.report_fulfillment_errors(&result, self.inh.body_id, fallback_has_occurred);
+ self.adjust_fulfillment_errors_for_expr_obligation(&mut result);
+ self.err_ctxt().report_fulfillment_errors(
+ &result,
+ self.inh.body_id,
+ fallback_has_occurred,
+ );
}
}
@@ -831,23 +827,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ty = item_ty.subst(self.tcx, substs);
self.write_resolution(hir_id, Ok((def_kind, def_id)));
- self.add_required_obligations_with_code(
- span,
- def_id,
- &substs,
- match lang_item {
- hir::LangItem::IntoFutureIntoFuture => {
- ObligationCauseCode::AwaitableExpr(expr_hir_id)
- }
- hir::LangItem::IteratorNext | hir::LangItem::IntoIterIntoIter => {
- ObligationCauseCode::ForLoopIterator
- }
- hir::LangItem::TryTraitFromOutput
- | hir::LangItem::TryTraitFromResidual
- | hir::LangItem::TryTraitBranch => ObligationCauseCode::QuestionMark,
- _ => traits::ItemObligation(def_id),
- },
- );
+
+ let code = match lang_item {
+ hir::LangItem::IntoFutureIntoFuture => {
+ Some(ObligationCauseCode::AwaitableExpr(expr_hir_id))
+ }
+ hir::LangItem::IteratorNext | hir::LangItem::IntoIterIntoIter => {
+ Some(ObligationCauseCode::ForLoopIterator)
+ }
+ hir::LangItem::TryTraitFromOutput
+ | hir::LangItem::TryTraitFromResidual
+ | hir::LangItem::TryTraitBranch => Some(ObligationCauseCode::QuestionMark),
+ _ => None,
+ };
+ if let Some(code) = code {
+ self.add_required_obligations_with_code(span, def_id, substs, move |_, _| code.clone());
+ } else {
+ self.add_required_obligations_for_hir(span, def_id, substs, hir_id);
+ }
+
(Res::Def(def_kind, def_id), ty)
}
@@ -986,7 +984,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if found != self.tcx.types.unit {
return;
}
- if let ExprKind::MethodCall(path_segment, [rcvr, ..], _) = expr.kind {
+ if let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind {
if self
.typeck_results
.borrow()
@@ -1273,7 +1271,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&mut self,
param: &ty::GenericParamDef,
arg: &GenericArg<'_>,
- ) -> subst::GenericArg<'tcx> {
+ ) -> ty::GenericArg<'tcx> {
match (&param.kind, arg) {
(GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
<dyn AstConv<'_>>::ast_region_to_region(self.fcx, lt, Some(param)).into()
@@ -1297,10 +1295,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn inferred_kind(
&mut self,
- substs: Option<&[subst::GenericArg<'tcx>]>,
+ substs: Option<&[ty::GenericArg<'tcx>]>,
param: &ty::GenericParamDef,
infer_args: bool,
- ) -> subst::GenericArg<'tcx> {
+ ) -> ty::GenericArg<'tcx> {
let tcx = self.fcx.tcx();
match param.kind {
GenericParamDefKind::Lifetime => {
@@ -1359,7 +1357,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// First, store the "user substs" for later.
self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty);
- self.add_required_obligations(span, def_id, &substs);
+ self.add_required_obligations_for_hir(span, def_id, &substs, hir_id);
// Substitute the values for the type parameters into the type of
// the referenced item.
@@ -1396,35 +1394,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
/// Add all the obligations that are required, substituting and normalized appropriately.
- pub(crate) fn add_required_obligations(
+ pub(crate) fn add_required_obligations_for_hir(
&self,
span: Span,
def_id: DefId,
- substs: &SubstsRef<'tcx>,
+ substs: SubstsRef<'tcx>,
+ hir_id: hir::HirId,
) {
- self.add_required_obligations_with_code(
- span,
- def_id,
- substs,
- traits::ItemObligation(def_id),
- )
+ self.add_required_obligations_with_code(span, def_id, substs, |idx, span| {
+ if span.is_dummy() {
+ ObligationCauseCode::ExprItemObligation(def_id, hir_id, idx)
+ } else {
+ ObligationCauseCode::ExprBindingObligation(def_id, span, hir_id, idx)
+ }
+ })
}
- #[tracing::instrument(level = "debug", skip(self, span, def_id, substs))]
+ #[instrument(level = "debug", skip(self, code, span, substs))]
fn add_required_obligations_with_code(
&self,
span: Span,
def_id: DefId,
- substs: &SubstsRef<'tcx>,
- code: ObligationCauseCode<'tcx>,
+ substs: SubstsRef<'tcx>,
+ code: impl Fn(usize, Span) -> ObligationCauseCode<'tcx>,
) {
+ let param_env = self.param_env;
+
+ let remap = match self.tcx.def_kind(def_id) {
+ // Associated consts have `Self: ~const Trait` bounds that should be satisfiable when
+ // `Self: Trait` is satisfied because it does not matter whether the impl is `const`.
+ // Therefore we have to remap the param env here to be non-const.
+ hir::def::DefKind::AssocConst => true,
+ hir::def::DefKind::AssocFn
+ if self.tcx.def_kind(self.tcx.parent(def_id)) == hir::def::DefKind::Trait =>
+ {
+ // N.B.: All callsites to this function involve checking a path expression.
+ //
+ // When instantiating a trait method as a function item, it does not actually matter whether
+ // the trait is `const` or not, or whether `where T: ~const Tr` needs to be satisfied as
+ // `const`. If we were to introduce instantiating trait methods as `const fn`s, we would
+ // check that after this, either via a bound `where F: ~const FnOnce` or when coercing to a
+ // `const fn` pointer.
+ //
+ // FIXME(fee1-dead) FIXME(const_trait_impl): update this doc when trait methods can satisfy
+ // `~const FnOnce` or can be coerced to `const fn` pointer.
+ true
+ }
+ _ => false,
+ };
let (bounds, _) = self.instantiate_bounds(span, def_id, &substs);
- for obligation in traits::predicates_for_generics(
- traits::ObligationCause::new(span, self.body_id, code),
- self.param_env,
+ for mut obligation in traits::predicates_for_generics(
+ |idx, predicate_span| {
+ traits::ObligationCause::new(span, self.body_id, code(idx, predicate_span))
+ },
+ param_env,
bounds,
) {
+ if remap {
+ obligation = obligation.without_const(self.tcx);
+ }
self.register_predicate(obligation);
}
}
@@ -1438,7 +1467,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty
} else {
if !self.is_tainted_by_errors() {
- self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true)
+ self.err_ctxt()
+ .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true)
.emit();
}
let err = self.tcx.ty_error();
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
index 7602f2550..fc83994ca 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
@@ -130,14 +130,17 @@ impl<'tcx> ArgMatrix<'tcx> {
let ai = &self.expected_indices;
let ii = &self.provided_indices;
+ // Issue: 100478, when we end the iteration,
+ // `next_unmatched_idx` will point to the index of the first unmatched
+ let mut next_unmatched_idx = 0;
for i in 0..cmp::max(ai.len(), ii.len()) {
- // If we eliminate the last row, any left-over inputs are considered missing
+ // If we eliminate the last row, any left-over arguments are considered missing
if i >= mat.len() {
- return Some(Issue::Missing(i));
+ return Some(Issue::Missing(next_unmatched_idx));
}
- // If we eliminate the last column, any left-over arguments are extra
+ // If we eliminate the last column, any left-over inputs are extra
if mat[i].len() == 0 {
- return Some(Issue::Extra(i));
+ return Some(Issue::Extra(next_unmatched_idx));
}
// Make sure we don't pass the bounds of our matrix
@@ -145,6 +148,7 @@ impl<'tcx> ArgMatrix<'tcx> {
let is_input = i < ii.len();
if is_arg && is_input && matches!(mat[i][i], Compatibility::Compatible) {
// This is a satisfied input, so move along
+ next_unmatched_idx += 1;
continue;
}
@@ -163,7 +167,7 @@ impl<'tcx> ArgMatrix<'tcx> {
if is_input {
for j in 0..ai.len() {
// If we find at least one argument that could satisfy this input
- // this argument isn't useless
+ // this input isn't useless
if matches!(mat[i][j], Compatibility::Compatible) {
useless = false;
break;
@@ -232,8 +236,8 @@ impl<'tcx> ArgMatrix<'tcx> {
if matches!(c, Compatibility::Compatible) { Some(i) } else { None }
})
.collect();
- if compat.len() != 1 {
- // this could go into multiple slots, don't bother exploring both
+ if compat.len() < 1 {
+ // try to find a cycle even when this could go into multiple slots, see #101097
is_cycle = false;
break;
}
@@ -309,7 +313,8 @@ impl<'tcx> ArgMatrix<'tcx> {
}
while !self.provided_indices.is_empty() || !self.expected_indices.is_empty() {
- match self.find_issue() {
+ let res = self.find_issue();
+ match res {
Some(Issue::Invalid(idx)) => {
let compatibility = self.compatibility_matrix[idx][idx].clone();
let input_idx = self.provided_indices[idx];
@@ -364,7 +369,9 @@ impl<'tcx> ArgMatrix<'tcx> {
None => {
// We didn't find any issues, so we need to push the algorithm forward
// First, eliminate any arguments that currently satisfy their inputs
- for (inp, arg) in self.eliminate_satisfied() {
+ let eliminated = self.eliminate_satisfied();
+ assert!(!eliminated.is_empty(), "didn't eliminated any indice in this round");
+ for (inp, arg) in eliminated {
matched_inputs[arg] = Some(inp);
}
}
diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs
index 8f34a970f..9a096f24f 100644
--- a/compiler/rustc_typeck/src/check/gather_locals.rs
+++ b/compiler/rustc_hir_typeck/src/gather_locals.rs
@@ -1,9 +1,10 @@
-use crate::check::{FnCtxt, LocalTy, UserType};
+use crate::{FnCtxt, LocalTy};
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::PatKind;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::ty::Ty;
+use rustc_middle::ty::UserType;
use rustc_span::Span;
use rustc_trait_selection::traits;
diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs
index a2c23db16..122ad7009 100644
--- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs
@@ -210,7 +210,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
}
/// For an expression with an uninhabited return type (e.g. a function that returns !),
- /// this adds a self edge to to the CFG to model the fact that the function does not
+ /// this adds a self edge to the CFG to model the fact that the function does not
/// return.
fn handle_uninhabited_return(&mut self, expr: &Expr<'tcx>) {
let ty = self.typeck_results.expr_ty(expr);
@@ -256,6 +256,8 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
| hir::Node::TypeBinding(..)
| hir::Node::TraitRef(..)
| hir::Node::Pat(..)
+ | hir::Node::PatField(..)
+ | hir::Node::ExprField(..)
| hir::Node::Arm(..)
| hir::Node::Local(..)
| hir::Node::Ctor(..)
@@ -432,7 +434,8 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
self.handle_uninhabited_return(expr);
}
- ExprKind::MethodCall(_, exprs, _) => {
+ ExprKind::MethodCall(_, receiver, exprs, _) => {
+ self.visit_expr(receiver);
for expr in exprs {
self.visit_expr(expr);
}
diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs
index 139d17d2e..139d17d2e 100644
--- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs
diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs
index c0a0bfe8e..c0a0bfe8e 100644
--- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs
diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs
index ded0888c3..bfe95852a 100644
--- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs
@@ -1,13 +1,16 @@
use super::TrackedValue;
use crate::{
- check::FnCtxt,
expr_use_visitor::{self, ExprUseVisitor},
+ FnCtxt,
};
use hir::{def_id::DefId, Body, HirId, HirIdMap};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
-use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind};
use rustc_middle::ty::{ParamEnv, TyCtxt};
+use rustc_middle::{
+ hir::place::{PlaceBase, Projection, ProjectionKind},
+ ty::TypeVisitable,
+};
pub(super) fn find_consumed_and_borrowed<'a, 'tcx>(
fcx: &'a FnCtxt<'a, 'tcx>,
@@ -159,8 +162,8 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
bk: rustc_middle::ty::BorrowKind,
) {
debug!(
- "borrow: place_with_id = {place_with_id:?}, diag_expr_id={diag_expr_id:?}, \
- borrow_kind={bk:?}"
+ "borrow: place_with_id = {place_with_id:#?}, diag_expr_id={diag_expr_id:#?}, \
+ borrow_kind={bk:#?}"
);
self.borrow_place(place_with_id);
@@ -198,7 +201,13 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
// If the type being assigned needs dropped, then the mutation counts as a borrow
// since it is essentially doing `Drop::drop(&mut x); x = new_value;`.
- if assignee_place.place.base_ty.needs_drop(self.tcx, self.param_env) {
+ let ty = self.tcx.erase_regions(assignee_place.place.base_ty);
+ if ty.needs_infer() {
+ self.tcx.sess.delay_span_bug(
+ self.tcx.hir().span(assignee_place.hir_id),
+ &format!("inference variables in {ty}"),
+ );
+ } else if ty.needs_drop(self.tcx, self.param_env) {
self.places
.borrowed
.insert(TrackedValue::from_place_with_projections_allowed(assignee_place));
diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs
index ced919f66..362f1c343 100644
--- a/compiler/rustc_typeck/src/mem_categorization.rs
+++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs
@@ -92,7 +92,7 @@ impl HirNode for hir::Pat<'_> {
#[derive(Clone)]
pub(crate) struct MemCategorizationContext<'a, 'tcx> {
pub(crate) typeck_results: &'a ty::TypeckResults<'tcx>,
- infcx: &'a InferCtxt<'a, 'tcx>,
+ infcx: &'a InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
body_owner: LocalDefId,
upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>,
@@ -103,7 +103,7 @@ pub(crate) type McResult<T> = Result<T, ()>;
impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
/// Creates a `MemCategorizationContext`.
pub(crate) fn new(
- infcx: &'a InferCtxt<'a, 'tcx>,
+ infcx: &'a InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
body_owner: LocalDefId,
typeck_results: &'a ty::TypeckResults<'tcx>,
@@ -184,7 +184,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
/// modes #42640) may look like `Some(x)` but in fact have
/// implicit deref patterns attached (e.g., it is really
/// `&Some(x)`). In that case, we return the "outermost" type
- /// (e.g., `&Option<T>).
+ /// (e.g., `&Option<T>`).
pub(crate) fn pat_ty_adjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> {
// Check for implicit `&` types wrapping the pattern; note
// that these are never attached to binding patterns, so
@@ -265,6 +265,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment)
}
+ #[instrument(level = "debug", skip(self, previous))]
fn cat_expr_adjusted_with<F>(
&self,
expr: &hir::Expr<'_>,
@@ -274,7 +275,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
where
F: FnOnce() -> McResult<PlaceWithHirId<'tcx>>,
{
- debug!("cat_expr_adjusted_with({:?}): {:?}", adjustment, expr);
let target = self.resolve_vars_if_possible(adjustment.target);
match adjustment.kind {
adjustment::Adjust::Deref(overloaded) => {
@@ -292,13 +292,15 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
adjustment::Adjust::NeverToAny
| adjustment::Adjust::Pointer(_)
- | adjustment::Adjust::Borrow(_) => {
+ | adjustment::Adjust::Borrow(_)
+ | adjustment::Adjust::DynStar => {
// Result is an rvalue.
Ok(self.cat_rvalue(expr.hir_id, expr.span, target))
}
}
}
+ #[instrument(level = "debug", skip(self))]
pub(crate) fn cat_expr_unadjusted(
&self,
expr: &hir::Expr<'_>,
@@ -387,6 +389,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
}
}
+ #[instrument(level = "debug", skip(self, span))]
pub(crate) fn cat_res(
&self,
hir_id: hir::HirId,
@@ -394,8 +397,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
expr_ty: Ty<'tcx>,
res: Res,
) -> McResult<PlaceWithHirId<'tcx>> {
- debug!("cat_res: id={:?} expr={:?} def={:?}", hir_id, expr_ty, res);
-
match res {
Res::Def(
DefKind::Ctor(..)
@@ -475,13 +476,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
ret
}
+ #[instrument(level = "debug", skip(self))]
fn cat_overloaded_place(
&self,
expr: &hir::Expr<'_>,
base: &hir::Expr<'_>,
) -> McResult<PlaceWithHirId<'tcx>> {
- debug!("cat_overloaded_place(expr={:?}, base={:?})", expr, base);
-
// Reconstruct the output assuming it's a reference with the
// same region and mutability as the receiver. This holds for
// `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
@@ -497,13 +497,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
self.cat_deref(expr, base)
}
+ #[instrument(level = "debug", skip(self, node))]
fn cat_deref(
&self,
node: &impl HirNode,
base_place: PlaceWithHirId<'tcx>,
) -> McResult<PlaceWithHirId<'tcx>> {
- debug!("cat_deref: base_place={:?}", base_place);
-
let base_curr_ty = base_place.place.ty();
let deref_ty = match base_curr_ty.builtin_deref(true) {
Some(mt) => mt.ty,
@@ -562,7 +561,8 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _)
| Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
| Res::SelfCtor(..)
- | Res::SelfTy { .. } => {
+ | Res::SelfTyParam { .. }
+ | Res::SelfTyAlias { .. } => {
// Structs and Unions have only have one variant.
Ok(VariantIdx::new(0))
}
diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 2c89b63ae..be4ea9986 100644
--- a/compiler/rustc_typeck/src/check/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -1,16 +1,16 @@
use super::{probe, MethodCallee};
-use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall};
-use crate::check::{callee, FnCtxt};
+use crate::{callee, FnCtxt};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::GenericArg;
+use rustc_hir_analysis::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall};
use rustc_infer::infer::{self, InferOk};
use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::fold::TypeFoldable;
-use rustc_middle::ty::subst::{self, Subst, SubstsRef};
+use rustc_middle::ty::subst::{self, SubstsRef};
use rustc_middle::ty::{self, GenericParamDefKind, Ty};
use rustc_span::Span;
use rustc_trait_selection::traits;
@@ -491,7 +491,19 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// so we just call `predicates_for_generics` directly to avoid redoing work.
// `self.add_required_obligations(self.span, def_id, &all_substs);`
for obligation in traits::predicates_for_generics(
- traits::ObligationCause::new(self.span, self.body_id, traits::ItemObligation(def_id)),
+ |idx, span| {
+ let code = if span.is_dummy() {
+ ObligationCauseCode::ExprItemObligation(def_id, self.call_expr.hir_id, idx)
+ } else {
+ ObligationCauseCode::ExprBindingObligation(
+ def_id,
+ span,
+ self.call_expr.hir_id,
+ idx,
+ )
+ };
+ traits::ObligationCause::new(self.span, self.body_id, code)
+ },
self.param_env,
method_predicates,
) {
diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs
index 7c68d9304..3c98a2aa3 100644
--- a/compiler/rustc_typeck/src/check/method/prelude2021.rs
+++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs
@@ -1,3 +1,7 @@
+use crate::{
+ method::probe::{self, Pick},
+ FnCtxt,
+};
use hir::def_id::DefId;
use hir::HirId;
use hir::ItemKind;
@@ -12,11 +16,6 @@ use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
use rustc_trait_selection::infer::InferCtxtExt;
-use crate::check::{
- method::probe::{self, Pick},
- FnCtxt,
-};
-
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(super) fn lint_dot_call_from_2018(
&self,
@@ -82,14 +81,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
prelude_or_array_lint,
self_expr.hir_id,
self_expr.span,
+ format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name),
|lint| {
let sp = self_expr.span;
- let mut lint = lint.build(&format!(
- "trait method `{}` will become ambiguous in Rust 2021",
- segment.ident.name
- ));
-
let derefs = "*".repeat(pick.autoderefs);
let autoref = match pick.autoref_or_ptr_adjustment {
@@ -133,7 +128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
- lint.emit();
+ lint
},
);
} else {
@@ -143,6 +138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
prelude_or_array_lint,
call_expr.hir_id,
call_expr.span,
+ format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name),
|lint| {
let sp = call_expr.span;
let trait_name = self.trait_path_or_bare_name(
@@ -151,16 +147,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pick.item.container_id(self.tcx),
);
- let mut lint = lint.build(&format!(
- "trait method `{}` will become ambiguous in Rust 2021",
- segment.ident.name
- ));
-
let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp);
if precise {
let args = args
.iter()
- .skip(1)
.map(|arg| {
let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
format!(
@@ -203,7 +193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
- lint.emit();
+ lint
},
);
}
@@ -258,15 +248,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return;
}
- self.tcx.struct_span_lint_hir(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| {
- // "type" refers to either a type or, more likely, a trait from which
- // the associated function or method is from.
- let container_id = pick.item.container_id(self.tcx);
- let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id);
- let trait_generics = self.tcx.generics_of(container_id);
-
- let trait_name =
- if trait_generics.params.len() <= trait_generics.has_self as usize {
+ self.tcx.struct_span_lint_hir(
+ RUST_2021_PRELUDE_COLLISIONS,
+ expr_id,
+ span,
+ format!(
+ "trait-associated function `{}` will become ambiguous in Rust 2021",
+ method_name.name
+ ),
+ |lint| {
+ // "type" refers to either a type or, more likely, a trait from which
+ // the associated function or method is from.
+ let container_id = pick.item.container_id(self.tcx);
+ let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id);
+ let trait_generics = self.tcx.generics_of(container_id);
+
+ let trait_name = if trait_generics.params.len() <= trait_generics.has_self as usize
+ {
trait_path
} else {
let counts = trait_generics.own_counts();
@@ -283,44 +281,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
};
- let mut lint = lint.build(&format!(
- "trait-associated function `{}` will become ambiguous in Rust 2021",
- method_name.name
- ));
-
- let mut self_ty_name = self_ty_span
- .find_ancestor_inside(span)
- .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
- .unwrap_or_else(|| self_ty.to_string());
-
- // Get the number of generics the self type has (if an Adt) unless we can determine that
- // the user has written the self type with generics already which we (naively) do by looking
- // for a "<" in `self_ty_name`.
- if !self_ty_name.contains('<') {
- if let Adt(def, _) = self_ty.kind() {
- let generics = self.tcx.generics_of(def.did());
- if !generics.params.is_empty() {
- let counts = generics.own_counts();
- self_ty_name += &format!(
- "<{}>",
- std::iter::repeat("'_")
- .take(counts.lifetimes)
- .chain(std::iter::repeat("_").take(counts.types + counts.consts))
- .collect::<Vec<_>>()
- .join(", ")
- );
+ let mut self_ty_name = self_ty_span
+ .find_ancestor_inside(span)
+ .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
+ .unwrap_or_else(|| self_ty.to_string());
+
+ // Get the number of generics the self type has (if an Adt) unless we can determine that
+ // the user has written the self type with generics already which we (naively) do by looking
+ // for a "<" in `self_ty_name`.
+ if !self_ty_name.contains('<') {
+ if let Adt(def, _) = self_ty.kind() {
+ let generics = self.tcx.generics_of(def.did());
+ if !generics.params.is_empty() {
+ let counts = generics.own_counts();
+ self_ty_name += &format!(
+ "<{}>",
+ std::iter::repeat("'_")
+ .take(counts.lifetimes)
+ .chain(
+ std::iter::repeat("_").take(counts.types + counts.consts)
+ )
+ .collect::<Vec<_>>()
+ .join(", ")
+ );
+ }
}
}
- }
- lint.span_suggestion(
- span,
- "disambiguate the associated function",
- format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
- Applicability::MachineApplicable,
- );
-
- lint.emit();
- });
+ lint.span_suggestion(
+ span,
+ "disambiguate the associated function",
+ format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
+ Applicability::MachineApplicable,
+ );
+
+ lint
+ },
+ );
}
fn trait_path_or_bare_name(
diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index efe15fec7..28aa2302f 100644
--- a/compiler/rustc_typeck/src/check/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -3,14 +3,12 @@ use super::CandidateSource;
use super::MethodError;
use super::NoMatchData;
-use crate::check::FnCtxt;
use crate::errors::MethodCallOnUnknownType;
-use crate::hir::def::DefKind;
-use crate::hir::def_id::DefId;
-
+use crate::FnCtxt;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir as hir;
+use rustc_hir::def::DefKind;
use rustc_hir::def::Namespace;
use rustc_infer::infer::canonical::OriginalQueryValues;
use rustc_infer::infer::canonical::{Canonical, QueryResponse};
@@ -19,10 +17,11 @@ use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::middle::stability;
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
-use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
use rustc_middle::ty::GenericParamDefKind;
use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable};
+use rustc_middle::ty::{InternalSubsts, SubstsRef};
use rustc_session::lint;
+use rustc_span::def_id::DefId;
use rustc_span::def_id::LocalDefId;
use rustc_span::lev_distance::{
find_best_match_for_name_with_substrings, lev_distance_with_substrings,
@@ -253,7 +252,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// would result in an error (basically, the same criteria we
/// would use to decide if a method is a plausible fit for
/// ambiguity purposes).
- #[instrument(level = "debug", skip(self, scope_expr_id))]
+ #[instrument(level = "debug", skip(self, candidate_filter))]
pub fn probe_for_return_type(
&self,
span: Span,
@@ -261,11 +260,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return_type: Ty<'tcx>,
self_ty: Ty<'tcx>,
scope_expr_id: hir::HirId,
+ candidate_filter: impl Fn(&ty::AssocItem) -> bool,
) -> Vec<ty::AssocItem> {
- debug!(
- "probe(self_ty={:?}, return_type={}, scope_expr_id={})",
- self_ty, return_type, scope_expr_id
- );
let method_names = self
.probe_op(
span,
@@ -276,7 +272,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self_ty,
scope_expr_id,
ProbeScope::AllTraits,
- |probe_cx| Ok(probe_cx.candidate_method_names()),
+ |probe_cx| Ok(probe_cx.candidate_method_names(candidate_filter)),
)
.unwrap_or_default();
method_names
@@ -299,7 +295,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.collect()
}
- #[instrument(level = "debug", skip(self, scope_expr_id))]
+ #[instrument(level = "debug", skip(self))]
pub fn probe_for_name(
&self,
span: Span,
@@ -310,10 +306,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
scope_expr_id: hir::HirId,
scope: ProbeScope,
) -> PickResult<'tcx> {
- debug!(
- "probe(self_ty={:?}, item_name={}, scope_expr_id={})",
- self_ty, item_name, scope_expr_id
- );
self.probe_op(
span,
mode,
@@ -417,9 +409,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
lint::builtin::TYVAR_BEHIND_RAW_POINTER,
scope_expr_id,
span,
- |lint| {
- lint.build("type annotations needed").emit();
- },
+ "type annotations needed",
+ |lint| lint,
);
}
} else {
@@ -481,69 +472,65 @@ fn method_autoderef_steps<'tcx>(
) -> MethodAutoderefStepsResult<'tcx> {
debug!("method_autoderef_steps({:?})", goal);
- tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| {
- let ParamEnvAnd { param_env, value: self_ty } = goal;
-
- let mut autoderef =
- Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP)
- .include_raw_pointers()
- .silence_errors();
- let mut reached_raw_pointer = false;
- let mut steps: Vec<_> = autoderef
- .by_ref()
- .map(|(ty, d)| {
- let step = CandidateStep {
- self_ty: infcx.make_query_response_ignoring_pending_obligations(
- inference_vars.clone(),
- ty,
- ),
- autoderefs: d,
- from_unsafe_deref: reached_raw_pointer,
- unsize: false,
- };
- if let ty::RawPtr(_) = ty.kind() {
- // all the subsequent steps will be from_unsafe_deref
- reached_raw_pointer = true;
- }
- step
- })
- .collect();
-
- let final_ty = autoderef.final_ty(true);
- let opt_bad_ty = match final_ty.kind() {
- ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy {
- reached_raw_pointer,
- ty: infcx
- .make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
- }),
- ty::Array(elem_ty, _) => {
- let dereferences = steps.len() - 1;
-
- steps.push(CandidateStep {
- self_ty: infcx.make_query_response_ignoring_pending_obligations(
- inference_vars,
- infcx.tcx.mk_slice(*elem_ty),
- ),
- autoderefs: dereferences,
- // this could be from an unsafe deref if we had
- // a *mut/const [T; N]
- from_unsafe_deref: reached_raw_pointer,
- unsize: true,
- });
-
- None
+ let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal);
+ let ParamEnvAnd { param_env, value: self_ty } = goal;
+
+ let mut autoderef =
+ Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP)
+ .include_raw_pointers()
+ .silence_errors();
+ let mut reached_raw_pointer = false;
+ let mut steps: Vec<_> = autoderef
+ .by_ref()
+ .map(|(ty, d)| {
+ let step = CandidateStep {
+ self_ty: infcx
+ .make_query_response_ignoring_pending_obligations(inference_vars.clone(), ty),
+ autoderefs: d,
+ from_unsafe_deref: reached_raw_pointer,
+ unsize: false,
+ };
+ if let ty::RawPtr(_) = ty.kind() {
+ // all the subsequent steps will be from_unsafe_deref
+ reached_raw_pointer = true;
}
- _ => None,
- };
-
- debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty);
+ step
+ })
+ .collect();
+
+ let final_ty = autoderef.final_ty(true);
+ let opt_bad_ty = match final_ty.kind() {
+ ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy {
+ reached_raw_pointer,
+ ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
+ }),
+ ty::Array(elem_ty, _) => {
+ let dereferences = steps.len() - 1;
+
+ steps.push(CandidateStep {
+ self_ty: infcx.make_query_response_ignoring_pending_obligations(
+ inference_vars,
+ infcx.tcx.mk_slice(*elem_ty),
+ ),
+ autoderefs: dereferences,
+ // this could be from an unsafe deref if we had
+ // a *mut/const [T; N]
+ from_unsafe_deref: reached_raw_pointer,
+ unsize: true,
+ });
- MethodAutoderefStepsResult {
- steps: tcx.arena.alloc_from_iter(steps),
- opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)),
- reached_recursion_limit: autoderef.reached_recursion_limit(),
+ None
}
- })
+ _ => None,
+ };
+
+ debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty);
+
+ MethodAutoderefStepsResult {
+ steps: tcx.arena.alloc_from_iter(steps),
+ opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)),
+ reached_recursion_limit: autoderef.reached_recursion_limit(),
+ }
}
impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
@@ -980,12 +967,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
- fn candidate_method_names(&self) -> Vec<Ident> {
+ fn candidate_method_names(
+ &self,
+ candidate_filter: impl Fn(&ty::AssocItem) -> bool,
+ ) -> Vec<Ident> {
let mut set = FxHashSet::default();
let mut names: Vec<_> = self
.inherent_candidates
.iter()
.chain(&self.extension_candidates)
+ .filter(|candidate| candidate_filter(&candidate.item))
.filter(|candidate| {
if let Some(return_ty) = self.return_type {
self.matches_return_type(&candidate.item, None, return_ty)
@@ -1366,24 +1357,24 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
stable_pick: &Pick<'_>,
unstable_candidates: &[(Candidate<'tcx>, Symbol)],
) {
+ let def_kind = stable_pick.item.kind.as_def_kind();
self.tcx.struct_span_lint_hir(
lint::builtin::UNSTABLE_NAME_COLLISIONS,
self.scope_expr_id,
self.span,
+ format!(
+ "{} {} with this name may be added to the standard library in the future",
+ def_kind.article(),
+ def_kind.descr(stable_pick.item.def_id),
+ ),
|lint| {
- let def_kind = stable_pick.item.kind.as_def_kind();
- let mut diag = lint.build(&format!(
- "{} {} with this name may be added to the standard library in the future",
- def_kind.article(),
- def_kind.descr(stable_pick.item.def_id),
- ));
match (stable_pick.item.kind, stable_pick.item.container) {
(ty::AssocKind::Fn, _) => {
// FIXME: This should be a `span_suggestion` instead of `help`
// However `self.span` only
// highlights the method name, so we can't use it. Also consider reusing
// the code from `report_method_error()`.
- diag.help(&format!(
+ lint.help(&format!(
"call with fully qualified syntax `{}(...)` to keep using the current \
method",
self.tcx.def_path_str(stable_pick.item.def_id),
@@ -1391,7 +1382,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
(ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => {
let def_id = stable_pick.item.container_id(self.tcx);
- diag.span_suggestion(
+ lint.span_suggestion(
self.span,
"use the fully qualified path to the associated const",
format!(
@@ -1407,7 +1398,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
if self.tcx.sess.is_nightly_build() {
for (candidate, feature) in unstable_candidates {
- diag.help(&format!(
+ lint.help(&format!(
"add `#![feature({})]` to the crate attributes to enable `{}`",
feature,
self.tcx.def_path_str(candidate.item.def_id),
@@ -1415,7 +1406,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
- diag.emit();
+ lint
},
);
}
@@ -1514,8 +1505,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
traits::normalize(selcx, self.param_env, cause.clone(), impl_bounds);
// Convert the bounds into obligations.
- let impl_obligations =
- traits::predicates_for_generics(cause, self.param_env, impl_bounds);
+ let impl_obligations = traits::predicates_for_generics(
+ move |_, _| cause.clone(),
+ self.param_env,
+ impl_bounds,
+ );
let candidate_obligations = impl_obligations
.chain(norm_obligations.into_iter())
@@ -1700,7 +1694,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
pcx.allow_similar_names = true;
pcx.assemble_inherent_candidates();
- let method_names = pcx.candidate_method_names();
+ let method_names = pcx.candidate_method_names(|_| true);
pcx.allow_similar_names = false;
let applicable_close_candidates: Vec<ty::AssocItem> = method_names
.iter()
diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index c92b93cbc..6c21ed902 100644
--- a/compiler/rustc_typeck/src/check/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -1,7 +1,9 @@
//! Give useful errors and suggestions to users when an item can't be
//! found or is otherwise invalid.
-use crate::check::FnCtxt;
+use crate::errors;
+use crate::FnCtxt;
+use rustc_ast::ast::Mutability;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
@@ -16,12 +18,12 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
use rustc_middle::traits::util::supertraits;
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
use rustc_middle::ty::print::with_crate_prefix;
-use rustc_middle::ty::ToPolyTraitRef;
use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable};
+use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef};
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Symbol;
use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span};
-use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _;
+use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_trait_selection::traits::{
FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedNote,
@@ -30,8 +32,8 @@ use rustc_trait_selection::traits::{
use std::cmp::Ordering;
use std::iter;
-use super::probe::{Mode, ProbeScope};
-use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData};
+use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope};
+use super::{CandidateSource, MethodError, NoMatchData};
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
@@ -95,7 +97,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
item_name: Ident,
source: SelfSource<'tcx>,
error: MethodError<'tcx>,
- args: Option<&'tcx [hir::Expr<'tcx>]>,
+ args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
// Avoid suggestions when we don't know what's going on.
if rcvr_ty.references_error() {
@@ -104,7 +106,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let report_candidates = |span: Span,
err: &mut Diagnostic,
- mut sources: Vec<CandidateSource>,
+ sources: &mut Vec<CandidateSource>,
sugg_span: Span| {
sources.sort();
sources.dedup();
@@ -246,7 +248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match error {
MethodError::NoMatch(NoMatchData {
- static_candidates: static_sources,
+ static_candidates: mut static_sources,
unsatisfied_predicates,
out_of_scope_traits,
lev_candidate,
@@ -270,7 +272,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
};
- if self.suggest_constraining_numerical_ty(
+ if self.suggest_wrapping_range_with_parens(
+ tcx, actual, source, span, item_name, &ty_str,
+ ) || self.suggest_constraining_numerical_ty(
tcx, actual, source, span, item_kind, item_name, &ty_str,
) {
return None;
@@ -363,44 +367,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
- if self.is_fn_ty(rcvr_ty, span) {
- if let SelfSource::MethodCall(expr) = source {
- let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() {
- if let Some(local_id) = def_id.as_local() {
- let hir_id = tcx.hir().local_def_id_to_hir_id(local_id);
- let node = tcx.hir().get(hir_id);
- let fields = node.tuple_fields();
- if let Some(fields) = fields
- && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) {
- Some((fields.len(), of))
- } else {
- None
- }
- } else {
- // The logic here isn't smart but `associated_item_def_ids`
- // doesn't work nicely on local.
- if let DefKind::Ctor(of, _) = tcx.def_kind(def_id) {
- let parent_def_id = tcx.parent(*def_id);
- Some((tcx.associated_item_def_ids(parent_def_id).len(), of))
- } else {
- None
- }
- }
- } else {
- None
- };
-
- // If the function is a tuple constructor, we recommend that they call it
- if let Some((fields, kind)) = suggest {
- suggest_call_constructor(expr.span, kind, fields, &mut err);
- } else {
- // General case
- err.span_label(
- expr.span,
- "this is a function, perhaps you wish to call it",
- );
- }
- }
+ if let SelfSource::MethodCall(rcvr_expr) = source {
+ self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| {
+ let call_expr = self
+ .tcx
+ .hir()
+ .expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id));
+ let probe = self.lookup_probe(
+ span,
+ item_name,
+ output_ty,
+ call_expr,
+ ProbeScope::AllTraits,
+ );
+ probe.is_ok()
+ });
}
let mut custom_span_label = false;
@@ -441,9 +422,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.help(&format!("try with `{}::{}`", ty_str, item_name,));
}
- report_candidates(span, &mut err, static_sources, sugg_span);
+ report_candidates(span, &mut err, &mut static_sources, sugg_span);
} else if static_sources.len() > 1 {
- report_candidates(span, &mut err, static_sources, sugg_span);
+ report_candidates(span, &mut err, &mut static_sources, sugg_span);
}
let mut bound_spans = vec![];
@@ -560,7 +541,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
bound_spans.push((self.tcx.def_span(def.did()), msg))
}
// Point at the trait object that couldn't satisfy the bound.
- ty::Dynamic(preds, _) => {
+ ty::Dynamic(preds, _, _) => {
for pred in preds.iter() {
match pred.skip_binder() {
ty::ExistentialPredicate::Trait(tr) => bound_spans
@@ -877,8 +858,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Avoid crashing.
return (None, None);
}
- let OnUnimplementedNote { message, label, .. } =
- self.on_unimplemented_note(trait_ref, &obligation);
+ let OnUnimplementedNote { message, label, .. } = self
+ .err_ctxt()
+ .on_unimplemented_note(trait_ref, &obligation);
(message, label)
})
.unwrap_or((None, None))
@@ -904,7 +886,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- let label_span_not_found = |err: &mut DiagnosticBuilder<'_, _>| {
+ let label_span_not_found = |err: &mut Diagnostic| {
if unsatisfied_predicates.is_empty() {
err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
let is_string_or_ref_str = match actual.kind() {
@@ -1000,9 +982,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
label_span_not_found(&mut err);
}
- self.check_for_field_method(&mut err, source, span, actual, item_name);
+ // Don't suggest (for example) `expr.field.method()` if `expr.method()`
+ // doesn't exist due to unsatisfied predicates.
+ if unsatisfied_predicates.is_empty() {
+ self.check_for_field_method(&mut err, source, span, actual, item_name);
+ }
- self.check_for_unwrap_self(&mut err, source, span, actual, item_name);
+ self.check_for_inner_self(&mut err, source, span, actual, item_name);
bound_spans.sort();
bound_spans.dedup();
@@ -1017,10 +1003,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span,
rcvr_ty,
item_name,
- args.map(|args| args.len()),
+ args.map(|(_, args)| args.len() + 1),
source,
out_of_scope_traits,
&unsatisfied_predicates,
+ &static_sources,
unsatisfied_bounds,
);
}
@@ -1062,23 +1049,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// that had unsatisfied trait bounds
if unsatisfied_predicates.is_empty() {
let def_kind = lev_candidate.kind.as_def_kind();
- err.span_suggestion(
- span,
- &format!(
- "there is {} {} with a similar name",
- def_kind.article(),
- def_kind.descr(lev_candidate.def_id),
- ),
- lev_candidate.name,
- Applicability::MaybeIncorrect,
- );
+ // Methods are defined within the context of a struct and their first parameter is always self,
+ // which represents the instance of the struct the method is being called on
+ // Associated functions don’t take self as a parameter and
+ // they are not methods because they don’t have an instance of the struct to work with.
+ if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter {
+ err.span_suggestion(
+ span,
+ &format!("there is a method with a similar name",),
+ lev_candidate.name,
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ err.span_suggestion(
+ span,
+ &format!(
+ "there is {} {} with a similar name",
+ def_kind.article(),
+ def_kind.descr(lev_candidate.def_id),
+ ),
+ lev_candidate.name,
+ Applicability::MaybeIncorrect,
+ );
+ }
}
}
+ self.check_for_deref_method(&mut err, source, rcvr_ty, item_name);
+
return Some(err);
}
- MethodError::Ambiguity(sources) => {
+ MethodError::Ambiguity(mut sources) => {
let mut err = struct_span_err!(
self.sess(),
item_name.span,
@@ -1087,7 +1089,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
err.span_label(item_name.span, format!("multiple `{}` found", item_name));
- report_candidates(span, &mut err, sources, sugg_span);
+ report_candidates(span, &mut err, &mut sources, sugg_span);
err.emit();
}
@@ -1150,7 +1152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
rcvr_ty: Ty<'tcx>,
expr: &hir::Expr<'_>,
item_name: Ident,
- err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+ err: &mut Diagnostic,
) -> bool {
let tcx = self.tcx;
let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() {
@@ -1165,7 +1167,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => None,
});
if let Some((field, field_ty)) = field_receiver {
- let scope = tcx.parent_module(self.body_id).to_def_id();
+ let scope = tcx.parent_module(self.body_id);
let is_accessible = field.vis.is_accessible_from(scope, tcx);
if is_accessible {
@@ -1204,6 +1206,89 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
false
}
+ /// Suggest possible range with adding parentheses, for example:
+ /// when encountering `0..1.map(|i| i + 1)` suggest `(0..1).map(|i| i + 1)`.
+ fn suggest_wrapping_range_with_parens(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ actual: Ty<'tcx>,
+ source: SelfSource<'tcx>,
+ span: Span,
+ item_name: Ident,
+ ty_str: &str,
+ ) -> bool {
+ if let SelfSource::MethodCall(expr) = source {
+ for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) {
+ if let Node::Expr(parent_expr) = parent {
+ let lang_item = match parent_expr.kind {
+ ExprKind::Struct(ref qpath, _, _) => match **qpath {
+ QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range),
+ QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo),
+ QPath::LangItem(LangItem::RangeToInclusive, ..) => {
+ Some(LangItem::RangeToInclusive)
+ }
+ _ => None,
+ },
+ ExprKind::Call(ref func, _) => match func.kind {
+ // `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
+ ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => {
+ Some(LangItem::RangeInclusiveStruct)
+ }
+ _ => None,
+ },
+ _ => None,
+ };
+
+ if lang_item.is_none() {
+ continue;
+ }
+
+ let span_included = match parent_expr.kind {
+ hir::ExprKind::Struct(_, eps, _) => {
+ eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span))
+ }
+ // `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
+ hir::ExprKind::Call(ref func, ..) => func.span.contains(span),
+ _ => false,
+ };
+
+ if !span_included {
+ continue;
+ }
+
+ let range_def_id = self.tcx.require_lang_item(lang_item.unwrap(), None);
+ let range_ty =
+ self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]);
+
+ let pick = self.probe_for_name(
+ span,
+ Mode::MethodCall,
+ item_name,
+ IsSuggestion(true),
+ range_ty,
+ expr.hir_id,
+ ProbeScope::AllTraits,
+ );
+ if pick.is_ok() {
+ let range_span = parent_expr.span.with_hi(expr.span.hi());
+ tcx.sess.emit_err(errors::MissingParentheseInRange {
+ span,
+ ty_str: ty_str.to_string(),
+ method_name: item_name.as_str().to_string(),
+ add_missing_parentheses: Some(errors::AddMissingParenthesesInRange {
+ func_name: item_name.name.as_str().to_string(),
+ left: range_span.shrink_to_lo(),
+ right: range_span.shrink_to_hi(),
+ }),
+ });
+ return true;
+ }
+ }
+ }
+ }
+ false
+ }
+
fn suggest_constraining_numerical_ty(
&self,
tcx: TyCtxt<'tcx>,
@@ -1266,7 +1351,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If this is a floating point literal that ends with '.',
// get rid of it to stop this from becoming a member access.
let snippet = snippet.strip_suffix('.').unwrap_or(&snippet);
-
err.span_suggestion(
lit.span,
&format!(
@@ -1282,7 +1366,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// local binding
if let hir::def::Res::Local(hir_id) = path.res {
let span = tcx.hir().span(hir_id);
- let snippet = tcx.sess.source_map().span_to_snippet(span);
let filename = tcx.sess.source_map().span_to_filename(span);
let parent_node =
@@ -1292,7 +1375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
concrete_type,
);
- match (filename, parent_node, snippet) {
+ match (filename, parent_node) {
(
FileName::Real(_),
Node::Local(hir::Local {
@@ -1300,14 +1383,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty,
..
}),
- Ok(ref snippet),
) => {
+ let type_span = ty.map(|ty| ty.span.with_lo(span.hi())).unwrap_or(span.shrink_to_hi());
err.span_suggestion(
// account for `let x: _ = 42;`
- // ^^^^
- span.to(ty.as_ref().map(|ty| ty.span).unwrap_or(span)),
+ // ^^^
+ type_span,
&msg,
- format!("{}: {}", snippet, concrete_type),
+ format!(": {concrete_type}"),
Applicability::MaybeIncorrect,
);
}
@@ -1327,55 +1410,82 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_for_field_method(
&self,
- err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+ err: &mut Diagnostic,
source: SelfSource<'tcx>,
span: Span,
actual: Ty<'tcx>,
item_name: Ident,
) {
if let SelfSource::MethodCall(expr) = source
- && let Some((fields, substs)) = self.get_field_candidates(span, actual)
+ && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
+ && let Some((fields, substs)) =
+ self.get_field_candidates_considering_privacy(span, actual, mod_id)
{
let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
- for candidate_field in fields.iter() {
- if let Some(field_path) = self.check_for_nested_field_satisfying(
- span,
- &|_, field_ty| {
- self.lookup_probe(
- span,
- item_name,
- field_ty,
- call_expr,
- ProbeScope::AllTraits,
- )
- .is_ok()
- },
- candidate_field,
- substs,
- vec![],
- self.tcx.parent_module(expr.hir_id).to_def_id(),
- ) {
- let field_path_str = field_path
+
+ let lang_items = self.tcx.lang_items();
+ let never_mention_traits = [
+ lang_items.clone_trait(),
+ lang_items.deref_trait(),
+ lang_items.deref_mut_trait(),
+ self.tcx.get_diagnostic_item(sym::AsRef),
+ self.tcx.get_diagnostic_item(sym::AsMut),
+ self.tcx.get_diagnostic_item(sym::Borrow),
+ self.tcx.get_diagnostic_item(sym::BorrowMut),
+ ];
+ let candidate_fields: Vec<_> = fields
+ .filter_map(|candidate_field| {
+ self.check_for_nested_field_satisfying(
+ span,
+ &|_, field_ty| {
+ self.lookup_probe(
+ span,
+ item_name,
+ field_ty,
+ call_expr,
+ ProbeScope::TraitsInScope,
+ )
+ .map_or(false, |pick| {
+ !never_mention_traits
+ .iter()
+ .flatten()
+ .any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id)
+ })
+ },
+ candidate_field,
+ substs,
+ vec![],
+ mod_id,
+ )
+ })
+ .map(|field_path| {
+ field_path
.iter()
.map(|id| id.name.to_ident_string())
.collect::<Vec<String>>()
- .join(".");
- debug!("field_path_str: {:?}", field_path_str);
-
- err.span_suggestion_verbose(
- item_name.span.shrink_to_lo(),
- "one of the expressions' fields has a method of the same name",
- format!("{field_path_str}."),
- Applicability::MaybeIncorrect,
- );
- }
+ .join(".")
+ })
+ .collect();
+
+ let len = candidate_fields.len();
+ if len > 0 {
+ err.span_suggestions(
+ item_name.span.shrink_to_lo(),
+ format!(
+ "{} of the expressions' fields {} a method of the same name",
+ if len > 1 { "some" } else { "one" },
+ if len > 1 { "have" } else { "has" },
+ ),
+ candidate_fields.iter().map(|path| format!("{path}.")),
+ Applicability::MaybeIncorrect,
+ );
}
}
}
- fn check_for_unwrap_self(
+ fn check_for_inner_self(
&self,
- err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+ err: &mut Diagnostic,
source: SelfSource<'tcx>,
span: Span,
actual: Ty<'tcx>,
@@ -1386,81 +1496,168 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id));
let ty::Adt(kind, substs) = actual.kind() else { return; };
- if !kind.is_enum() {
- return;
- }
+ match kind.adt_kind() {
+ ty::AdtKind::Enum => {
+ let matching_variants: Vec<_> = kind
+ .variants()
+ .iter()
+ .flat_map(|variant| {
+ let [field] = &variant.fields[..] else { return None; };
+ let field_ty = field.ty(tcx, substs);
+
+ // Skip `_`, since that'll just lead to ambiguity.
+ if self.resolve_vars_if_possible(field_ty).is_ty_var() {
+ return None;
+ }
- let matching_variants: Vec<_> = kind
- .variants()
- .iter()
- .flat_map(|variant| {
- let [field] = &variant.fields[..] else { return None; };
- let field_ty = field.ty(tcx, substs);
+ self.lookup_probe(
+ span,
+ item_name,
+ field_ty,
+ call_expr,
+ ProbeScope::TraitsInScope,
+ )
+ .ok()
+ .map(|pick| (variant, field, pick))
+ })
+ .collect();
+
+ let ret_ty_matches = |diagnostic_item| {
+ if let Some(ret_ty) = self
+ .ret_coercion
+ .as_ref()
+ .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty()))
+ && let ty::Adt(kind, _) = ret_ty.kind()
+ && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did())
+ {
+ true
+ } else {
+ false
+ }
+ };
- // Skip `_`, since that'll just lead to ambiguity.
- if self.resolve_vars_if_possible(field_ty).is_ty_var() {
- return None;
+ match &matching_variants[..] {
+ [(_, field, pick)] => {
+ let self_ty = field.ty(tcx, substs);
+ err.span_note(
+ tcx.def_span(pick.item.def_id),
+ &format!("the method `{item_name}` exists on the type `{self_ty}`"),
+ );
+ let (article, kind, variant, question) =
+ if tcx.is_diagnostic_item(sym::Result, kind.did()) {
+ ("a", "Result", "Err", ret_ty_matches(sym::Result))
+ } else if tcx.is_diagnostic_item(sym::Option, kind.did()) {
+ ("an", "Option", "None", ret_ty_matches(sym::Option))
+ } else {
+ return;
+ };
+ if question {
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ format!(
+ "use the `?` operator to extract the `{self_ty}` value, propagating \
+ {article} `{kind}::{variant}` value to the caller"
+ ),
+ "?",
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ format!(
+ "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \
+ panicking if the value is {article} `{kind}::{variant}`"
+ ),
+ ".expect(\"REASON\")",
+ Applicability::HasPlaceholders,
+ );
+ }
+ }
+ // FIXME(compiler-errors): Support suggestions for other matching enum variants
+ _ => {}
}
-
- self.lookup_probe(span, item_name, field_ty, call_expr, ProbeScope::AllTraits)
- .ok()
- .map(|pick| (variant, field, pick))
- })
- .collect();
-
- let ret_ty_matches = |diagnostic_item| {
- if let Some(ret_ty) = self
- .ret_coercion
- .as_ref()
- .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty()))
- && let ty::Adt(kind, _) = ret_ty.kind()
- && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did())
- {
- true
- } else {
- false
}
- };
+ // Target wrapper types - types that wrap or pretend to wrap another type,
+ // perhaps this inner type is meant to be called?
+ ty::AdtKind::Struct | ty::AdtKind::Union => {
+ let [first] = ***substs else { return; };
+ let ty::GenericArgKind::Type(ty) = first.unpack() else { return; };
+ let Ok(pick) = self.lookup_probe(
+ span,
+ item_name,
+ ty,
+ call_expr,
+ ProbeScope::TraitsInScope,
+ ) else { return; };
- match &matching_variants[..] {
- [(_, field, pick)] => {
- let self_ty = field.ty(tcx, substs);
- err.span_note(
- tcx.def_span(pick.item.def_id),
- &format!("the method `{item_name}` exists on the type `{self_ty}`"),
- );
- let (article, kind, variant, question) =
- if Some(kind.did()) == tcx.get_diagnostic_item(sym::Result) {
- ("a", "Result", "Err", ret_ty_matches(sym::Result))
- } else if Some(kind.did()) == tcx.get_diagnostic_item(sym::Option) {
- ("an", "Option", "None", ret_ty_matches(sym::Option))
- } else {
- return;
+ let name = self.ty_to_value_string(actual);
+ let inner_id = kind.did();
+ let mutable = if let Some(AutorefOrPtrAdjustment::Autoref { mutbl, .. }) =
+ pick.autoref_or_ptr_adjustment
+ {
+ Some(mutbl)
+ } else {
+ None
+ };
+
+ if tcx.is_diagnostic_item(sym::LocalKey, inner_id) {
+ err.help("use `with` or `try_with` to access thread local storage");
+ } else if Some(kind.did()) == tcx.lang_items().maybe_uninit() {
+ err.help(format!(
+ "if this `{name}` has been initialized, \
+ use one of the `assume_init` methods to access the inner value"
+ ));
+ } else if tcx.is_diagnostic_item(sym::RefCell, inner_id) {
+ let (suggestion, borrow_kind, panic_if) = match mutable {
+ Some(Mutability::Not) => (".borrow()", "borrow", "a mutable borrow exists"),
+ Some(Mutability::Mut) => {
+ (".borrow_mut()", "mutably borrow", "any borrows exist")
+ }
+ None => return,
};
- if question {
err.span_suggestion_verbose(
expr.span.shrink_to_hi(),
format!(
- "use the `?` operator to extract the `{self_ty}` value, propagating \
- {article} `{kind}::{variant}` value to the caller"
+ "use `{suggestion}` to {borrow_kind} the `{ty}`, \
+ panicking if {panic_if}"
),
- "?",
- Applicability::MachineApplicable,
+ suggestion,
+ Applicability::MaybeIncorrect,
);
- } else {
+ } else if tcx.is_diagnostic_item(sym::Mutex, inner_id) {
err.span_suggestion_verbose(
expr.span.shrink_to_hi(),
format!(
- "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \
- panicking if the value is {article} `{kind}::{variant}`"
+ "use `.lock().unwrap()` to borrow the `{ty}`, \
+ blocking the current thread until it can be acquired"
),
- ".expect(\"REASON\")",
- Applicability::HasPlaceholders,
+ ".lock().unwrap()",
+ Applicability::MaybeIncorrect,
);
- }
+ } else if tcx.is_diagnostic_item(sym::RwLock, inner_id) {
+ let (suggestion, borrow_kind) = match mutable {
+ Some(Mutability::Not) => (".read().unwrap()", "borrow"),
+ Some(Mutability::Mut) => (".write().unwrap()", "mutably borrow"),
+ None => return,
+ };
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ format!(
+ "use `{suggestion}` to {borrow_kind} the `{ty}`, \
+ blocking the current thread until it can be acquired"
+ ),
+ suggestion,
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ return;
+ };
+
+ err.span_note(
+ tcx.def_span(pick.item.def_id),
+ &format!("the method `{item_name}` exists on the type `{ty}`"),
+ );
}
- // FIXME(compiler-errors): Support suggestions for other matching enum variants
- _ => {}
}
}
@@ -1631,6 +1828,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
+ fn check_for_deref_method(
+ &self,
+ err: &mut Diagnostic,
+ self_source: SelfSource<'tcx>,
+ rcvr_ty: Ty<'tcx>,
+ item_name: Ident,
+ ) {
+ let SelfSource::QPath(ty) = self_source else { return; };
+ for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) {
+ if let Ok(pick) = self.probe_for_name(
+ ty.span,
+ Mode::Path,
+ item_name,
+ IsSuggestion(true),
+ deref_ty,
+ ty.hir_id,
+ ProbeScope::TraitsInScope,
+ ) {
+ if deref_ty.is_suggestable(self.tcx, true)
+ // If this method receives `&self`, then the provided
+ // argument _should_ coerce, so it's valid to suggest
+ // just changing the path.
+ && pick.item.fn_has_self_parameter
+ && let Some(self_ty) =
+ self.tcx.fn_sig(pick.item.def_id).inputs().skip_binder().get(0)
+ && self_ty.is_ref()
+ {
+ let suggested_path = match deref_ty.kind() {
+ ty::Bool
+ | ty::Char
+ | ty::Int(_)
+ | ty::Uint(_)
+ | ty::Float(_)
+ | ty::Adt(_, _)
+ | ty::Str
+ | ty::Projection(_)
+ | ty::Param(_) => format!("{deref_ty}"),
+ _ => format!("<{deref_ty}>"),
+ };
+ err.span_suggestion_verbose(
+ ty.span,
+ format!("the function `{item_name}` is implemented on `{deref_ty}`"),
+ suggested_path,
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ err.span_note(
+ ty.span,
+ format!("the function `{item_name}` is implemented on `{deref_ty}`"),
+ );
+ }
+ return;
+ }
+ }
+ }
+
/// Print out the type for use in value namespace.
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
match ty.kind() {
@@ -1763,6 +2016,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Option<ty::Predicate<'tcx>>,
Option<ObligationCause<'tcx>>,
)],
+ static_candidates: &[CandidateSource],
unsatisfied_bounds: bool,
) {
let mut alt_rcvr_sugg = false;
@@ -1877,6 +2131,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None => true,
})
.filter(|info| {
+ // Static candidates are already implemented, and known not to work
+ // Do not suggest them again
+ static_candidates.iter().all(|sc| match *sc {
+ CandidateSource::Trait(def_id) => def_id != info.def_id,
+ CandidateSource::Impl(def_id) => {
+ self.tcx.trait_id_of_impl(def_id) != Some(info.def_id)
+ }
+ })
+ })
+ .filter(|info| {
// We approximate the coherence rules to only suggest
// traits that are legal to implement by requiring that
// either the type or trait is local. Multi-dispatch means
@@ -1999,7 +2263,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Colon,
Nothing,
}
- let ast_generics = hir.get_generics(id.owner).unwrap();
+ let ast_generics = hir.get_generics(id.owner.def_id).unwrap();
let (sp, mut introducer) = if let Some(span) =
ast_generics.bounds_span_for_suggestions(def_id)
{
@@ -2158,6 +2422,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
+ /// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else`
+ /// FIXME: currently not working for suggesting `map_or_else`, see #102408
+ pub(crate) fn suggest_else_fn_with_closure(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ found: Ty<'tcx>,
+ expected: Ty<'tcx>,
+ ) -> bool {
+ let Some((_def_id_or_name, output, _inputs)) = self.extract_callable_info(expr, found)
+ else { return false; };
+
+ if !self.can_coerce(output, expected) {
+ return false;
+ }
+
+ let parent = self.tcx.hir().get_parent_node(expr.hir_id);
+ if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) &&
+ let hir::ExprKind::MethodCall(
+ hir::PathSegment { ident: method_name, .. },
+ self_expr,
+ args,
+ ..,
+ ) = call_expr.kind &&
+ let Some(self_ty) = self.typeck_results.borrow().expr_ty_opt(self_expr) {
+ let new_name = Ident {
+ name: Symbol::intern(&format!("{}_else", method_name.as_str())),
+ span: method_name.span,
+ };
+ let probe = self.lookup_probe(
+ expr.span,
+ new_name,
+ self_ty,
+ self_expr,
+ ProbeScope::TraitsInScope,
+ );
+
+ // check the method arguments number
+ if let Ok(pick) = probe &&
+ let fn_sig = self.tcx.fn_sig(pick.item.def_id) &&
+ let fn_args = fn_sig.skip_binder().inputs() &&
+ fn_args.len() == args.len() + 1 {
+ err.span_suggestion_verbose(
+ method_name.span.shrink_to_hi(),
+ &format!("try calling `{}` instead", new_name.name.as_str()),
+ "_else",
+ Applicability::MaybeIncorrect,
+ );
+ return true;
+ }
+ }
+ false
+ }
+
/// Checks whether there is a local type somewhere in the chain of
/// autoderefs of `rcvr_ty`.
fn type_derefs_to_local(
@@ -2232,7 +2550,7 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> {
fn print_disambiguation_help<'tcx>(
item_name: Ident,
- args: Option<&'tcx [hir::Expr<'tcx>]>,
+ args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
err: &mut Diagnostic,
trait_name: String,
rcvr_ty: Ty<'_>,
@@ -2244,7 +2562,7 @@ fn print_disambiguation_help<'tcx>(
fn_has_self_parameter: bool,
) {
let mut applicability = Applicability::MachineApplicable;
- let (span, sugg) = if let (ty::AssocKind::Fn, Some(args)) = (kind, args) {
+ let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) {
let args = format!(
"({}{})",
if rcvr_ty.is_region_ptr() {
@@ -2252,7 +2570,8 @@ fn print_disambiguation_help<'tcx>(
} else {
""
},
- args.iter()
+ std::iter::once(receiver)
+ .chain(args.iter())
.map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| {
applicability = Applicability::HasPlaceholders;
"_".to_owned()
diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 837c32355..ea90da4a6 100644
--- a/compiler/rustc_typeck/src/check/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -1,6 +1,5 @@
-use crate::check::FnCtxt;
+use crate::FnCtxt;
use rustc_ast as ast;
-
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
@@ -569,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> Ty<'tcx> {
// Determine the binding mode...
let bm = match ba {
- hir::BindingAnnotation::Unannotated => def_bm,
+ hir::BindingAnnotation::NONE => def_bm,
_ => BindingMode::convert(ba),
};
// ...and store it in a side table:
@@ -600,7 +599,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If there are multiple arms, make sure they all agree on
// what the type of the binding `x` ought to be.
if var_id != pat.hir_id {
- self.check_binding_alt_eq_ty(pat.span, var_id, local_ty, ti);
+ self.check_binding_alt_eq_ty(ba, pat.span, var_id, local_ty, ti);
}
if let Some(p) = sub {
@@ -610,7 +609,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
local_ty
}
- fn check_binding_alt_eq_ty(&self, span: Span, var_id: HirId, ty: Ty<'tcx>, ti: TopInfo<'tcx>) {
+ fn check_binding_alt_eq_ty(
+ &self,
+ ba: hir::BindingAnnotation,
+ span: Span,
+ var_id: HirId,
+ ty: Ty<'tcx>,
+ ti: TopInfo<'tcx>,
+ ) {
let var_ty = self.local_ty(span, var_id).decl_ty;
if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) {
let hir = self.tcx.hir();
@@ -628,12 +634,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
let pre = if in_match { "in the same arm, " } else { "" };
err.note(&format!("{}a binding must have the same type in all alternatives", pre));
- // FIXME: check if `var_ty` and `ty` can be made the same type by adding or removing
- // `ref` or `&` to the pattern.
+ self.suggest_adding_missing_ref_or_removing_ref(
+ &mut err,
+ span,
+ var_ty,
+ self.resolve_vars_with_obligations(ty),
+ ba,
+ );
err.emit();
}
}
+ fn suggest_adding_missing_ref_or_removing_ref(
+ &self,
+ err: &mut Diagnostic,
+ span: Span,
+ expected: Ty<'tcx>,
+ actual: Ty<'tcx>,
+ ba: hir::BindingAnnotation,
+ ) {
+ match (expected.kind(), actual.kind(), ba) {
+ (ty::Ref(_, inner_ty, _), _, hir::BindingAnnotation::NONE)
+ if self.can_eq(self.param_env, *inner_ty, actual).is_ok() =>
+ {
+ err.span_suggestion_verbose(
+ span.shrink_to_lo(),
+ "consider adding `ref`",
+ "ref ",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ (_, ty::Ref(_, inner_ty, _), hir::BindingAnnotation::REF)
+ if self.can_eq(self.param_env, expected, *inner_ty).is_ok() =>
+ {
+ err.span_suggestion_verbose(
+ span.with_hi(span.lo() + BytePos(4)),
+ "consider removing `ref`",
+ "",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => (),
+ }
+ }
+
// Precondition: pat is a Ref(_) pattern
fn borrow_pat_suggestion(&self, err: &mut Diagnostic, pat: &Pat<'_>) {
let tcx = self.tcx;
@@ -882,7 +926,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
),
);
match self.tcx.hir().get(self.tcx.hir().get_parent_node(pat.hir_id)) {
- hir::Node::Pat(Pat { kind: hir::PatKind::Struct(..), .. }) => {
+ hir::Node::PatField(..) => {
e.span_suggestion_verbose(
ident.span.shrink_to_hi(),
"bind the struct field to a different name instead",
@@ -936,7 +980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pat: &'tcx Pat<'tcx>,
qpath: &'tcx hir::QPath<'tcx>,
subpats: &'tcx [Pat<'tcx>],
- ddpos: Option<usize>,
+ ddpos: hir::DotDotPos,
expected: Ty<'tcx>,
def_bm: BindingMode,
ti: TopInfo<'tcx>,
@@ -1021,7 +1065,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Type-check subpatterns.
if subpats.len() == variant.fields.len()
- || subpats.len() < variant.fields.len() && ddpos.is_some()
+ || subpats.len() < variant.fields.len() && ddpos.as_opt_usize().is_some()
{
let ty::Adt(_, substs) = pat_ty.kind() else {
bug!("unexpected pattern type {:?}", pat_ty);
@@ -1209,14 +1253,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
span: Span,
elements: &'tcx [Pat<'tcx>],
- ddpos: Option<usize>,
+ ddpos: hir::DotDotPos,
expected: Ty<'tcx>,
def_bm: BindingMode,
ti: TopInfo<'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx;
let mut expected_len = elements.len();
- if ddpos.is_some() {
+ if ddpos.as_opt_usize().is_some() {
// Require known type only when `..` is present.
if let ty::Tuple(tys) = self.structurally_resolved_type(span, expected).kind() {
expected_len = tys.len();
@@ -1352,7 +1396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.iter()
.copied()
.filter(|(field, _)| {
- field.vis.is_accessible_from(tcx.parent_module(pat.hir_id).to_def_id(), tcx)
+ field.vis.is_accessible_from(tcx.parent_module(pat.hir_id), tcx)
&& !matches!(
tcx.eval_stability(field.did, None, DUMMY_SP, None),
EvalResult::Deny { .. }
@@ -1745,10 +1789,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&unmentioned_fields.iter().map(|(_, i)| i).collect::<Vec<_>>(),
);
- self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, |build| {
- let mut lint = build.build("some fields are not explicitly listed");
+ self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, "some fields are not explicitly listed", |lint| {
lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns));
-
lint.help(
"ensure that all fields are mentioned explicitly by adding the suggested fields",
);
@@ -1756,7 +1798,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"the pattern is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found",
ty,
));
- lint.emit();
+
+ lint
});
}
diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs
index 2e0f37eba..ba8cf6926 100644
--- a/compiler/rustc_typeck/src/check/place_op.rs
+++ b/compiler/rustc_hir_typeck/src/place_op.rs
@@ -1,5 +1,5 @@
-use crate::check::method::MethodCallee;
-use crate::check::{has_expected_num_generic_args, FnCtxt, PlaceOp};
+use crate::method::MethodCallee;
+use crate::{has_expected_num_generic_args, FnCtxt, PlaceOp};
use rustc_ast as ast;
use rustc_errors::Applicability;
use rustc_hir as hir;
diff --git a/compiler/rustc_typeck/src/check/rvalue_scopes.rs b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs
index 22c9e7961..22c9e7961 100644
--- a/compiler/rustc_typeck/src/check/rvalue_scopes.rs
+++ b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs
diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index dd8f943b9..4dea40829 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -352,7 +352,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// and that the path can be captured with required capture kind (depending on use in closure,
/// move closure etc.)
///
- /// Returns the set of of adjusted information along with the inferred closure kind and span
+ /// Returns the set of adjusted information along with the inferred closure kind and span
/// associated with the closure kind inference.
///
/// Note that we *always* infer a minimal kind, even if
@@ -749,10 +749,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES,
closure_hir_id,
closure_head_span,
+ reasons.migration_message(),
|lint| {
- let mut diagnostics_builder = lint.build(
- &reasons.migration_message(),
- );
for NeededMigration { var_hir_id, diagnostics_info } in &need_migrations {
// Labels all the usage of the captured variable and why they are responsible
// for migration being needed
@@ -760,13 +758,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match &lint_note.captures_info {
UpvarMigrationInfo::CapturingPrecise { source_expr: Some(capture_expr_id), var_name: captured_name } => {
let cause_span = self.tcx.hir().span(*capture_expr_id);
- diagnostics_builder.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`",
+ lint.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`",
self.tcx.hir().name(*var_hir_id),
captured_name,
));
}
UpvarMigrationInfo::CapturingNothing { use_span } => {
- diagnostics_builder.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect",
+ lint.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect",
self.tcx.hir().name(*var_hir_id),
));
}
@@ -781,13 +779,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match &lint_note.captures_info {
UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => {
- diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure",
+ lint.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure",
self.tcx.hir().name(*var_hir_id),
captured_name,
));
}
UpvarMigrationInfo::CapturingNothing { use_span: _ } => {
- diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure",
+ lint.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure",
v = self.tcx.hir().name(*var_hir_id),
));
}
@@ -800,7 +798,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match &lint_note.captures_info {
UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => {
let var_name = self.tcx.hir().name(*var_hir_id);
- diagnostics_builder.span_label(closure_head_span, format!("\
+ lint.span_label(closure_head_span, format!("\
in Rust 2018, this closure implements {missing_trait} \
as `{var_name}` implements {missing_trait}, but in Rust 2021, \
this closure will no longer implement {missing_trait} \
@@ -814,7 +812,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
- diagnostics_builder.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>");
+ lint.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>");
let diagnostic_msg = format!(
"add a dummy let to cause {} to be fully captured",
@@ -857,7 +855,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// We take the indentation from the next non-empty line.
let line2 = lines.find(|line| !line.is_empty()).unwrap_or_default();
let indent = line2.split_once(|c: char| !c.is_whitespace()).unwrap_or_default().0;
- diagnostics_builder.span_suggestion(
+ lint.span_suggestion(
closure_body_span.with_lo(closure_body_span.lo() + BytePos::from_usize(line1.len())).shrink_to_lo(),
&diagnostic_msg,
format!("\n{indent}{migration_string};"),
@@ -868,7 +866,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// braces, but with more than just the opening
// brace on the first line. We put the `let`
// directly after the `{`.
- diagnostics_builder.span_suggestion(
+ lint.span_suggestion(
closure_body_span.with_lo(closure_body_span.lo() + BytePos(1)).shrink_to_lo(),
&diagnostic_msg,
format!(" {migration_string};"),
@@ -877,7 +875,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
// This is a closure without braces around the body.
// We add braces to add the `let` before the body.
- diagnostics_builder.multipart_suggestion(
+ lint.multipart_suggestion(
&diagnostic_msg,
vec![
(closure_body_span.shrink_to_lo(), format!("{{ {migration_string}; ")),
@@ -887,7 +885,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
} else {
- diagnostics_builder.span_suggestion(
+ lint.span_suggestion(
closure_span,
&diagnostic_msg,
migration_string,
@@ -895,7 +893,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
- diagnostics_builder.emit();
+ lint
},
);
}
@@ -1217,7 +1215,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Combine all the reasons of why the root variable should be captured as a result of
// auto trait implementation issues
- auto_trait_migration_reasons.extend(capture_trait_reasons.clone());
+ auto_trait_migration_reasons.extend(capture_trait_reasons.iter().copied());
diagnostics_info.push(MigrationLintNote {
captures_info,
@@ -2024,6 +2022,10 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis(
tcx: TyCtxt<'_>,
closure_id: hir::HirId,
) -> bool {
+ if tcx.sess.rust_2021() {
+ return false;
+ }
+
let (level, _) =
tcx.lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id);
diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index f549807c3..1e26daa9c 100644
--- a/compiler/rustc_typeck/src/check/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -2,8 +2,7 @@
// unresolved type variables and replaces "ty_var" types with their
// substitutions.
-use crate::check::FnCtxt;
-
+use crate::FnCtxt;
use hir::def_id::LocalDefId;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::ErrorGuaranteed;
@@ -16,6 +15,7 @@ use rustc_middle::mir::FakeReadCause;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable};
+use rustc_middle::ty::TypeckResults;
use rustc_middle::ty::{self, ClosureSizeProfileData, Ty, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
@@ -192,6 +192,27 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
}
}
+ // (ouz-a 1005988): Normally `[T] : std::ops::Index<usize>` should be normalized
+ // into [T] but currently `Where` clause stops the normalization process for it,
+ // here we compare types of expr and base in a code without `Where` clause they would be equal
+ // if they are not we don't modify the expr, hence we bypass the ICE
+ fn is_builtin_index(
+ &mut self,
+ typeck_results: &TypeckResults<'tcx>,
+ e: &hir::Expr<'_>,
+ base_ty: Ty<'tcx>,
+ index_ty: Ty<'tcx>,
+ ) -> bool {
+ if let Some(elem_ty) = base_ty.builtin_index() {
+ let Some(exp_ty) = typeck_results.expr_ty_opt(e) else {return false;};
+ let resolved_exp_ty = self.resolve(exp_ty, &e.span);
+
+ elem_ty == resolved_exp_ty && index_ty == self.fcx.tcx.types.usize
+ } else {
+ false
+ }
+ }
+
// Similar to operators, indexing is always assumed to be overloaded
// Here, correct cases where an indexing expression can be simplified
// to use builtin indexing because the index type is known to be
@@ -222,8 +243,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
)
});
let index_ty = self.fcx.resolve_vars_if_possible(index_ty);
+ let resolved_base_ty = self.resolve(*base_ty, &base.span);
- if base_ty.builtin_index().is_some() && index_ty == self.fcx.tcx.types.usize {
+ if self.is_builtin_index(&typeck_results, e, resolved_base_ty, index_ty) {
// Remove the method call record
typeck_results.type_dependent_defs_mut().remove(e.hir_id);
typeck_results.node_substs_mut().remove(e.hir_id);
@@ -292,6 +314,17 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
intravisit::walk_expr(self, e);
}
+ fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
+ match &p.kind {
+ hir::GenericParamKind::Lifetime { .. } => {
+ // Nothing to write back here
+ }
+ hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => {
+ self.tcx().sess.delay_span_bug(p.span, format!("unexpected generic param: {p:?}"));
+ }
+ }
+ }
+
fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) {
self.visit_node_id(b.span, b.hir_id);
intravisit::walk_block(self, b);
@@ -468,7 +501,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
if !errors_buffer.is_empty() {
errors_buffer.sort_by_key(|diag| diag.span.primary_span());
- for mut diag in errors_buffer.drain(..) {
+ for mut diag in errors_buffer {
self.tcx().sess.diagnostic().emit_diagnostic(&mut diag);
}
}
@@ -503,33 +536,37 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
let opaque_types =
self.fcx.infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
for (opaque_type_key, decl) in opaque_types {
- let hidden_type = match decl.origin {
- hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => {
- let ty = self.resolve(decl.hidden_type.ty, &decl.hidden_type.span);
- struct RecursionChecker {
- def_id: LocalDefId,
- }
- impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker {
- type BreakTy = ();
- fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
- if let ty::Opaque(def_id, _) = *t.kind() {
- if def_id == self.def_id.to_def_id() {
- return ControlFlow::Break(());
- }
- }
- t.super_visit_with(self)
+ let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span);
+ let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span);
+
+ struct RecursionChecker {
+ def_id: LocalDefId,
+ }
+ impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker {
+ type BreakTy = ();
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if let ty::Opaque(def_id, _) = *t.kind() {
+ if def_id == self.def_id.to_def_id() {
+ return ControlFlow::Break(());
}
}
- if ty
- .visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id })
- .is_break()
- {
- return;
- }
- Some(ty)
+ t.super_visit_with(self)
}
- hir::OpaqueTyOrigin::TyAlias => None,
- };
+ }
+ if hidden_type
+ .visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id })
+ .is_break()
+ {
+ continue;
+ }
+
+ let hidden_type = hidden_type.remap_generic_params_to_declaration_params(
+ opaque_type_key,
+ self.fcx.infcx.tcx,
+ true,
+ decl.origin,
+ );
+
self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type);
}
}
@@ -667,7 +704,7 @@ impl Locatable for hir::HirId {
/// unresolved types and so forth.
struct Resolver<'cx, 'tcx> {
tcx: TyCtxt<'tcx>,
- infcx: &'cx InferCtxt<'cx, 'tcx>,
+ infcx: &'cx InferCtxt<'tcx>,
span: &'cx dyn Locatable,
body: &'tcx hir::Body<'tcx>,
@@ -684,27 +721,14 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
Resolver { tcx: fcx.tcx, infcx: fcx, span, body, replaced_with_error: false }
}
- fn report_type_error(&self, t: Ty<'tcx>) {
+ fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) {
if !self.tcx.sess.has_errors().is_some() {
self.infcx
+ .err_ctxt()
.emit_inference_failure_err(
Some(self.body.id()),
self.span.to_span(self.tcx),
- t.into(),
- E0282,
- false,
- )
- .emit();
- }
- }
-
- fn report_const_error(&self, c: ty::Const<'tcx>) {
- if self.tcx.sess.has_errors().is_none() {
- self.infcx
- .emit_inference_failure_err(
- Some(self.body.id()),
- self.span.to_span(self.tcx),
- c.into(),
+ p.into(),
E0282,
false,
)
@@ -749,7 +773,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> {
}
Err(_) => {
debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t);
- self.report_type_error(t);
+ self.report_error(t);
self.replaced_with_error = true;
self.tcx().ty_error()
}
@@ -766,7 +790,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> {
Ok(ct) => self.tcx.erase_regions(ct),
Err(_) => {
debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct);
- self.report_const_error(ct);
+ self.report_error(ct);
self.replaced_with_error = true;
self.tcx().const_error(ct.ty())
}
diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs
deleted file mode 100644
index ff39bf361..000000000
--- a/compiler/rustc_typeck/src/astconv/errors.rs
+++ /dev/null
@@ -1,410 +0,0 @@
-use crate::astconv::AstConv;
-use crate::errors::{ManualImplementation, MissingTypeParams};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed};
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_middle::ty;
-use rustc_session::parse::feature_err;
-use rustc_span::lev_distance::find_best_match_for_name;
-use rustc_span::symbol::{sym, Ident};
-use rustc_span::{Span, Symbol, DUMMY_SP};
-
-use std::collections::BTreeSet;
-
-impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
- /// On missing type parameters, emit an E0393 error and provide a structured suggestion using
- /// the type parameter's name as a placeholder.
- pub(crate) fn complain_about_missing_type_params(
- &self,
- missing_type_params: Vec<Symbol>,
- def_id: DefId,
- span: Span,
- empty_generic_args: bool,
- ) {
- if missing_type_params.is_empty() {
- return;
- }
-
- self.tcx().sess.emit_err(MissingTypeParams {
- span,
- def_span: self.tcx().def_span(def_id),
- missing_type_params,
- empty_generic_args,
- });
- }
-
- /// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit
- /// an error and attempt to build a reasonable structured suggestion.
- pub(crate) fn complain_about_internal_fn_trait(
- &self,
- span: Span,
- trait_def_id: DefId,
- trait_segment: &'_ hir::PathSegment<'_>,
- is_impl: bool,
- ) {
- if self.tcx().features().unboxed_closures {
- return;
- }
-
- let trait_def = self.tcx().trait_def(trait_def_id);
- if !trait_def.paren_sugar {
- if trait_segment.args().parenthesized {
- // For now, require that parenthetical notation be used only with `Fn()` etc.
- let mut err = feature_err(
- &self.tcx().sess.parse_sess,
- sym::unboxed_closures,
- span,
- "parenthetical notation is only stable when used with `Fn`-family traits",
- );
- err.emit();
- }
-
- return;
- }
-
- let sess = self.tcx().sess;
-
- if !trait_segment.args().parenthesized {
- // For now, require that parenthetical notation be used only with `Fn()` etc.
- let mut err = feature_err(
- &sess.parse_sess,
- sym::unboxed_closures,
- span,
- "the precise format of `Fn`-family traits' type parameters is subject to change",
- );
- // Do not suggest the other syntax if we are in trait impl:
- // the desugaring would contain an associated type constraint.
- if !is_impl {
- let args = trait_segment
- .args
- .as_ref()
- .and_then(|args| args.args.get(0))
- .and_then(|arg| match arg {
- hir::GenericArg::Type(ty) => match ty.kind {
- hir::TyKind::Tup(t) => t
- .iter()
- .map(|e| sess.source_map().span_to_snippet(e.span))
- .collect::<Result<Vec<_>, _>>()
- .map(|a| a.join(", ")),
- _ => sess.source_map().span_to_snippet(ty.span),
- }
- .map(|s| format!("({})", s))
- .ok(),
- _ => None,
- })
- .unwrap_or_else(|| "()".to_string());
- let ret = trait_segment
- .args()
- .bindings
- .iter()
- .find_map(|b| match (b.ident.name == sym::Output, &b.kind) {
- (true, hir::TypeBindingKind::Equality { term }) => {
- let span = match term {
- hir::Term::Ty(ty) => ty.span,
- hir::Term::Const(c) => self.tcx().hir().span(c.hir_id),
- };
- sess.source_map().span_to_snippet(span).ok()
- }
- _ => None,
- })
- .unwrap_or_else(|| "()".to_string());
- err.span_suggestion(
- span,
- "use parenthetical notation instead",
- format!("{}{} -> {}", trait_segment.ident, args, ret),
- Applicability::MaybeIncorrect,
- );
- }
- err.emit();
- }
-
- if is_impl {
- let trait_name = self.tcx().def_path_str(trait_def_id);
- self.tcx().sess.emit_err(ManualImplementation { span, trait_name });
- }
- }
-
- pub(crate) fn complain_about_assoc_type_not_found<I>(
- &self,
- all_candidates: impl Fn() -> I,
- ty_param_name: &str,
- assoc_name: Ident,
- span: Span,
- ) -> ErrorGuaranteed
- where
- I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
- {
- // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
- // valid span, so we point at the whole path segment instead.
- let span = if assoc_name.span != DUMMY_SP { assoc_name.span } else { span };
- let mut err = struct_span_err!(
- self.tcx().sess,
- span,
- E0220,
- "associated type `{}` not found for `{}`",
- assoc_name,
- ty_param_name
- );
-
- let all_candidate_names: Vec<_> = all_candidates()
- .flat_map(|r| self.tcx().associated_items(r.def_id()).in_definition_order())
- .filter_map(
- |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None },
- )
- .collect();
-
- if let (Some(suggested_name), true) = (
- find_best_match_for_name(&all_candidate_names, assoc_name.name, None),
- assoc_name.span != DUMMY_SP,
- ) {
- err.span_suggestion(
- assoc_name.span,
- "there is an associated type with a similar name",
- suggested_name,
- Applicability::MaybeIncorrect,
- );
- return err.emit();
- }
-
- // If we didn't find a good item in the supertraits (or couldn't get
- // the supertraits), like in ItemCtxt, then look more generally from
- // all visible traits. If there's one clear winner, just suggest that.
-
- let visible_traits: Vec<_> = self
- .tcx()
- .all_traits()
- .filter(|trait_def_id| {
- let viz = self.tcx().visibility(*trait_def_id);
- if let Some(def_id) = self.item_def_id() {
- viz.is_accessible_from(def_id, self.tcx())
- } else {
- viz.is_visible_locally()
- }
- })
- .collect();
-
- let wider_candidate_names: Vec<_> = visible_traits
- .iter()
- .flat_map(|trait_def_id| {
- self.tcx().associated_items(*trait_def_id).in_definition_order()
- })
- .filter_map(
- |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None },
- )
- .collect();
-
- if let (Some(suggested_name), true) = (
- find_best_match_for_name(&wider_candidate_names, assoc_name.name, None),
- assoc_name.span != DUMMY_SP,
- ) {
- if let [best_trait] = visible_traits
- .iter()
- .filter(|trait_def_id| {
- self.tcx()
- .associated_items(*trait_def_id)
- .filter_by_name_unhygienic(suggested_name)
- .any(|item| item.kind == ty::AssocKind::Type)
- })
- .collect::<Vec<_>>()[..]
- {
- err.span_label(
- assoc_name.span,
- format!(
- "there is a similarly named associated type `{suggested_name}` in the trait `{}`",
- self.tcx().def_path_str(*best_trait)
- ),
- );
- return err.emit();
- }
- }
-
- err.span_label(span, format!("associated type `{}` not found", assoc_name));
- err.emit()
- }
-
- /// When there are any missing associated types, emit an E0191 error and attempt to supply a
- /// reasonable suggestion on how to write it. For the case of multiple associated types in the
- /// same trait bound have the same name (as they come from different supertraits), we instead
- /// emit a generic note suggesting using a `where` clause to constraint instead.
- pub(crate) fn complain_about_missing_associated_types(
- &self,
- associated_types: FxHashMap<Span, BTreeSet<DefId>>,
- potential_assoc_types: Vec<Span>,
- trait_bounds: &[hir::PolyTraitRef<'_>],
- ) {
- if associated_types.values().all(|v| v.is_empty()) {
- return;
- }
- let tcx = self.tcx();
- // FIXME: Marked `mut` so that we can replace the spans further below with a more
- // appropriate one, but this should be handled earlier in the span assignment.
- let mut associated_types: FxHashMap<Span, Vec<_>> = associated_types
- .into_iter()
- .map(|(span, def_ids)| {
- (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect())
- })
- .collect();
- let mut names = vec![];
-
- // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
- // `issue-22560.rs`.
- let mut trait_bound_spans: Vec<Span> = vec![];
- for (span, items) in &associated_types {
- if !items.is_empty() {
- trait_bound_spans.push(*span);
- }
- for assoc_item in items {
- let trait_def_id = assoc_item.container_id(tcx);
- names.push(format!(
- "`{}` (from trait `{}`)",
- assoc_item.name,
- tcx.def_path_str(trait_def_id),
- ));
- }
- }
- if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
- match bound.trait_ref.path.segments {
- // FIXME: `trait_ref.path.span` can point to a full path with multiple
- // segments, even though `trait_ref.path.segments` is of length `1`. Work
- // around that bug here, even though it should be fixed elsewhere.
- // This would otherwise cause an invalid suggestion. For an example, look at
- // `src/test/ui/issues/issue-28344.rs` where instead of the following:
- //
- // error[E0191]: the value of the associated type `Output`
- // (from trait `std::ops::BitXor`) must be specified
- // --> $DIR/issue-28344.rs:4:17
- // |
- // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
- // | ^^^^^^ help: specify the associated type:
- // | `BitXor<Output = Type>`
- //
- // we would output:
- //
- // error[E0191]: the value of the associated type `Output`
- // (from trait `std::ops::BitXor`) must be specified
- // --> $DIR/issue-28344.rs:4:17
- // |
- // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
- // | ^^^^^^^^^^^^^ help: specify the associated type:
- // | `BitXor::bitor<Output = Type>`
- [segment] if segment.args.is_none() => {
- trait_bound_spans = vec![segment.ident.span];
- associated_types = associated_types
- .into_iter()
- .map(|(_, items)| (segment.ident.span, items))
- .collect();
- }
- _ => {}
- }
- }
- names.sort();
- trait_bound_spans.sort();
- let mut err = struct_span_err!(
- tcx.sess,
- trait_bound_spans,
- E0191,
- "the value of the associated type{} {} must be specified",
- pluralize!(names.len()),
- names.join(", "),
- );
- let mut suggestions = vec![];
- let mut types_count = 0;
- let mut where_constraints = vec![];
- let mut already_has_generics_args_suggestion = false;
- for (span, assoc_items) in &associated_types {
- let mut names: FxHashMap<_, usize> = FxHashMap::default();
- for item in assoc_items {
- types_count += 1;
- *names.entry(item.name).or_insert(0) += 1;
- }
- let mut dupes = false;
- for item in assoc_items {
- let prefix = if names[&item.name] > 1 {
- let trait_def_id = item.container_id(tcx);
- dupes = true;
- format!("{}::", tcx.def_path_str(trait_def_id))
- } else {
- String::new()
- };
- if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
- err.span_label(sp, format!("`{}{}` defined here", prefix, item.name));
- }
- }
- if potential_assoc_types.len() == assoc_items.len() {
- // When the amount of missing associated types equals the number of
- // extra type arguments present. A suggesting to replace the generic args with
- // associated types is already emitted.
- already_has_generics_args_suggestion = true;
- } else if let (Ok(snippet), false) =
- (tcx.sess.source_map().span_to_snippet(*span), dupes)
- {
- let types: Vec<_> =
- assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect();
- let code = if snippet.ends_with('>') {
- // The user wrote `Trait<'a>` or similar and we don't have a type we can
- // suggest, but at least we can clue them to the correct syntax
- // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
- // suggestion.
- format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", "))
- } else {
- // The user wrote `Iterator`, so we don't have a type we can suggest, but at
- // least we can clue them to the correct syntax `Iterator<Item = Type>`.
- format!("{}<{}>", snippet, types.join(", "))
- };
- suggestions.push((*span, code));
- } else if dupes {
- where_constraints.push(*span);
- }
- }
- let where_msg = "consider introducing a new type parameter, adding `where` constraints \
- using the fully-qualified path to the associated types";
- if !where_constraints.is_empty() && suggestions.is_empty() {
- // If there are duplicates associated type names and a single trait bound do not
- // use structured suggestion, it means that there are multiple supertraits with
- // the same associated type name.
- err.help(where_msg);
- }
- if suggestions.len() != 1 || already_has_generics_args_suggestion {
- // We don't need this label if there's an inline suggestion, show otherwise.
- for (span, assoc_items) in &associated_types {
- let mut names: FxHashMap<_, usize> = FxHashMap::default();
- for item in assoc_items {
- types_count += 1;
- *names.entry(item.name).or_insert(0) += 1;
- }
- let mut label = vec![];
- for item in assoc_items {
- let postfix = if names[&item.name] > 1 {
- let trait_def_id = item.container_id(tcx);
- format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id))
- } else {
- String::new()
- };
- label.push(format!("`{}`{}", item.name, postfix));
- }
- if !label.is_empty() {
- err.span_label(
- *span,
- format!(
- "associated type{} {} must be specified",
- pluralize!(label.len()),
- label.join(", "),
- ),
- );
- }
- }
- }
- if !suggestions.is_empty() {
- err.multipart_suggestion(
- &format!("specify the associated type{}", pluralize!(types_count)),
- suggestions,
- Applicability::HasPlaceholders,
- );
- if !where_constraints.is_empty() {
- err.span_help(where_constraints, where_msg);
- }
- }
- err.emit();
- }
-}
diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs
deleted file mode 100644
index 40aa27a29..000000000
--- a/compiler/rustc_typeck/src/astconv/generics.rs
+++ /dev/null
@@ -1,664 +0,0 @@
-use super::IsMethodCall;
-use crate::astconv::{
- AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
- GenericArgCountResult, GenericArgPosition,
-};
-use crate::errors::AssocTypeBindingNotAllowed;
-use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs};
-use rustc_ast::ast::ParamKindOrd;
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan};
-use rustc_hir as hir;
-use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::DefId;
-use rustc_hir::GenericArg;
-use rustc_infer::infer::TyCtxtInferExt;
-use rustc_middle::ty::{
- self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt,
-};
-use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS;
-use rustc_span::{symbol::kw, Span};
-use smallvec::SmallVec;
-
-impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
- /// Report an error that a generic argument did not match the generic parameter that was
- /// expected.
- fn generic_arg_mismatch_err(
- tcx: TyCtxt<'_>,
- arg: &GenericArg<'_>,
- param: &GenericParamDef,
- possible_ordering_error: bool,
- help: Option<&str>,
- ) {
- let sess = tcx.sess;
- let mut err = struct_span_err!(
- sess,
- arg.span(),
- E0747,
- "{} provided when a {} was expected",
- arg.descr(),
- param.kind.descr(),
- );
-
- if let GenericParamDefKind::Const { .. } = param.kind {
- if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) {
- err.help("const arguments cannot yet be inferred with `_`");
- if sess.is_nightly_build() {
- err.help(
- "add `#![feature(generic_arg_infer)]` to the crate attributes to enable",
- );
- }
- }
- }
-
- let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diagnostic| {
- let suggestions = vec![
- (arg.span().shrink_to_lo(), String::from("{ ")),
- (arg.span().shrink_to_hi(), String::from(" }")),
- ];
- err.multipart_suggestion(
- "if this generic argument was intended as a const parameter, \
- surround it with braces",
- suggestions,
- Applicability::MaybeIncorrect,
- );
- };
-
- // Specific suggestion set for diagnostics
- match (arg, &param.kind) {
- (
- GenericArg::Type(hir::Ty {
- kind: hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)),
- ..
- }),
- GenericParamDefKind::Const { .. },
- ) => match path.res {
- Res::Err => {
- add_braces_suggestion(arg, &mut err);
- err.set_primary_message(
- "unresolved item provided when a constant was expected",
- )
- .emit();
- return;
- }
- Res::Def(DefKind::TyParam, src_def_id) => {
- if let Some(param_local_id) = param.def_id.as_local() {
- let param_name = tcx.hir().ty_param_name(param_local_id);
- let param_type = tcx.infer_ctxt().enter(|infcx| {
- infcx.resolve_numeric_literals_with_default(tcx.type_of(param.def_id))
- });
- if param_type.is_suggestable(tcx, false) {
- err.span_suggestion(
- tcx.def_span(src_def_id),
- "consider changing this type parameter to be a `const` generic",
- format!("const {}: {}", param_name, param_type),
- Applicability::MaybeIncorrect,
- );
- };
- }
- }
- _ => add_braces_suggestion(arg, &mut err),
- },
- (
- GenericArg::Type(hir::Ty { kind: hir::TyKind::Path(_), .. }),
- GenericParamDefKind::Const { .. },
- ) => add_braces_suggestion(arg, &mut err),
- (
- GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }),
- GenericParamDefKind::Const { .. },
- ) if tcx.type_of(param.def_id) == tcx.types.usize => {
- let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id()));
- if let Ok(snippet) = snippet {
- err.span_suggestion(
- arg.span(),
- "array type provided where a `usize` was expected, try",
- format!("{{ {} }}", snippet),
- Applicability::MaybeIncorrect,
- );
- }
- }
- (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => {
- let body = tcx.hir().body(cnst.value.body);
- if let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) =
- body.value.kind
- {
- if let Res::Def(DefKind::Fn { .. }, id) = path.res {
- err.help(&format!(
- "`{}` is a function item, not a type",
- tcx.item_name(id)
- ));
- err.help("function item types cannot be named directly");
- }
- }
- }
- _ => {}
- }
-
- let kind_ord = param.kind.to_ord();
- let arg_ord = arg.to_ord();
-
- // This note is only true when generic parameters are strictly ordered by their kind.
- if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal {
- let (first, last) = if kind_ord < arg_ord {
- (param.kind.descr(), arg.descr())
- } else {
- (arg.descr(), param.kind.descr())
- };
- err.note(&format!("{} arguments must be provided before {} arguments", first, last));
- if let Some(help) = help {
- err.help(help);
- }
- }
-
- err.emit();
- }
-
- /// Creates the relevant generic argument substitutions
- /// corresponding to a set of generic parameters. This is a
- /// rather complex function. Let us try to explain the role
- /// of each of its parameters:
- ///
- /// To start, we are given the `def_id` of the thing we are
- /// creating the substitutions for, and a partial set of
- /// substitutions `parent_substs`. In general, the substitutions
- /// for an item begin with substitutions for all the "parents" of
- /// that item -- e.g., for a method it might include the
- /// parameters from the impl.
- ///
- /// Therefore, the method begins by walking down these parents,
- /// starting with the outermost parent and proceed inwards until
- /// it reaches `def_id`. For each parent `P`, it will check `parent_substs`
- /// first to see if the parent's substitutions are listed in there. If so,
- /// we can append those and move on. Otherwise, it invokes the
- /// three callback functions:
- ///
- /// - `args_for_def_id`: given the `DefId` `P`, supplies back the
- /// generic arguments that were given to that parent from within
- /// the path; so e.g., if you have `<T as Foo>::Bar`, the `DefId`
- /// might refer to the trait `Foo`, and the arguments might be
- /// `[T]`. The boolean value indicates whether to infer values
- /// for arguments whose values were not explicitly provided.
- /// - `provided_kind`: given the generic parameter and the value from `args_for_def_id`,
- /// instantiate a `GenericArg`.
- /// - `inferred_kind`: if no parameter was provided, and inference is enabled, then
- /// creates a suitable inference variable.
- pub fn create_substs_for_generic_args<'a>(
- tcx: TyCtxt<'tcx>,
- def_id: DefId,
- parent_substs: &[subst::GenericArg<'tcx>],
- has_self: bool,
- self_ty: Option<Ty<'tcx>>,
- arg_count: &GenericArgCountResult,
- ctx: &mut impl CreateSubstsForGenericArgsCtxt<'a, 'tcx>,
- ) -> SubstsRef<'tcx> {
- // Collect the segments of the path; we need to substitute arguments
- // for parameters throughout the entire path (wherever there are
- // generic parameters).
- let mut parent_defs = tcx.generics_of(def_id);
- let count = parent_defs.count();
- let mut stack = vec![(def_id, parent_defs)];
- while let Some(def_id) = parent_defs.parent {
- parent_defs = tcx.generics_of(def_id);
- stack.push((def_id, parent_defs));
- }
-
- // We manually build up the substitution, rather than using convenience
- // methods in `subst.rs`, so that we can iterate over the arguments and
- // parameters in lock-step linearly, instead of trying to match each pair.
- let mut substs: SmallVec<[subst::GenericArg<'tcx>; 8]> = SmallVec::with_capacity(count);
- // Iterate over each segment of the path.
- while let Some((def_id, defs)) = stack.pop() {
- let mut params = defs.params.iter().peekable();
-
- // If we have already computed substitutions for parents, we can use those directly.
- while let Some(&param) = params.peek() {
- if let Some(&kind) = parent_substs.get(param.index as usize) {
- substs.push(kind);
- params.next();
- } else {
- break;
- }
- }
-
- // `Self` is handled first, unless it's been handled in `parent_substs`.
- if has_self {
- if let Some(&param) = params.peek() {
- if param.index == 0 {
- if let GenericParamDefKind::Type { .. } = param.kind {
- substs.push(
- self_ty
- .map(|ty| ty.into())
- .unwrap_or_else(|| ctx.inferred_kind(None, param, true)),
- );
- params.next();
- }
- }
- }
- }
-
- // Check whether this segment takes generic arguments and the user has provided any.
- let (generic_args, infer_args) = ctx.args_for_def_id(def_id);
-
- let args_iter = generic_args.iter().flat_map(|generic_args| generic_args.args.iter());
- let mut args = args_iter.clone().peekable();
-
- // If we encounter a type or const when we expect a lifetime, we infer the lifetimes.
- // If we later encounter a lifetime, we know that the arguments were provided in the
- // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be
- // inferred, so we can use it for diagnostics later.
- let mut force_infer_lt = None;
-
- loop {
- // We're going to iterate through the generic arguments that the user
- // provided, matching them with the generic parameters we expect.
- // Mismatches can occur as a result of elided lifetimes, or for malformed
- // input. We try to handle both sensibly.
- match (args.peek(), params.peek()) {
- (Some(&arg), Some(&param)) => {
- match (arg, &param.kind, arg_count.explicit_late_bound) {
- (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _)
- | (
- GenericArg::Type(_) | GenericArg::Infer(_),
- GenericParamDefKind::Type { .. },
- _,
- )
- | (
- GenericArg::Const(_) | GenericArg::Infer(_),
- GenericParamDefKind::Const { .. },
- _,
- ) => {
- substs.push(ctx.provided_kind(param, arg));
- args.next();
- params.next();
- }
- (
- GenericArg::Infer(_) | GenericArg::Type(_) | GenericArg::Const(_),
- GenericParamDefKind::Lifetime,
- _,
- ) => {
- // We expected a lifetime argument, but got a type or const
- // argument. That means we're inferring the lifetimes.
- substs.push(ctx.inferred_kind(None, param, infer_args));
- force_infer_lt = Some((arg, param));
- params.next();
- }
- (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => {
- // We've come across a lifetime when we expected something else in
- // the presence of explicit late bounds. This is most likely
- // due to the presence of the explicit bound so we're just going to
- // ignore it.
- args.next();
- }
- (_, _, _) => {
- // We expected one kind of parameter, but the user provided
- // another. This is an error. However, if we already know that
- // the arguments don't match up with the parameters, we won't issue
- // an additional error, as the user already knows what's wrong.
- if arg_count.correct.is_ok() {
- // We're going to iterate over the parameters to sort them out, and
- // show that order to the user as a possible order for the parameters
- let mut param_types_present = defs
- .params
- .clone()
- .into_iter()
- .map(|param| (param.kind.to_ord(), param))
- .collect::<Vec<(ParamKindOrd, GenericParamDef)>>();
- param_types_present.sort_by_key(|(ord, _)| *ord);
- let (mut param_types_present, ordered_params): (
- Vec<ParamKindOrd>,
- Vec<GenericParamDef>,
- ) = param_types_present.into_iter().unzip();
- param_types_present.dedup();
-
- Self::generic_arg_mismatch_err(
- tcx,
- arg,
- param,
- !args_iter.clone().is_sorted_by_key(|arg| arg.to_ord()),
- Some(&format!(
- "reorder the arguments: {}: `<{}>`",
- param_types_present
- .into_iter()
- .map(|ord| format!("{}s", ord))
- .collect::<Vec<String>>()
- .join(", then "),
- ordered_params
- .into_iter()
- .filter_map(|param| {
- if param.name == kw::SelfUpper {
- None
- } else {
- Some(param.name.to_string())
- }
- })
- .collect::<Vec<String>>()
- .join(", ")
- )),
- );
- }
-
- // We've reported the error, but we want to make sure that this
- // problem doesn't bubble down and create additional, irrelevant
- // errors. In this case, we're simply going to ignore the argument
- // and any following arguments. The rest of the parameters will be
- // inferred.
- while args.next().is_some() {}
- }
- }
- }
-
- (Some(&arg), None) => {
- // We should never be able to reach this point with well-formed input.
- // There are three situations in which we can encounter this issue.
- //
- // 1. The number of arguments is incorrect. In this case, an error
- // will already have been emitted, and we can ignore it.
- // 2. There are late-bound lifetime parameters present, yet the
- // lifetime arguments have also been explicitly specified by the
- // user.
- // 3. We've inferred some lifetimes, which have been provided later (i.e.
- // after a type or const). We want to throw an error in this case.
-
- if arg_count.correct.is_ok()
- && arg_count.explicit_late_bound == ExplicitLateBound::No
- {
- let kind = arg.descr();
- assert_eq!(kind, "lifetime");
- let (provided_arg, param) =
- force_infer_lt.expect("lifetimes ought to have been inferred");
- Self::generic_arg_mismatch_err(tcx, provided_arg, param, false, None);
- }
-
- break;
- }
-
- (None, Some(&param)) => {
- // If there are fewer arguments than parameters, it means
- // we're inferring the remaining arguments.
- substs.push(ctx.inferred_kind(Some(&substs), param, infer_args));
- params.next();
- }
-
- (None, None) => break,
- }
- }
- }
-
- tcx.intern_substs(&substs)
- }
-
- /// Checks that the correct number of generic arguments have been provided.
- /// Used specifically for function calls.
- pub fn check_generic_arg_count_for_call(
- tcx: TyCtxt<'_>,
- span: Span,
- def_id: DefId,
- generics: &ty::Generics,
- seg: &hir::PathSegment<'_>,
- is_method_call: IsMethodCall,
- ) -> GenericArgCountResult {
- let empty_args = hir::GenericArgs::none();
- let gen_args = seg.args.unwrap_or(&empty_args);
- let gen_pos = if is_method_call == IsMethodCall::Yes {
- GenericArgPosition::MethodCall
- } else {
- GenericArgPosition::Value
- };
- let has_self = generics.parent.is_none() && generics.has_self;
-
- Self::check_generic_arg_count(
- tcx,
- span,
- def_id,
- seg,
- generics,
- gen_args,
- gen_pos,
- has_self,
- seg.infer_args,
- )
- }
-
- /// Checks that the correct number of generic arguments have been provided.
- /// This is used both for datatypes and function calls.
- #[instrument(skip(tcx, gen_pos), level = "debug")]
- pub(crate) fn check_generic_arg_count(
- tcx: TyCtxt<'_>,
- span: Span,
- def_id: DefId,
- seg: &hir::PathSegment<'_>,
- gen_params: &ty::Generics,
- gen_args: &hir::GenericArgs<'_>,
- gen_pos: GenericArgPosition,
- has_self: bool,
- infer_args: bool,
- ) -> GenericArgCountResult {
- let default_counts = gen_params.own_defaults();
- let param_counts = gen_params.own_counts();
-
- // Subtracting from param count to ensure type params synthesized from `impl Trait`
- // cannot be explicitly specified.
- let synth_type_param_count = gen_params
- .params
- .iter()
- .filter(|param| {
- matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. })
- })
- .count();
- let named_type_param_count =
- param_counts.types - has_self as usize - synth_type_param_count;
- let infer_lifetimes =
- (gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params();
-
- if gen_pos != GenericArgPosition::Type && !gen_args.bindings.is_empty() {
- Self::prohibit_assoc_ty_binding(tcx, gen_args.bindings[0].span);
- }
-
- let explicit_late_bound =
- Self::prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos);
-
- let mut invalid_args = vec![];
-
- let mut check_lifetime_args =
- |min_expected_args: usize,
- max_expected_args: usize,
- provided_args: usize,
- late_bounds_ignore: bool| {
- if (min_expected_args..=max_expected_args).contains(&provided_args) {
- return Ok(());
- }
-
- if late_bounds_ignore {
- return Ok(());
- }
-
- if provided_args > max_expected_args {
- invalid_args.extend(
- gen_args.args[max_expected_args..provided_args]
- .iter()
- .map(|arg| arg.span()),
- );
- };
-
- let gen_args_info = if provided_args > min_expected_args {
- invalid_args.extend(
- gen_args.args[min_expected_args..provided_args]
- .iter()
- .map(|arg| arg.span()),
- );
- let num_redundant_args = provided_args - min_expected_args;
- GenericArgsInfo::ExcessLifetimes { num_redundant_args }
- } else {
- let num_missing_args = min_expected_args - provided_args;
- GenericArgsInfo::MissingLifetimes { num_missing_args }
- };
-
- let reported = WrongNumberOfGenericArgs::new(
- tcx,
- gen_args_info,
- seg,
- gen_params,
- has_self as usize,
- gen_args,
- def_id,
- )
- .diagnostic()
- .emit();
-
- Err(reported)
- };
-
- let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes };
- let max_expected_lifetime_args = param_counts.lifetimes;
- let num_provided_lifetime_args = gen_args.num_lifetime_params();
-
- let lifetimes_correct = check_lifetime_args(
- min_expected_lifetime_args,
- max_expected_lifetime_args,
- num_provided_lifetime_args,
- explicit_late_bound == ExplicitLateBound::Yes,
- );
-
- let mut check_types_and_consts = |expected_min,
- expected_max,
- expected_max_with_synth,
- provided,
- params_offset,
- args_offset| {
- debug!(
- ?expected_min,
- ?expected_max,
- ?provided,
- ?params_offset,
- ?args_offset,
- "check_types_and_consts"
- );
- if (expected_min..=expected_max).contains(&provided) {
- return Ok(());
- }
-
- let num_default_params = expected_max - expected_min;
-
- let gen_args_info = if provided > expected_max {
- invalid_args.extend(
- gen_args.args[args_offset + expected_max..args_offset + provided]
- .iter()
- .map(|arg| arg.span()),
- );
- let num_redundant_args = provided - expected_max;
-
- // Provide extra note if synthetic arguments like `impl Trait` are specified.
- let synth_provided = provided <= expected_max_with_synth;
-
- GenericArgsInfo::ExcessTypesOrConsts {
- num_redundant_args,
- num_default_params,
- args_offset,
- synth_provided,
- }
- } else {
- let num_missing_args = expected_max - provided;
-
- GenericArgsInfo::MissingTypesOrConsts {
- num_missing_args,
- num_default_params,
- args_offset,
- }
- };
-
- debug!(?gen_args_info);
-
- let reported = WrongNumberOfGenericArgs::new(
- tcx,
- gen_args_info,
- seg,
- gen_params,
- params_offset,
- gen_args,
- def_id,
- )
- .diagnostic()
- .emit_unless(gen_args.has_err());
-
- Err(reported)
- };
-
- let args_correct = {
- let expected_min = if infer_args {
- 0
- } else {
- param_counts.consts + named_type_param_count
- - default_counts.types
- - default_counts.consts
- };
- debug!(?expected_min);
- debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params());
-
- check_types_and_consts(
- expected_min,
- param_counts.consts + named_type_param_count,
- param_counts.consts + named_type_param_count + synth_type_param_count,
- gen_args.num_generic_params(),
- param_counts.lifetimes + has_self as usize,
- gen_args.num_lifetime_params(),
- )
- };
-
- GenericArgCountResult {
- explicit_late_bound,
- correct: lifetimes_correct.and(args_correct).map_err(|reported| {
- GenericArgCountMismatch { reported: Some(reported), invalid_args }
- }),
- }
- }
-
- /// Emits an error regarding forbidden type binding associations
- pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) {
- tcx.sess.emit_err(AssocTypeBindingNotAllowed { span });
- }
-
- /// Prohibits explicit lifetime arguments if late-bound lifetime parameters
- /// are present. This is used both for datatypes and function calls.
- pub(crate) fn prohibit_explicit_late_bound_lifetimes(
- tcx: TyCtxt<'_>,
- def: &ty::Generics,
- args: &hir::GenericArgs<'_>,
- position: GenericArgPosition,
- ) -> ExplicitLateBound {
- let param_counts = def.own_counts();
- let infer_lifetimes = position != GenericArgPosition::Type && !args.has_lifetime_params();
-
- if infer_lifetimes {
- return ExplicitLateBound::No;
- }
-
- if let Some(span_late) = def.has_late_bound_regions {
- let msg = "cannot specify lifetime arguments explicitly \
- if late bound lifetime parameters are present";
- let note = "the late bound lifetime parameter is introduced here";
- let span = args.args[0].span();
-
- if position == GenericArgPosition::Value
- && args.num_lifetime_params() != param_counts.lifetimes
- {
- let mut err = tcx.sess.struct_span_err(span, msg);
- err.span_note(span_late, note);
- err.emit();
- } else {
- let mut multispan = MultiSpan::from_span(span);
- multispan.push_span_label(span_late, note);
- tcx.struct_span_lint_hir(
- LATE_BOUND_LIFETIME_ARGUMENTS,
- args.args[0].id(),
- multispan,
- |lint| {
- lint.build(msg).emit();
- },
- );
- }
-
- ExplicitLateBound::Yes
- } else {
- ExplicitLateBound::No
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
deleted file mode 100644
index 8a5c7fee6..000000000
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ /dev/null
@@ -1,3091 +0,0 @@
-//! Conversion from AST representation of types to the `ty.rs` representation.
-//! The main routine here is `ast_ty_to_ty()`; each use is parameterized by an
-//! instance of `AstConv`.
-
-mod errors;
-mod generics;
-
-use crate::bounds::Bounds;
-use crate::collect::HirPlaceholderCollector;
-use crate::errors::{
- AmbiguousLifetimeBound, MultipleRelaxedDefaultBounds, TraitObjectDeclaredWithNoTraits,
- TypeofReservedKeywordUsed, ValueOfAssociatedStructAlreadySpecified,
-};
-use crate::middle::resolve_lifetime as rl;
-use crate::require_c_abi_if_c_variadic;
-use rustc_ast::TraitObjectSyntax;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{
- struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, MultiSpan,
-};
-use rustc_hir as hir;
-use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
-use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::intravisit::{walk_generics, Visitor as _};
-use rustc_hir::lang_items::LangItem;
-use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
-use rustc_middle::middle::stability::AllowUnstable;
-use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, Subst, SubstsRef};
-use rustc_middle::ty::GenericParamDefKind;
-use rustc_middle::ty::{
- self, Const, DefIdTree, EarlyBinder, IsSuggestable, Ty, TyCtxt, TypeVisitable,
-};
-use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS};
-use rustc_span::edition::Edition;
-use rustc_span::lev_distance::find_best_match_for_name;
-use rustc_span::symbol::{kw, Ident, Symbol};
-use rustc_span::{Span, DUMMY_SP};
-use rustc_target::spec::abi;
-use rustc_trait_selection::traits;
-use rustc_trait_selection::traits::astconv_object_safety_violations;
-use rustc_trait_selection::traits::error_reporting::{
- report_object_safety_error, suggestions::NextTypeParamName,
-};
-use rustc_trait_selection::traits::wf::object_region_bounds;
-
-use smallvec::SmallVec;
-use std::collections::BTreeSet;
-use std::slice;
-
-#[derive(Debug)]
-pub struct PathSeg(pub DefId, pub usize);
-
-pub trait AstConv<'tcx> {
- fn tcx<'a>(&'a self) -> TyCtxt<'tcx>;
-
- fn item_def_id(&self) -> Option<DefId>;
-
- /// Returns predicates in scope of the form `X: Foo<T>`, where `X`
- /// is a type parameter `X` with the given id `def_id` and T
- /// matches `assoc_name`. This is a subset of the full set of
- /// predicates.
- ///
- /// This is used for one specific purpose: resolving "short-hand"
- /// associated type references like `T::Item`. In principle, we
- /// would do that by first getting the full set of predicates in
- /// scope and then filtering down to find those that apply to `T`,
- /// but this can lead to cycle errors. The problem is that we have
- /// to do this resolution *in order to create the predicates in
- /// the first place*. Hence, we have this "special pass".
- fn get_type_parameter_bounds(
- &self,
- span: Span,
- def_id: DefId,
- assoc_name: Ident,
- ) -> ty::GenericPredicates<'tcx>;
-
- /// Returns the lifetime to use when a lifetime is omitted (and not elided).
- fn re_infer(&self, param: Option<&ty::GenericParamDef>, span: Span)
- -> Option<ty::Region<'tcx>>;
-
- /// Returns the type to use when a type is omitted.
- fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx>;
-
- /// Returns `true` if `_` is allowed in type signatures in the current context.
- fn allow_ty_infer(&self) -> bool;
-
- /// Returns the const to use when a const is omitted.
- fn ct_infer(
- &self,
- ty: Ty<'tcx>,
- param: Option<&ty::GenericParamDef>,
- span: Span,
- ) -> Const<'tcx>;
-
- /// Projecting an associated type from a (potentially)
- /// higher-ranked trait reference is more complicated, because of
- /// the possibility of late-bound regions appearing in the
- /// associated type binding. This is not legal in function
- /// signatures for that reason. In a function body, we can always
- /// handle it because we can use inference variables to remove the
- /// late-bound regions.
- fn projected_ty_from_poly_trait_ref(
- &self,
- span: Span,
- item_def_id: DefId,
- item_segment: &hir::PathSegment<'_>,
- poly_trait_ref: ty::PolyTraitRef<'tcx>,
- ) -> Ty<'tcx>;
-
- /// Normalize an associated type coming from the user.
- fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>;
-
- /// Invoked when we encounter an error from some prior pass
- /// (e.g., resolve) that is translated into a ty-error. This is
- /// used to help suppress derived errors typeck might otherwise
- /// report.
- fn set_tainted_by_errors(&self);
-
- fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span);
-}
-
-#[derive(Debug)]
-struct ConvertedBinding<'a, 'tcx> {
- hir_id: hir::HirId,
- item_name: Ident,
- kind: ConvertedBindingKind<'a, 'tcx>,
- gen_args: &'a GenericArgs<'a>,
- span: Span,
-}
-
-#[derive(Debug)]
-enum ConvertedBindingKind<'a, 'tcx> {
- Equality(ty::Term<'tcx>),
- Constraint(&'a [hir::GenericBound<'a>]),
-}
-
-/// New-typed boolean indicating whether explicit late-bound lifetimes
-/// are present in a set of generic arguments.
-///
-/// For example if we have some method `fn f<'a>(&'a self)` implemented
-/// for some type `T`, although `f` is generic in the lifetime `'a`, `'a`
-/// is late-bound so should not be provided explicitly. Thus, if `f` is
-/// instantiated with some generic arguments providing `'a` explicitly,
-/// we taint those arguments with `ExplicitLateBound::Yes` so that we
-/// can provide an appropriate diagnostic later.
-#[derive(Copy, Clone, PartialEq)]
-pub enum ExplicitLateBound {
- Yes,
- No,
-}
-
-#[derive(Copy, Clone, PartialEq)]
-pub enum IsMethodCall {
- Yes,
- No,
-}
-
-/// Denotes the "position" of a generic argument, indicating if it is a generic type,
-/// generic function or generic method call.
-#[derive(Copy, Clone, PartialEq)]
-pub(crate) enum GenericArgPosition {
- Type,
- Value, // e.g., functions
- MethodCall,
-}
-
-/// A marker denoting that the generic arguments that were
-/// provided did not match the respective generic parameters.
-#[derive(Clone, Default)]
-pub struct GenericArgCountMismatch {
- /// Indicates whether a fatal error was reported (`Some`), or just a lint (`None`).
- pub reported: Option<ErrorGuaranteed>,
- /// A list of spans of arguments provided that were not valid.
- pub invalid_args: Vec<Span>,
-}
-
-/// Decorates the result of a generic argument count mismatch
-/// check with whether explicit late bounds were provided.
-#[derive(Clone)]
-pub struct GenericArgCountResult {
- pub explicit_late_bound: ExplicitLateBound,
- pub correct: Result<(), GenericArgCountMismatch>,
-}
-
-pub trait CreateSubstsForGenericArgsCtxt<'a, 'tcx> {
- fn args_for_def_id(&mut self, def_id: DefId) -> (Option<&'a GenericArgs<'a>>, bool);
-
- fn provided_kind(
- &mut self,
- param: &ty::GenericParamDef,
- arg: &GenericArg<'_>,
- ) -> subst::GenericArg<'tcx>;
-
- fn inferred_kind(
- &mut self,
- substs: Option<&[subst::GenericArg<'tcx>]>,
- param: &ty::GenericParamDef,
- infer_args: bool,
- ) -> subst::GenericArg<'tcx>;
-}
-
-impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
- #[tracing::instrument(level = "debug", skip(self))]
- pub fn ast_region_to_region(
- &self,
- lifetime: &hir::Lifetime,
- def: Option<&ty::GenericParamDef>,
- ) -> ty::Region<'tcx> {
- let tcx = self.tcx();
- let lifetime_name = |def_id| tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id));
-
- let r = match tcx.named_region(lifetime.hir_id) {
- Some(rl::Region::Static) => tcx.lifetimes.re_static,
-
- Some(rl::Region::LateBound(debruijn, index, def_id)) => {
- let name = lifetime_name(def_id.expect_local());
- let br = ty::BoundRegion {
- var: ty::BoundVar::from_u32(index),
- kind: ty::BrNamed(def_id, name),
- };
- tcx.mk_region(ty::ReLateBound(debruijn, br))
- }
-
- Some(rl::Region::EarlyBound(index, id)) => {
- let name = lifetime_name(id.expect_local());
- tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, index, name }))
- }
-
- Some(rl::Region::Free(scope, id)) => {
- let name = lifetime_name(id.expect_local());
- tcx.mk_region(ty::ReFree(ty::FreeRegion {
- scope,
- bound_region: ty::BrNamed(id, name),
- }))
-
- // (*) -- not late-bound, won't change
- }
-
- None => {
- self.re_infer(def, lifetime.span).unwrap_or_else(|| {
- debug!(?lifetime, "unelided lifetime in signature");
-
- // This indicates an illegal lifetime
- // elision. `resolve_lifetime` should have
- // reported an error in this case -- but if
- // not, let's error out.
- tcx.sess.delay_span_bug(lifetime.span, "unelided lifetime in signature");
-
- // Supply some dummy value. We don't have an
- // `re_error`, annoyingly, so use `'static`.
- tcx.lifetimes.re_static
- })
- }
- };
-
- debug!("ast_region_to_region(lifetime={:?}) yields {:?}", lifetime, r);
-
- r
- }
-
- /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`,
- /// returns an appropriate set of substitutions for this particular reference to `I`.
- pub fn ast_path_substs_for_ty(
- &self,
- span: Span,
- def_id: DefId,
- item_segment: &hir::PathSegment<'_>,
- ) -> SubstsRef<'tcx> {
- let (substs, _) = self.create_substs_for_ast_path(
- span,
- def_id,
- &[],
- item_segment,
- item_segment.args(),
- item_segment.infer_args,
- None,
- );
- let assoc_bindings = self.create_assoc_bindings_for_generic_args(item_segment.args());
-
- if let Some(b) = assoc_bindings.first() {
- Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
- }
-
- substs
- }
-
- /// Given the type/lifetime/const arguments provided to some path (along with
- /// an implicit `Self`, if this is a trait reference), returns the complete
- /// set of substitutions. This may involve applying defaulted type parameters.
- /// Constraints on associated types are created from `create_assoc_bindings_for_generic_args`.
- ///
- /// Example:
- ///
- /// ```ignore (illustrative)
- /// T: std::ops::Index<usize, Output = u32>
- /// // ^1 ^^^^^^^^^^^^^^2 ^^^^3 ^^^^^^^^^^^4
- /// ```
- ///
- /// 1. The `self_ty` here would refer to the type `T`.
- /// 2. The path in question is the path to the trait `std::ops::Index`,
- /// which will have been resolved to a `def_id`
- /// 3. The `generic_args` contains info on the `<...>` contents. The `usize` type
- /// parameters are returned in the `SubstsRef`, the associated type bindings like
- /// `Output = u32` are returned from `create_assoc_bindings_for_generic_args`.
- ///
- /// Note that the type listing given here is *exactly* what the user provided.
- ///
- /// For (generic) associated types
- ///
- /// ```ignore (illustrative)
- /// <Vec<u8> as Iterable<u8>>::Iter::<'a>
- /// ```
- ///
- /// We have the parent substs are the substs for the parent trait:
- /// `[Vec<u8>, u8]` and `generic_args` are the arguments for the associated
- /// type itself: `['a]`. The returned `SubstsRef` concatenates these two
- /// lists: `[Vec<u8>, u8, 'a]`.
- #[tracing::instrument(level = "debug", skip(self, span))]
- fn create_substs_for_ast_path<'a>(
- &self,
- span: Span,
- def_id: DefId,
- parent_substs: &[subst::GenericArg<'tcx>],
- seg: &hir::PathSegment<'_>,
- generic_args: &'a hir::GenericArgs<'_>,
- infer_args: bool,
- self_ty: Option<Ty<'tcx>>,
- ) -> (SubstsRef<'tcx>, GenericArgCountResult) {
- // If the type is parameterized by this region, then replace this
- // region with the current anon region binding (in other words,
- // whatever & would get replaced with).
-
- let tcx = self.tcx();
- let generics = tcx.generics_of(def_id);
- debug!("generics: {:?}", generics);
-
- if generics.has_self {
- if generics.parent.is_some() {
- // The parent is a trait so it should have at least one subst
- // for the `Self` type.
- assert!(!parent_substs.is_empty())
- } else {
- // This item (presumably a trait) needs a self-type.
- assert!(self_ty.is_some());
- }
- } else {
- assert!(self_ty.is_none() && parent_substs.is_empty());
- }
-
- let arg_count = Self::check_generic_arg_count(
- tcx,
- span,
- def_id,
- seg,
- generics,
- generic_args,
- GenericArgPosition::Type,
- self_ty.is_some(),
- infer_args,
- );
-
- // Skip processing if type has no generic parameters.
- // Traits always have `Self` as a generic parameter, which means they will not return early
- // here and so associated type bindings will be handled regardless of whether there are any
- // non-`Self` generic parameters.
- if generics.params.is_empty() {
- return (tcx.intern_substs(&[]), arg_count);
- }
-
- let is_object = self_ty.map_or(false, |ty| ty == self.tcx().types.trait_object_dummy_self);
-
- struct SubstsForAstPathCtxt<'a, 'tcx> {
- astconv: &'a (dyn AstConv<'tcx> + 'a),
- def_id: DefId,
- generic_args: &'a GenericArgs<'a>,
- span: Span,
- missing_type_params: Vec<Symbol>,
- inferred_params: Vec<Span>,
- infer_args: bool,
- is_object: bool,
- }
-
- impl<'tcx, 'a> SubstsForAstPathCtxt<'tcx, 'a> {
- fn default_needs_object_self(&mut self, param: &ty::GenericParamDef) -> bool {
- let tcx = self.astconv.tcx();
- if let GenericParamDefKind::Type { has_default, .. } = param.kind {
- if self.is_object && has_default {
- let default_ty = tcx.at(self.span).type_of(param.def_id);
- let self_param = tcx.types.self_param;
- if default_ty.walk().any(|arg| arg == self_param.into()) {
- // There is no suitable inference default for a type parameter
- // that references self, in an object type.
- return true;
- }
- }
- }
-
- false
- }
- }
-
- impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for SubstsForAstPathCtxt<'a, 'tcx> {
- fn args_for_def_id(&mut self, did: DefId) -> (Option<&'a GenericArgs<'a>>, bool) {
- if did == self.def_id {
- (Some(self.generic_args), self.infer_args)
- } else {
- // The last component of this tuple is unimportant.
- (None, false)
- }
- }
-
- fn provided_kind(
- &mut self,
- param: &ty::GenericParamDef,
- arg: &GenericArg<'_>,
- ) -> subst::GenericArg<'tcx> {
- let tcx = self.astconv.tcx();
-
- let mut handle_ty_args = |has_default, ty: &hir::Ty<'_>| {
- if has_default {
- tcx.check_optional_stability(
- param.def_id,
- Some(arg.id()),
- arg.span(),
- None,
- AllowUnstable::No,
- |_, _| {
- // Default generic parameters may not be marked
- // with stability attributes, i.e. when the
- // default parameter was defined at the same time
- // as the rest of the type. As such, we ignore missing
- // stability attributes.
- },
- );
- }
- if let (hir::TyKind::Infer, false) = (&ty.kind, self.astconv.allow_ty_infer()) {
- self.inferred_params.push(ty.span);
- tcx.ty_error().into()
- } else {
- self.astconv.ast_ty_to_ty(ty).into()
- }
- };
-
- match (&param.kind, arg) {
- (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
- self.astconv.ast_region_to_region(lt, Some(param)).into()
- }
- (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => {
- handle_ty_args(has_default, ty)
- }
- (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Infer(inf)) => {
- handle_ty_args(has_default, &inf.to_ty())
- }
- (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
- ty::Const::from_opt_const_arg_anon_const(
- tcx,
- ty::WithOptConstParam {
- did: tcx.hir().local_def_id(ct.value.hir_id),
- const_param_did: Some(param.def_id),
- },
- )
- .into()
- }
- (&GenericParamDefKind::Const { .. }, hir::GenericArg::Infer(inf)) => {
- let ty = tcx.at(self.span).type_of(param.def_id);
- if self.astconv.allow_ty_infer() {
- self.astconv.ct_infer(ty, Some(param), inf.span).into()
- } else {
- self.inferred_params.push(inf.span);
- tcx.const_error(ty).into()
- }
- }
- _ => unreachable!(),
- }
- }
-
- fn inferred_kind(
- &mut self,
- substs: Option<&[subst::GenericArg<'tcx>]>,
- param: &ty::GenericParamDef,
- infer_args: bool,
- ) -> subst::GenericArg<'tcx> {
- let tcx = self.astconv.tcx();
- match param.kind {
- GenericParamDefKind::Lifetime => self
- .astconv
- .re_infer(Some(param), self.span)
- .unwrap_or_else(|| {
- debug!(?param, "unelided lifetime in signature");
-
- // This indicates an illegal lifetime in a non-assoc-trait position
- tcx.sess.delay_span_bug(self.span, "unelided lifetime in signature");
-
- // Supply some dummy value. We don't have an
- // `re_error`, annoyingly, so use `'static`.
- tcx.lifetimes.re_static
- })
- .into(),
- GenericParamDefKind::Type { has_default, .. } => {
- if !infer_args && has_default {
- // No type parameter provided, but a default exists.
-
- // If we are converting an object type, then the
- // `Self` parameter is unknown. However, some of the
- // other type parameters may reference `Self` in their
- // defaults. This will lead to an ICE if we are not
- // careful!
- if self.default_needs_object_self(param) {
- self.missing_type_params.push(param.name);
- tcx.ty_error().into()
- } else {
- // This is a default type parameter.
- let substs = substs.unwrap();
- if substs.iter().any(|arg| match arg.unpack() {
- GenericArgKind::Type(ty) => ty.references_error(),
- _ => false,
- }) {
- // Avoid ICE #86756 when type error recovery goes awry.
- return tcx.ty_error().into();
- }
- self.astconv
- .normalize_ty(
- self.span,
- EarlyBinder(tcx.at(self.span).type_of(param.def_id))
- .subst(tcx, substs),
- )
- .into()
- }
- } else if infer_args {
- // No type parameters were provided, we can infer all.
- let param = if !self.default_needs_object_self(param) {
- Some(param)
- } else {
- None
- };
- self.astconv.ty_infer(param, self.span).into()
- } else {
- // We've already errored above about the mismatch.
- tcx.ty_error().into()
- }
- }
- GenericParamDefKind::Const { has_default } => {
- let ty = tcx.at(self.span).type_of(param.def_id);
- if !infer_args && has_default {
- tcx.bound_const_param_default(param.def_id)
- .subst(tcx, substs.unwrap())
- .into()
- } else {
- if infer_args {
- self.astconv.ct_infer(ty, Some(param), self.span).into()
- } else {
- // We've already errored above about the mismatch.
- tcx.const_error(ty).into()
- }
- }
- }
- }
- }
- }
-
- let mut substs_ctx = SubstsForAstPathCtxt {
- astconv: self,
- def_id,
- span,
- generic_args,
- missing_type_params: vec![],
- inferred_params: vec![],
- infer_args,
- is_object,
- };
- let substs = Self::create_substs_for_generic_args(
- tcx,
- def_id,
- parent_substs,
- self_ty.is_some(),
- self_ty,
- &arg_count,
- &mut substs_ctx,
- );
-
- self.complain_about_missing_type_params(
- substs_ctx.missing_type_params,
- def_id,
- span,
- generic_args.args.is_empty(),
- );
-
- debug!(
- "create_substs_for_ast_path(generic_params={:?}, self_ty={:?}) -> {:?}",
- generics, self_ty, substs
- );
-
- (substs, arg_count)
- }
-
- fn create_assoc_bindings_for_generic_args<'a>(
- &self,
- generic_args: &'a hir::GenericArgs<'_>,
- ) -> Vec<ConvertedBinding<'a, 'tcx>> {
- // Convert associated-type bindings or constraints into a separate vector.
- // Example: Given this:
- //
- // T: Iterator<Item = u32>
- //
- // The `T` is passed in as a self-type; the `Item = u32` is
- // not a "type parameter" of the `Iterator` trait, but rather
- // a restriction on `<T as Iterator>::Item`, so it is passed
- // back separately.
- let assoc_bindings = generic_args
- .bindings
- .iter()
- .map(|binding| {
- let kind = match binding.kind {
- hir::TypeBindingKind::Equality { ref term } => match term {
- hir::Term::Ty(ref ty) => {
- ConvertedBindingKind::Equality(self.ast_ty_to_ty(ty).into())
- }
- hir::Term::Const(ref c) => {
- let local_did = self.tcx().hir().local_def_id(c.hir_id);
- let c = Const::from_anon_const(self.tcx(), local_did);
- ConvertedBindingKind::Equality(c.into())
- }
- },
- hir::TypeBindingKind::Constraint { ref bounds } => {
- ConvertedBindingKind::Constraint(bounds)
- }
- };
- ConvertedBinding {
- hir_id: binding.hir_id,
- item_name: binding.ident,
- kind,
- gen_args: binding.gen_args,
- span: binding.span,
- }
- })
- .collect();
-
- assoc_bindings
- }
-
- pub(crate) fn create_substs_for_associated_item(
- &self,
- tcx: TyCtxt<'tcx>,
- span: Span,
- item_def_id: DefId,
- item_segment: &hir::PathSegment<'_>,
- parent_substs: SubstsRef<'tcx>,
- ) -> SubstsRef<'tcx> {
- debug!(
- "create_substs_for_associated_item(span: {:?}, item_def_id: {:?}, item_segment: {:?}",
- span, item_def_id, item_segment
- );
- if tcx.generics_of(item_def_id).params.is_empty() {
- self.prohibit_generics(slice::from_ref(item_segment).iter(), |_| {});
-
- parent_substs
- } else {
- self.create_substs_for_ast_path(
- span,
- item_def_id,
- parent_substs,
- item_segment,
- item_segment.args(),
- item_segment.infer_args,
- None,
- )
- .0
- }
- }
-
- /// Instantiates the path for the given trait reference, assuming that it's
- /// bound to a valid trait type. Returns the `DefId` of the defining trait.
- /// The type _cannot_ be a type other than a trait type.
- ///
- /// If the `projections` argument is `None`, then assoc type bindings like `Foo<T = X>`
- /// are disallowed. Otherwise, they are pushed onto the vector given.
- pub fn instantiate_mono_trait_ref(
- &self,
- trait_ref: &hir::TraitRef<'_>,
- self_ty: Ty<'tcx>,
- ) -> ty::TraitRef<'tcx> {
- self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});
-
- self.ast_path_to_mono_trait_ref(
- trait_ref.path.span,
- trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()),
- self_ty,
- trait_ref.path.segments.last().unwrap(),
- true,
- )
- }
-
- fn instantiate_poly_trait_ref_inner(
- &self,
- hir_id: hir::HirId,
- span: Span,
- binding_span: Option<Span>,
- constness: ty::BoundConstness,
- bounds: &mut Bounds<'tcx>,
- speculative: bool,
- trait_ref_span: Span,
- trait_def_id: DefId,
- trait_segment: &hir::PathSegment<'_>,
- args: &GenericArgs<'_>,
- infer_args: bool,
- self_ty: Ty<'tcx>,
- ) -> GenericArgCountResult {
- let (substs, arg_count) = self.create_substs_for_ast_path(
- trait_ref_span,
- trait_def_id,
- &[],
- trait_segment,
- args,
- infer_args,
- Some(self_ty),
- );
-
- let tcx = self.tcx();
- let bound_vars = tcx.late_bound_vars(hir_id);
- debug!(?bound_vars);
-
- let assoc_bindings = self.create_assoc_bindings_for_generic_args(args);
-
- let poly_trait_ref =
- ty::Binder::bind_with_vars(ty::TraitRef::new(trait_def_id, substs), bound_vars);
-
- debug!(?poly_trait_ref, ?assoc_bindings);
- bounds.trait_bounds.push((poly_trait_ref, span, constness));
-
- let mut dup_bindings = FxHashMap::default();
- for binding in &assoc_bindings {
- // Specify type to assert that error was already reported in `Err` case.
- let _: Result<_, ErrorGuaranteed> = self.add_predicates_for_ast_type_binding(
- hir_id,
- poly_trait_ref,
- binding,
- bounds,
- speculative,
- &mut dup_bindings,
- binding_span.unwrap_or(binding.span),
- );
- // Okay to ignore `Err` because of `ErrorGuaranteed` (see above).
- }
-
- arg_count
- }
-
- /// Given a trait bound like `Debug`, applies that trait bound the given self-type to construct
- /// a full trait reference. The resulting trait reference is returned. This may also generate
- /// auxiliary bounds, which are added to `bounds`.
- ///
- /// Example:
- ///
- /// ```ignore (illustrative)
- /// poly_trait_ref = Iterator<Item = u32>
- /// self_ty = Foo
- /// ```
- ///
- /// this would return `Foo: Iterator` and add `<Foo as Iterator>::Item = u32` into `bounds`.
- ///
- /// **A note on binders:** against our usual convention, there is an implied bounder around
- /// the `self_ty` and `poly_trait_ref` parameters here. So they may reference bound regions.
- /// If for example you had `for<'a> Foo<'a>: Bar<'a>`, then the `self_ty` would be `Foo<'a>`
- /// where `'a` is a bound region at depth 0. Similarly, the `poly_trait_ref` would be
- /// `Bar<'a>`. The returned poly-trait-ref will have this binder instantiated explicitly,
- /// however.
- #[tracing::instrument(level = "debug", skip(self, span, constness, bounds, speculative))]
- pub(crate) fn instantiate_poly_trait_ref(
- &self,
- trait_ref: &hir::TraitRef<'_>,
- span: Span,
- constness: ty::BoundConstness,
- self_ty: Ty<'tcx>,
- bounds: &mut Bounds<'tcx>,
- speculative: bool,
- ) -> GenericArgCountResult {
- let hir_id = trait_ref.hir_ref_id;
- let binding_span = None;
- let trait_ref_span = trait_ref.path.span;
- let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise());
- let trait_segment = trait_ref.path.segments.last().unwrap();
- let args = trait_segment.args();
- let infer_args = trait_segment.infer_args;
-
- self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});
- self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false);
-
- self.instantiate_poly_trait_ref_inner(
- hir_id,
- span,
- binding_span,
- constness,
- bounds,
- speculative,
- trait_ref_span,
- trait_def_id,
- trait_segment,
- args,
- infer_args,
- self_ty,
- )
- }
-
- pub(crate) fn instantiate_lang_item_trait_ref(
- &self,
- lang_item: hir::LangItem,
- span: Span,
- hir_id: hir::HirId,
- args: &GenericArgs<'_>,
- self_ty: Ty<'tcx>,
- bounds: &mut Bounds<'tcx>,
- ) {
- let binding_span = Some(span);
- let constness = ty::BoundConstness::NotConst;
- let speculative = false;
- let trait_ref_span = span;
- let trait_def_id = self.tcx().require_lang_item(lang_item, Some(span));
- let trait_segment = &hir::PathSegment::invalid();
- let infer_args = false;
-
- self.instantiate_poly_trait_ref_inner(
- hir_id,
- span,
- binding_span,
- constness,
- bounds,
- speculative,
- trait_ref_span,
- trait_def_id,
- trait_segment,
- args,
- infer_args,
- self_ty,
- );
- }
-
- fn ast_path_to_mono_trait_ref(
- &self,
- span: Span,
- trait_def_id: DefId,
- self_ty: Ty<'tcx>,
- trait_segment: &hir::PathSegment<'_>,
- is_impl: bool,
- ) -> ty::TraitRef<'tcx> {
- let (substs, _) = self.create_substs_for_ast_trait_ref(
- span,
- trait_def_id,
- self_ty,
- trait_segment,
- is_impl,
- );
- let assoc_bindings = self.create_assoc_bindings_for_generic_args(trait_segment.args());
- if let Some(b) = assoc_bindings.first() {
- Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
- }
- ty::TraitRef::new(trait_def_id, substs)
- }
-
- #[tracing::instrument(level = "debug", skip(self, span))]
- fn create_substs_for_ast_trait_ref<'a>(
- &self,
- span: Span,
- trait_def_id: DefId,
- self_ty: Ty<'tcx>,
- trait_segment: &'a hir::PathSegment<'a>,
- is_impl: bool,
- ) -> (SubstsRef<'tcx>, GenericArgCountResult) {
- self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl);
-
- self.create_substs_for_ast_path(
- span,
- trait_def_id,
- &[],
- trait_segment,
- trait_segment.args(),
- trait_segment.infer_args,
- Some(self_ty),
- )
- }
-
- fn trait_defines_associated_type_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool {
- self.tcx()
- .associated_items(trait_def_id)
- .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, trait_def_id)
- .is_some()
- }
- fn trait_defines_associated_const_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool {
- self.tcx()
- .associated_items(trait_def_id)
- .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Const, trait_def_id)
- .is_some()
- }
-
- // Sets `implicitly_sized` to true on `Bounds` if necessary
- pub(crate) fn add_implicitly_sized<'hir>(
- &self,
- bounds: &mut Bounds<'hir>,
- ast_bounds: &'hir [hir::GenericBound<'hir>],
- self_ty_where_predicates: Option<(hir::HirId, &'hir [hir::WherePredicate<'hir>])>,
- span: Span,
- ) {
- let tcx = self.tcx();
-
- // Try to find an unbound in bounds.
- let mut unbound = None;
- let mut search_bounds = |ast_bounds: &'hir [hir::GenericBound<'hir>]| {
- for ab in ast_bounds {
- if let hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = ab {
- if unbound.is_none() {
- unbound = Some(&ptr.trait_ref);
- } else {
- tcx.sess.emit_err(MultipleRelaxedDefaultBounds { span });
- }
- }
- }
- };
- search_bounds(ast_bounds);
- if let Some((self_ty, where_clause)) = self_ty_where_predicates {
- let self_ty_def_id = tcx.hir().local_def_id(self_ty).to_def_id();
- for clause in where_clause {
- if let hir::WherePredicate::BoundPredicate(pred) = clause {
- if pred.is_param_bound(self_ty_def_id) {
- search_bounds(pred.bounds);
- }
- }
- }
- }
-
- let sized_def_id = tcx.lang_items().require(LangItem::Sized);
- match (&sized_def_id, unbound) {
- (Ok(sized_def_id), Some(tpb))
- if tpb.path.res == Res::Def(DefKind::Trait, *sized_def_id) =>
- {
- // There was in fact a `?Sized` bound, return without doing anything
- return;
- }
- (_, Some(_)) => {
- // There was a `?Trait` bound, but it was not `?Sized`; warn.
- tcx.sess.span_warn(
- span,
- "default bound relaxed for a type parameter, but \
- this does nothing because the given bound is not \
- a default; only `?Sized` is supported",
- );
- // Otherwise, add implicitly sized if `Sized` is available.
- }
- _ => {
- // There was no `?Sized` bound; add implicitly sized if `Sized` is available.
- }
- }
- if sized_def_id.is_err() {
- // No lang item for `Sized`, so we can't add it as a bound.
- return;
- }
- bounds.implicitly_sized = Some(span);
- }
-
- /// This helper takes a *converted* parameter type (`param_ty`)
- /// and an *unconverted* list of bounds:
- ///
- /// ```text
- /// fn foo<T: Debug>
- /// ^ ^^^^^ `ast_bounds` parameter, in HIR form
- /// |
- /// `param_ty`, in ty form
- /// ```
- ///
- /// It adds these `ast_bounds` into the `bounds` structure.
- ///
- /// **A note on binders:** there is an implied binder around
- /// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref`
- /// for more details.
- #[tracing::instrument(level = "debug", skip(self, ast_bounds, bounds))]
- pub(crate) fn add_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'hir>>>(
- &self,
- param_ty: Ty<'tcx>,
- ast_bounds: I,
- bounds: &mut Bounds<'tcx>,
- bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
- ) {
- for ast_bound in ast_bounds {
- match ast_bound {
- hir::GenericBound::Trait(poly_trait_ref, modifier) => {
- let constness = match modifier {
- hir::TraitBoundModifier::MaybeConst => ty::BoundConstness::ConstIfConst,
- hir::TraitBoundModifier::None => ty::BoundConstness::NotConst,
- hir::TraitBoundModifier::Maybe => continue,
- };
-
- let _ = self.instantiate_poly_trait_ref(
- &poly_trait_ref.trait_ref,
- poly_trait_ref.span,
- constness,
- param_ty,
- bounds,
- false,
- );
- }
- &hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => {
- self.instantiate_lang_item_trait_ref(
- lang_item, span, hir_id, args, param_ty, bounds,
- );
- }
- hir::GenericBound::Outlives(lifetime) => {
- let region = self.ast_region_to_region(lifetime, None);
- bounds
- .region_bounds
- .push((ty::Binder::bind_with_vars(region, bound_vars), lifetime.span));
- }
- }
- }
- }
-
- /// Translates a list of bounds from the HIR into the `Bounds` data structure.
- /// The self-type for the bounds is given by `param_ty`.
- ///
- /// Example:
- ///
- /// ```ignore (illustrative)
- /// fn foo<T: Bar + Baz>() { }
- /// // ^ ^^^^^^^^^ ast_bounds
- /// // param_ty
- /// ```
- ///
- /// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be
- /// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the
- /// example above, but is not true in supertrait listings like `trait Foo: Bar + Baz`.
- ///
- /// `span` should be the declaration size of the parameter.
- pub(crate) fn compute_bounds(
- &self,
- param_ty: Ty<'tcx>,
- ast_bounds: &[hir::GenericBound<'_>],
- ) -> Bounds<'tcx> {
- self.compute_bounds_inner(param_ty, ast_bounds)
- }
-
- /// Convert the bounds in `ast_bounds` that refer to traits which define an associated type
- /// named `assoc_name` into ty::Bounds. Ignore the rest.
- pub(crate) fn compute_bounds_that_match_assoc_type(
- &self,
- param_ty: Ty<'tcx>,
- ast_bounds: &[hir::GenericBound<'_>],
- assoc_name: Ident,
- ) -> Bounds<'tcx> {
- let mut result = Vec::new();
-
- for ast_bound in ast_bounds {
- if let Some(trait_ref) = ast_bound.trait_ref()
- && let Some(trait_did) = trait_ref.trait_def_id()
- && self.tcx().trait_may_define_assoc_type(trait_did, assoc_name)
- {
- result.push(ast_bound.clone());
- }
- }
-
- self.compute_bounds_inner(param_ty, &result)
- }
-
- fn compute_bounds_inner(
- &self,
- param_ty: Ty<'tcx>,
- ast_bounds: &[hir::GenericBound<'_>],
- ) -> Bounds<'tcx> {
- let mut bounds = Bounds::default();
-
- self.add_bounds(param_ty, ast_bounds.iter(), &mut bounds, ty::List::empty());
- debug!(?bounds);
-
- bounds
- }
-
- /// Given an HIR binding like `Item = Foo` or `Item: Foo`, pushes the corresponding predicates
- /// onto `bounds`.
- ///
- /// **A note on binders:** given something like `T: for<'a> Iterator<Item = &'a u32>`, the
- /// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside*
- /// the binder (e.g., `&'a u32`) and hence may reference bound regions.
- #[tracing::instrument(
- level = "debug",
- skip(self, bounds, speculative, dup_bindings, path_span)
- )]
- fn add_predicates_for_ast_type_binding(
- &self,
- hir_ref_id: hir::HirId,
- trait_ref: ty::PolyTraitRef<'tcx>,
- binding: &ConvertedBinding<'_, 'tcx>,
- bounds: &mut Bounds<'tcx>,
- speculative: bool,
- dup_bindings: &mut FxHashMap<DefId, Span>,
- path_span: Span,
- ) -> Result<(), ErrorGuaranteed> {
- // Given something like `U: SomeTrait<T = X>`, we want to produce a
- // predicate like `<U as SomeTrait>::T = X`. This is somewhat
- // subtle in the event that `T` is defined in a supertrait of
- // `SomeTrait`, because in that case we need to upcast.
- //
- // That is, consider this case:
- //
- // ```
- // trait SubTrait: SuperTrait<i32> { }
- // trait SuperTrait<A> { type T; }
- //
- // ... B: SubTrait<T = foo> ...
- // ```
- //
- // We want to produce `<B as SuperTrait<i32>>::T == foo`.
-
- let tcx = self.tcx();
-
- let candidate =
- if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) {
- // Simple case: X is defined in the current trait.
- trait_ref
- } else {
- // Otherwise, we have to walk through the supertraits to find
- // those that do.
- self.one_bound_for_assoc_type(
- || traits::supertraits(tcx, trait_ref),
- || trait_ref.print_only_trait_path().to_string(),
- binding.item_name,
- path_span,
- || match binding.kind {
- ConvertedBindingKind::Equality(ty) => Some(ty.to_string()),
- _ => None,
- },
- )?
- };
-
- let (assoc_ident, def_scope) =
- tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id);
-
- // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
- // of calling `filter_by_name_and_kind`.
- let find_item_of_kind = |kind| {
- tcx.associated_items(candidate.def_id())
- .filter_by_name_unhygienic(assoc_ident.name)
- .find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
- };
- let assoc_item = find_item_of_kind(ty::AssocKind::Type)
- .or_else(|| find_item_of_kind(ty::AssocKind::Const))
- .expect("missing associated type");
-
- if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) {
- tcx.sess
- .struct_span_err(
- binding.span,
- &format!("{} `{}` is private", assoc_item.kind, binding.item_name),
- )
- .span_label(binding.span, &format!("private {}", assoc_item.kind))
- .emit();
- }
- tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None);
-
- if !speculative {
- dup_bindings
- .entry(assoc_item.def_id)
- .and_modify(|prev_span| {
- self.tcx().sess.emit_err(ValueOfAssociatedStructAlreadySpecified {
- span: binding.span,
- prev_span: *prev_span,
- item_name: binding.item_name,
- def_path: tcx.def_path_str(assoc_item.container_id(tcx)),
- });
- })
- .or_insert(binding.span);
- }
-
- // Include substitutions for generic parameters of associated types
- let projection_ty = candidate.map_bound(|trait_ref| {
- let ident = Ident::new(assoc_item.name, binding.item_name.span);
- let item_segment = hir::PathSegment {
- ident,
- hir_id: Some(binding.hir_id),
- res: None,
- args: Some(binding.gen_args),
- infer_args: false,
- };
-
- let substs_trait_ref_and_assoc_item = self.create_substs_for_associated_item(
- tcx,
- path_span,
- assoc_item.def_id,
- &item_segment,
- trait_ref.substs,
- );
-
- debug!(
- "add_predicates_for_ast_type_binding: substs for trait-ref and assoc_item: {:?}",
- substs_trait_ref_and_assoc_item
- );
-
- ty::ProjectionTy {
- item_def_id: assoc_item.def_id,
- substs: substs_trait_ref_and_assoc_item,
- }
- });
-
- if !speculative {
- // Find any late-bound regions declared in `ty` that are not
- // declared in the trait-ref or assoc_item. These are not well-formed.
- //
- // Example:
- //
- // for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
- // for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
- if let ConvertedBindingKind::Equality(ty) = binding.kind {
- let late_bound_in_trait_ref =
- tcx.collect_constrained_late_bound_regions(&projection_ty);
- let late_bound_in_ty =
- tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty));
- debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref);
- debug!("late_bound_in_ty = {:?}", late_bound_in_ty);
-
- // FIXME: point at the type params that don't have appropriate lifetimes:
- // struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
- // ---- ---- ^^^^^^^
- self.validate_late_bound_regions(
- late_bound_in_trait_ref,
- late_bound_in_ty,
- |br_name| {
- struct_span_err!(
- tcx.sess,
- binding.span,
- E0582,
- "binding for associated type `{}` references {}, \
- which does not appear in the trait input types",
- binding.item_name,
- br_name
- )
- },
- );
- }
- }
-
- match binding.kind {
- ConvertedBindingKind::Equality(mut term) => {
- // "Desugar" a constraint like `T: Iterator<Item = u32>` this to
- // the "projection predicate" for:
- //
- // `<T as Iterator>::Item = u32`
- let assoc_item_def_id = projection_ty.skip_binder().item_def_id;
- let def_kind = tcx.def_kind(assoc_item_def_id);
- match (def_kind, term) {
- (hir::def::DefKind::AssocTy, ty::Term::Ty(_))
- | (hir::def::DefKind::AssocConst, ty::Term::Const(_)) => (),
- (_, _) => {
- let got = if let ty::Term::Ty(_) = term { "type" } else { "constant" };
- let expected = def_kind.descr(assoc_item_def_id);
- tcx.sess
- .struct_span_err(
- binding.span,
- &format!("expected {expected} bound, found {got}"),
- )
- .span_note(
- tcx.def_span(assoc_item_def_id),
- &format!("{expected} defined here"),
- )
- .emit();
- term = match def_kind {
- hir::def::DefKind::AssocTy => tcx.ty_error().into(),
- hir::def::DefKind::AssocConst => tcx
- .const_error(
- tcx.bound_type_of(assoc_item_def_id)
- .subst(tcx, projection_ty.skip_binder().substs),
- )
- .into(),
- _ => unreachable!(),
- };
- }
- }
- bounds.projection_bounds.push((
- projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate {
- projection_ty,
- term: term,
- }),
- binding.span,
- ));
- }
- ConvertedBindingKind::Constraint(ast_bounds) => {
- // "Desugar" a constraint like `T: Iterator<Item: Debug>` to
- //
- // `<T as Iterator>::Item: Debug`
- //
- // Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty`
- // parameter to have a skipped binder.
- let param_ty = tcx.mk_ty(ty::Projection(projection_ty.skip_binder()));
- self.add_bounds(param_ty, ast_bounds.iter(), bounds, candidate.bound_vars());
- }
- }
- Ok(())
- }
-
- fn ast_path_to_ty(
- &self,
- span: Span,
- did: DefId,
- item_segment: &hir::PathSegment<'_>,
- ) -> Ty<'tcx> {
- let substs = self.ast_path_substs_for_ty(span, did, item_segment);
- self.normalize_ty(
- span,
- EarlyBinder(self.tcx().at(span).type_of(did)).subst(self.tcx(), substs),
- )
- }
-
- fn conv_object_ty_poly_trait_ref(
- &self,
- span: Span,
- trait_bounds: &[hir::PolyTraitRef<'_>],
- lifetime: &hir::Lifetime,
- borrowed: bool,
- ) -> Ty<'tcx> {
- let tcx = self.tcx();
-
- let mut bounds = Bounds::default();
- let mut potential_assoc_types = Vec::new();
- let dummy_self = self.tcx().types.trait_object_dummy_self;
- for trait_bound in trait_bounds.iter().rev() {
- if let GenericArgCountResult {
- correct:
- Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }),
- ..
- } = self.instantiate_poly_trait_ref(
- &trait_bound.trait_ref,
- trait_bound.span,
- ty::BoundConstness::NotConst,
- dummy_self,
- &mut bounds,
- false,
- ) {
- potential_assoc_types.extend(cur_potential_assoc_types);
- }
- }
-
- // Expand trait aliases recursively and check that only one regular (non-auto) trait
- // is used and no 'maybe' bounds are used.
- let expanded_traits =
- traits::expand_trait_aliases(tcx, bounds.trait_bounds.iter().map(|&(a, b, _)| (a, b)));
- let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits
- .filter(|i| i.trait_ref().self_ty().skip_binder() == dummy_self)
- .partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
- if regular_traits.len() > 1 {
- let first_trait = &regular_traits[0];
- let additional_trait = &regular_traits[1];
- let mut err = struct_span_err!(
- tcx.sess,
- additional_trait.bottom().1,
- E0225,
- "only auto traits can be used as additional traits in a trait object"
- );
- additional_trait.label_with_exp_info(
- &mut err,
- "additional non-auto trait",
- "additional use",
- );
- first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use");
- err.help(&format!(
- "consider creating a new trait with all of these as supertraits and using that \
- trait here instead: `trait NewTrait: {} {{}}`",
- regular_traits
- .iter()
- .map(|t| t.trait_ref().print_only_trait_path().to_string())
- .collect::<Vec<_>>()
- .join(" + "),
- ));
- err.note(
- "auto-traits like `Send` and `Sync` are traits that have special properties; \
- for more information on them, visit \
- <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>",
- );
- err.emit();
- }
-
- if regular_traits.is_empty() && auto_traits.is_empty() {
- let trait_alias_span = bounds
- .trait_bounds
- .iter()
- .map(|&(trait_ref, _, _)| trait_ref.def_id())
- .find(|&trait_ref| tcx.is_trait_alias(trait_ref))
- .map(|trait_ref| tcx.def_span(trait_ref));
- tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
- return tcx.ty_error();
- }
-
- // Check that there are no gross object safety violations;
- // most importantly, that the supertraits don't contain `Self`,
- // to avoid ICEs.
- for item in &regular_traits {
- let object_safety_violations =
- astconv_object_safety_violations(tcx, item.trait_ref().def_id());
- if !object_safety_violations.is_empty() {
- report_object_safety_error(
- tcx,
- span,
- item.trait_ref().def_id(),
- &object_safety_violations,
- )
- .emit();
- return tcx.ty_error();
- }
- }
-
- // Use a `BTreeSet` to keep output in a more consistent order.
- let mut associated_types: FxHashMap<Span, BTreeSet<DefId>> = FxHashMap::default();
-
- let regular_traits_refs_spans = bounds
- .trait_bounds
- .into_iter()
- .filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id()));
-
- for (base_trait_ref, span, constness) in regular_traits_refs_spans {
- assert_eq!(constness, ty::BoundConstness::NotConst);
-
- for obligation in traits::elaborate_trait_ref(tcx, base_trait_ref) {
- debug!(
- "conv_object_ty_poly_trait_ref: observing object predicate `{:?}`",
- obligation.predicate
- );
-
- let bound_predicate = obligation.predicate.kind();
- match bound_predicate.skip_binder() {
- ty::PredicateKind::Trait(pred) => {
- let pred = bound_predicate.rebind(pred);
- associated_types.entry(span).or_default().extend(
- tcx.associated_items(pred.def_id())
- .in_definition_order()
- .filter(|item| item.kind == ty::AssocKind::Type)
- .map(|item| item.def_id),
- );
- }
- ty::PredicateKind::Projection(pred) => {
- let pred = bound_predicate.rebind(pred);
- // A `Self` within the original bound will be substituted with a
- // `trait_object_dummy_self`, so check for that.
- let references_self = match pred.skip_binder().term {
- ty::Term::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()),
- ty::Term::Const(c) => c.ty().walk().any(|arg| arg == dummy_self.into()),
- };
-
- // If the projection output contains `Self`, force the user to
- // elaborate it explicitly to avoid a lot of complexity.
- //
- // The "classically useful" case is the following:
- // ```
- // trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
- // type MyOutput;
- // }
- // ```
- //
- // Here, the user could theoretically write `dyn MyTrait<Output = X>`,
- // but actually supporting that would "expand" to an infinitely-long type
- // `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
- //
- // Instead, we force the user to write
- // `dyn MyTrait<MyOutput = X, Output = X>`, which is uglier but works. See
- // the discussion in #56288 for alternatives.
- if !references_self {
- // Include projections defined on supertraits.
- bounds.projection_bounds.push((pred, span));
- }
- }
- _ => (),
- }
- }
- }
-
- for (projection_bound, _) in &bounds.projection_bounds {
- for def_ids in associated_types.values_mut() {
- def_ids.remove(&projection_bound.projection_def_id());
- }
- }
-
- self.complain_about_missing_associated_types(
- associated_types,
- potential_assoc_types,
- trait_bounds,
- );
-
- // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as
- // `dyn Trait + Send`.
- // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering
- // the bounds
- let mut duplicates = FxHashSet::default();
- auto_traits.retain(|i| duplicates.insert(i.trait_ref().def_id()));
- debug!("regular_traits: {:?}", regular_traits);
- debug!("auto_traits: {:?}", auto_traits);
-
- // Erase the `dummy_self` (`trait_object_dummy_self`) used above.
- let existential_trait_refs = regular_traits.iter().map(|i| {
- i.trait_ref().map_bound(|trait_ref: ty::TraitRef<'tcx>| {
- if trait_ref.self_ty() != dummy_self {
- // FIXME: There appears to be a missing filter on top of `expand_trait_aliases`,
- // which picks up non-supertraits where clauses - but also, the object safety
- // completely ignores trait aliases, which could be object safety hazards. We
- // `delay_span_bug` here to avoid an ICE in stable even when the feature is
- // disabled. (#66420)
- tcx.sess.delay_span_bug(
- DUMMY_SP,
- &format!(
- "trait_ref_to_existential called on {:?} with non-dummy Self",
- trait_ref,
- ),
- );
- }
- ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
- })
- });
- let existential_projections = bounds.projection_bounds.iter().map(|(bound, _)| {
- bound.map_bound(|b| {
- if b.projection_ty.self_ty() != dummy_self {
- tcx.sess.delay_span_bug(
- DUMMY_SP,
- &format!("trait_ref_to_existential called on {:?} with non-dummy Self", b),
- );
- }
- ty::ExistentialProjection::erase_self_ty(tcx, b)
- })
- });
-
- let regular_trait_predicates = existential_trait_refs
- .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait));
- let auto_trait_predicates = auto_traits.into_iter().map(|trait_ref| {
- ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id()))
- });
- // N.b. principal, projections, auto traits
- // FIXME: This is actually wrong with multiple principals in regards to symbol mangling
- let mut v = regular_trait_predicates
- .chain(
- existential_projections.map(|x| x.map_bound(ty::ExistentialPredicate::Projection)),
- )
- .chain(auto_trait_predicates)
- .collect::<SmallVec<[_; 8]>>();
- v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
- v.dedup();
- let existential_predicates = tcx.mk_poly_existential_predicates(v.into_iter());
-
- // Use explicitly-specified region bound.
- let region_bound = if !lifetime.is_elided() {
- self.ast_region_to_region(lifetime, None)
- } else {
- self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| {
- if tcx.named_region(lifetime.hir_id).is_some() {
- self.ast_region_to_region(lifetime, None)
- } else {
- self.re_infer(None, span).unwrap_or_else(|| {
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0228,
- "the lifetime bound for this object type cannot be deduced \
- from context; please supply an explicit bound"
- );
- if borrowed {
- // We will have already emitted an error E0106 complaining about a
- // missing named lifetime in `&dyn Trait`, so we elide this one.
- err.delay_as_bug();
- } else {
- err.emit();
- }
- tcx.lifetimes.re_static
- })
- }
- })
- };
- debug!("region_bound: {:?}", region_bound);
-
- let ty = tcx.mk_dynamic(existential_predicates, region_bound);
- debug!("trait_object_type: {:?}", ty);
- ty
- }
-
- fn report_ambiguous_associated_type(
- &self,
- span: Span,
- type_str: &str,
- trait_str: &str,
- name: Symbol,
- ) -> ErrorGuaranteed {
- let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type");
- if self
- .tcx()
- .resolutions(())
- .confused_type_with_std_module
- .keys()
- .any(|full_span| full_span.contains(span))
- {
- err.span_suggestion(
- span.shrink_to_lo(),
- "you are looking for the module in `std`, not the primitive type",
- "std::",
- Applicability::MachineApplicable,
- );
- } else {
- err.span_suggestion(
- span,
- "use fully-qualified syntax",
- format!("<{} as {}>::{}", type_str, trait_str, name),
- Applicability::HasPlaceholders,
- );
- }
- err.emit()
- }
-
- // Search for a bound on a type parameter which includes the associated item
- // given by `assoc_name`. `ty_param_def_id` is the `DefId` of the type parameter
- // This function will fail if there are no suitable bounds or there is
- // any ambiguity.
- fn find_bound_for_assoc_item(
- &self,
- ty_param_def_id: LocalDefId,
- assoc_name: Ident,
- span: Span,
- ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> {
- let tcx = self.tcx();
-
- debug!(
- "find_bound_for_assoc_item(ty_param_def_id={:?}, assoc_name={:?}, span={:?})",
- ty_param_def_id, assoc_name, span,
- );
-
- let predicates = &self
- .get_type_parameter_bounds(span, ty_param_def_id.to_def_id(), assoc_name)
- .predicates;
-
- debug!("find_bound_for_assoc_item: predicates={:#?}", predicates);
-
- let param_name = tcx.hir().ty_param_name(ty_param_def_id);
- self.one_bound_for_assoc_type(
- || {
- traits::transitive_bounds_that_define_assoc_type(
- tcx,
- predicates.iter().filter_map(|(p, _)| {
- Some(p.to_opt_poly_trait_pred()?.map_bound(|t| t.trait_ref))
- }),
- assoc_name,
- )
- },
- || param_name.to_string(),
- assoc_name,
- span,
- || None,
- )
- }
-
- // Checks that `bounds` contains exactly one element and reports appropriate
- // errors otherwise.
- fn one_bound_for_assoc_type<I>(
- &self,
- all_candidates: impl Fn() -> I,
- ty_param_name: impl Fn() -> String,
- assoc_name: Ident,
- span: Span,
- is_equality: impl Fn() -> Option<String>,
- ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed>
- where
- I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
- {
- let mut matching_candidates = all_candidates()
- .filter(|r| self.trait_defines_associated_type_named(r.def_id(), assoc_name));
- let mut const_candidates = all_candidates()
- .filter(|r| self.trait_defines_associated_const_named(r.def_id(), assoc_name));
-
- let (bound, next_cand) = match (matching_candidates.next(), const_candidates.next()) {
- (Some(bound), _) => (bound, matching_candidates.next()),
- (None, Some(bound)) => (bound, const_candidates.next()),
- (None, None) => {
- let reported = self.complain_about_assoc_type_not_found(
- all_candidates,
- &ty_param_name(),
- assoc_name,
- span,
- );
- return Err(reported);
- }
- };
- debug!("one_bound_for_assoc_type: bound = {:?}", bound);
-
- if let Some(bound2) = next_cand {
- debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2);
-
- let is_equality = is_equality();
- let bounds = IntoIterator::into_iter([bound, bound2]).chain(matching_candidates);
- let mut err = if is_equality.is_some() {
- // More specific Error Index entry.
- struct_span_err!(
- self.tcx().sess,
- span,
- E0222,
- "ambiguous associated type `{}` in bounds of `{}`",
- assoc_name,
- ty_param_name()
- )
- } else {
- struct_span_err!(
- self.tcx().sess,
- span,
- E0221,
- "ambiguous associated type `{}` in bounds of `{}`",
- assoc_name,
- ty_param_name()
- )
- };
- err.span_label(span, format!("ambiguous associated type `{}`", assoc_name));
-
- let mut where_bounds = vec![];
- for bound in bounds {
- let bound_id = bound.def_id();
- let bound_span = self
- .tcx()
- .associated_items(bound_id)
- .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, bound_id)
- .and_then(|item| self.tcx().hir().span_if_local(item.def_id));
-
- if let Some(bound_span) = bound_span {
- err.span_label(
- bound_span,
- format!(
- "ambiguous `{}` from `{}`",
- assoc_name,
- bound.print_only_trait_path(),
- ),
- );
- if let Some(constraint) = &is_equality {
- where_bounds.push(format!(
- " T: {trait}::{assoc} = {constraint}",
- trait=bound.print_only_trait_path(),
- assoc=assoc_name,
- constraint=constraint,
- ));
- } else {
- err.span_suggestion_verbose(
- span.with_hi(assoc_name.span.lo()),
- "use fully qualified syntax to disambiguate",
- format!(
- "<{} as {}>::",
- ty_param_name(),
- bound.print_only_trait_path(),
- ),
- Applicability::MaybeIncorrect,
- );
- }
- } else {
- err.note(&format!(
- "associated type `{}` could derive from `{}`",
- ty_param_name(),
- bound.print_only_trait_path(),
- ));
- }
- }
- if !where_bounds.is_empty() {
- err.help(&format!(
- "consider introducing a new type parameter `T` and adding `where` constraints:\
- \n where\n T: {},\n{}",
- ty_param_name(),
- where_bounds.join(",\n"),
- ));
- }
- let reported = err.emit();
- if !where_bounds.is_empty() {
- return Err(reported);
- }
- }
-
- Ok(bound)
- }
-
- // Create a type from a path to an associated type.
- // For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C`
- // and item_segment is the path segment for `D`. We return a type and a def for
- // the whole path.
- // Will fail except for `T::A` and `Self::A`; i.e., if `qself_ty`/`qself_def` are not a type
- // parameter or `Self`.
- // NOTE: When this function starts resolving `Trait::AssocTy` successfully
- // it should also start reporting the `BARE_TRAIT_OBJECTS` lint.
- pub fn associated_path_to_ty(
- &self,
- hir_ref_id: hir::HirId,
- span: Span,
- qself_ty: Ty<'tcx>,
- qself: &hir::Ty<'_>,
- assoc_segment: &hir::PathSegment<'_>,
- permit_variants: bool,
- ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> {
- let tcx = self.tcx();
- let assoc_ident = assoc_segment.ident;
- let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, ref path)) = qself.kind {
- path.res
- } else {
- Res::Err
- };
-
- debug!("associated_path_to_ty: {:?}::{}", qself_ty, assoc_ident);
-
- // Check if we have an enum variant.
- let mut variant_resolution = None;
- if let ty::Adt(adt_def, _) = qself_ty.kind() {
- if adt_def.is_enum() {
- let variant_def = adt_def
- .variants()
- .iter()
- .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did()));
- if let Some(variant_def) = variant_def {
- if permit_variants {
- tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None);
- self.prohibit_generics(slice::from_ref(assoc_segment).iter(), |err| {
- err.note("enum variants can't have type parameters");
- let type_name = tcx.item_name(adt_def.did());
- let msg = format!(
- "you might have meant to specity type parameters on enum \
- `{type_name}`"
- );
- let Some(args) = assoc_segment.args else { return; };
- // Get the span of the generics args *including* the leading `::`.
- let args_span = assoc_segment.ident.span.shrink_to_hi().to(args.span_ext);
- if tcx.generics_of(adt_def.did()).count() == 0 {
- // FIXME(estebank): we could also verify that the arguments being
- // work for the `enum`, instead of just looking if it takes *any*.
- err.span_suggestion_verbose(
- args_span,
- &format!("{type_name} doesn't have generic parameters"),
- "",
- Applicability::MachineApplicable,
- );
- return;
- }
- let Ok(snippet) = tcx.sess.source_map().span_to_snippet(args_span) else {
- err.note(&msg);
- return;
- };
- let (qself_sugg_span, is_self) = if let hir::TyKind::Path(
- hir::QPath::Resolved(_, ref path)
- ) = qself.kind {
- // If the path segment already has type params, we want to overwrite
- // them.
- match &path.segments[..] {
- // `segment` is the previous to last element on the path,
- // which would normally be the `enum` itself, while the last
- // `_` `PathSegment` corresponds to the variant.
- [.., hir::PathSegment {
- ident,
- args,
- res: Some(Res::Def(DefKind::Enum, _)),
- ..
- }, _] => (
- // We need to include the `::` in `Type::Variant::<Args>`
- // to point the span to `::<Args>`, not just `<Args>`.
- ident.span.shrink_to_hi().to(args.map_or(
- ident.span.shrink_to_hi(),
- |a| a.span_ext)),
- false,
- ),
- [segment] => (
- // We need to include the `::` in `Type::Variant::<Args>`
- // to point the span to `::<Args>`, not just `<Args>`.
- segment.ident.span.shrink_to_hi().to(segment.args.map_or(
- segment.ident.span.shrink_to_hi(),
- |a| a.span_ext)),
- kw::SelfUpper == segment.ident.name,
- ),
- _ => {
- err.note(&msg);
- return;
- }
- }
- } else {
- err.note(&msg);
- return;
- };
- let suggestion = vec![
- if is_self {
- // Account for people writing `Self::Variant::<Args>`, where
- // `Self` is the enum, and suggest replacing `Self` with the
- // appropriate type: `Type::<Args>::Variant`.
- (qself.span, format!("{type_name}{snippet}"))
- } else {
- (qself_sugg_span, snippet)
- },
- (args_span, String::new()),
- ];
- err.multipart_suggestion_verbose(
- &msg,
- suggestion,
- Applicability::MaybeIncorrect,
- );
- });
- return Ok((qself_ty, DefKind::Variant, variant_def.def_id));
- } else {
- variant_resolution = Some(variant_def.def_id);
- }
- }
- }
- }
-
- // Find the type of the associated item, and the trait where the associated
- // item is declared.
- let bound = match (&qself_ty.kind(), qself_res) {
- (_, Res::SelfTy { trait_: Some(_), alias_to: Some((impl_def_id, _)) }) => {
- // `Self` in an impl of a trait -- we have a concrete self type and a
- // trait reference.
- let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else {
- // A cycle error occurred, most likely.
- let guar = tcx.sess.delay_span_bug(span, "expected cycle error");
- return Err(guar);
- };
-
- self.one_bound_for_assoc_type(
- || traits::supertraits(tcx, ty::Binder::dummy(trait_ref)),
- || "Self".to_string(),
- assoc_ident,
- span,
- || None,
- )?
- }
- (
- &ty::Param(_),
- Res::SelfTy { trait_: Some(param_did), alias_to: None }
- | Res::Def(DefKind::TyParam, param_did),
- ) => self.find_bound_for_assoc_item(param_did.expect_local(), assoc_ident, span)?,
- _ => {
- let reported = if variant_resolution.is_some() {
- // Variant in type position
- let msg = format!("expected type, found variant `{}`", assoc_ident);
- tcx.sess.span_err(span, &msg)
- } else if qself_ty.is_enum() {
- let mut err = struct_span_err!(
- tcx.sess,
- assoc_ident.span,
- E0599,
- "no variant named `{}` found for enum `{}`",
- assoc_ident,
- qself_ty,
- );
-
- let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT");
- if let Some(suggested_name) = find_best_match_for_name(
- &adt_def
- .variants()
- .iter()
- .map(|variant| variant.name)
- .collect::<Vec<Symbol>>(),
- assoc_ident.name,
- None,
- ) {
- err.span_suggestion(
- assoc_ident.span,
- "there is a variant with a similar name",
- suggested_name,
- Applicability::MaybeIncorrect,
- );
- } else {
- err.span_label(
- assoc_ident.span,
- format!("variant not found in `{}`", qself_ty),
- );
- }
-
- if let Some(sp) = tcx.hir().span_if_local(adt_def.did()) {
- err.span_label(sp, format!("variant `{}` not found here", assoc_ident));
- }
-
- err.emit()
- } else if let Some(reported) = qself_ty.error_reported() {
- reported
- } else {
- // Don't print `TyErr` to the user.
- self.report_ambiguous_associated_type(
- span,
- &qself_ty.to_string(),
- "Trait",
- assoc_ident.name,
- )
- };
- return Err(reported);
- }
- };
-
- let trait_did = bound.def_id();
- let (assoc_ident, def_scope) =
- tcx.adjust_ident_and_get_scope(assoc_ident, trait_did, hir_ref_id);
-
- // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
- // of calling `filter_by_name_and_kind`.
- let item = tcx.associated_items(trait_did).in_definition_order().find(|i| {
- i.kind.namespace() == Namespace::TypeNS
- && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
- });
- // Assume that if it's not matched, there must be a const defined with the same name
- // but it was used in a type position.
- let Some(item) = item else {
- let msg = format!("found associated const `{assoc_ident}` when type was expected");
- let guar = tcx.sess.struct_span_err(span, &msg).emit();
- return Err(guar);
- };
-
- let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, assoc_segment, bound);
- let ty = self.normalize_ty(span, ty);
-
- let kind = DefKind::AssocTy;
- if !item.visibility(tcx).is_accessible_from(def_scope, tcx) {
- let kind = kind.descr(item.def_id);
- let msg = format!("{} `{}` is private", kind, assoc_ident);
- tcx.sess
- .struct_span_err(span, &msg)
- .span_label(span, &format!("private {}", kind))
- .emit();
- }
- tcx.check_stability(item.def_id, Some(hir_ref_id), span, None);
-
- if let Some(variant_def_id) = variant_resolution {
- tcx.struct_span_lint_hir(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| {
- let mut err = lint.build("ambiguous associated item");
- let mut could_refer_to = |kind: DefKind, def_id, also| {
- let note_msg = format!(
- "`{}` could{} refer to the {} defined here",
- assoc_ident,
- also,
- kind.descr(def_id)
- );
- err.span_note(tcx.def_span(def_id), &note_msg);
- };
-
- could_refer_to(DefKind::Variant, variant_def_id, "");
- could_refer_to(kind, item.def_id, " also");
-
- err.span_suggestion(
- span,
- "use fully-qualified syntax",
- format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident),
- Applicability::MachineApplicable,
- );
-
- err.emit();
- });
- }
- Ok((ty, kind, item.def_id))
- }
-
- fn qpath_to_ty(
- &self,
- span: Span,
- opt_self_ty: Option<Ty<'tcx>>,
- item_def_id: DefId,
- trait_segment: &hir::PathSegment<'_>,
- item_segment: &hir::PathSegment<'_>,
- ) -> Ty<'tcx> {
- let tcx = self.tcx();
-
- let trait_def_id = tcx.parent(item_def_id);
-
- debug!("qpath_to_ty: trait_def_id={:?}", trait_def_id);
-
- let Some(self_ty) = opt_self_ty else {
- let path_str = tcx.def_path_str(trait_def_id);
-
- let def_id = self.item_def_id();
-
- debug!("qpath_to_ty: self.item_def_id()={:?}", def_id);
-
- let parent_def_id = def_id
- .and_then(|def_id| {
- def_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id))
- })
- .map(|hir_id| tcx.hir().get_parent_item(hir_id).to_def_id());
-
- debug!("qpath_to_ty: parent_def_id={:?}", parent_def_id);
-
- // If the trait in segment is the same as the trait defining the item,
- // use the `<Self as ..>` syntax in the error.
- let is_part_of_self_trait_constraints = def_id == Some(trait_def_id);
- let is_part_of_fn_in_self_trait = parent_def_id == Some(trait_def_id);
-
- let type_name = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
- "Self"
- } else {
- "Type"
- };
-
- self.report_ambiguous_associated_type(
- span,
- type_name,
- &path_str,
- item_segment.ident.name,
- );
- return tcx.ty_error();
- };
-
- debug!("qpath_to_ty: self_type={:?}", self_ty);
-
- let trait_ref =
- self.ast_path_to_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false);
-
- let item_substs = self.create_substs_for_associated_item(
- tcx,
- span,
- item_def_id,
- item_segment,
- trait_ref.substs,
- );
-
- debug!("qpath_to_ty: trait_ref={:?}", trait_ref);
-
- self.normalize_ty(span, tcx.mk_projection(item_def_id, item_substs))
- }
-
- pub fn prohibit_generics<'a>(
- &self,
- segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone,
- extend: impl Fn(&mut DiagnosticBuilder<'tcx, ErrorGuaranteed>),
- ) -> bool {
- let args = segments.clone().flat_map(|segment| segment.args().args);
-
- let (lt, ty, ct, inf) =
- args.clone().fold((false, false, false, false), |(lt, ty, ct, inf), arg| match arg {
- hir::GenericArg::Lifetime(_) => (true, ty, ct, inf),
- hir::GenericArg::Type(_) => (lt, true, ct, inf),
- hir::GenericArg::Const(_) => (lt, ty, true, inf),
- hir::GenericArg::Infer(_) => (lt, ty, ct, true),
- });
- let mut emitted = false;
- if lt || ty || ct || inf {
- let types_and_spans: Vec<_> = segments
- .clone()
- .flat_map(|segment| {
- segment.res.and_then(|res| {
- if segment.args().args.is_empty() {
- None
- } else {
- Some((
- match res {
- Res::PrimTy(ty) => format!("{} `{}`", res.descr(), ty.name()),
- Res::Def(_, def_id)
- if let Some(name) = self.tcx().opt_item_name(def_id) => {
- format!("{} `{name}`", res.descr())
- }
- Res::Err => "this type".to_string(),
- _ => res.descr().to_string(),
- },
- segment.ident.span,
- ))
- }
- })
- })
- .collect();
- let this_type = match &types_and_spans[..] {
- [.., _, (last, _)] => format!(
- "{} and {last}",
- types_and_spans[..types_and_spans.len() - 1]
- .iter()
- .map(|(x, _)| x.as_str())
- .intersperse(&", ")
- .collect::<String>()
- ),
- [(only, _)] => only.to_string(),
- [] => "this type".to_string(),
- };
-
- let arg_spans: Vec<Span> = args.map(|arg| arg.span()).collect();
-
- let mut kinds = Vec::with_capacity(4);
- if lt {
- kinds.push("lifetime");
- }
- if ty {
- kinds.push("type");
- }
- if ct {
- kinds.push("const");
- }
- if inf {
- kinds.push("generic");
- }
- let (kind, s) = match kinds[..] {
- [.., _, last] => (
- format!(
- "{} and {last}",
- kinds[..kinds.len() - 1]
- .iter()
- .map(|&x| x)
- .intersperse(", ")
- .collect::<String>()
- ),
- "s",
- ),
- [only] => (format!("{only}"), ""),
- [] => unreachable!(),
- };
- let last_span = *arg_spans.last().unwrap();
- let span: MultiSpan = arg_spans.into();
- let mut err = struct_span_err!(
- self.tcx().sess,
- span,
- E0109,
- "{kind} arguments are not allowed on {this_type}",
- );
- err.span_label(last_span, format!("{kind} argument{s} not allowed"));
- for (what, span) in types_and_spans {
- err.span_label(span, format!("not allowed on {what}"));
- }
- extend(&mut err);
- err.emit();
- emitted = true;
- }
-
- for segment in segments {
- // Only emit the first error to avoid overloading the user with error messages.
- if let [binding, ..] = segment.args().bindings {
- Self::prohibit_assoc_ty_binding(self.tcx(), binding.span);
- return true;
- }
- }
- emitted
- }
-
- // FIXME(eddyb, varkor) handle type paths here too, not just value ones.
- pub fn def_ids_for_value_path_segments(
- &self,
- segments: &[hir::PathSegment<'_>],
- self_ty: Option<Ty<'tcx>>,
- kind: DefKind,
- def_id: DefId,
- ) -> Vec<PathSeg> {
- // We need to extract the type parameters supplied by the user in
- // the path `path`. Due to the current setup, this is a bit of a
- // tricky-process; the problem is that resolve only tells us the
- // end-point of the path resolution, and not the intermediate steps.
- // Luckily, we can (at least for now) deduce the intermediate steps
- // just from the end-point.
- //
- // There are basically five cases to consider:
- //
- // 1. Reference to a constructor of a struct:
- //
- // struct Foo<T>(...)
- //
- // In this case, the parameters are declared in the type space.
- //
- // 2. Reference to a constructor of an enum variant:
- //
- // enum E<T> { Foo(...) }
- //
- // In this case, the parameters are defined in the type space,
- // but may be specified either on the type or the variant.
- //
- // 3. Reference to a fn item or a free constant:
- //
- // fn foo<T>() { }
- //
- // In this case, the path will again always have the form
- // `a::b::foo::<T>` where only the final segment should have
- // type parameters. However, in this case, those parameters are
- // declared on a value, and hence are in the `FnSpace`.
- //
- // 4. Reference to a method or an associated constant:
- //
- // impl<A> SomeStruct<A> {
- // fn foo<B>(...)
- // }
- //
- // Here we can have a path like
- // `a::b::SomeStruct::<A>::foo::<B>`, in which case parameters
- // may appear in two places. The penultimate segment,
- // `SomeStruct::<A>`, contains parameters in TypeSpace, and the
- // final segment, `foo::<B>` contains parameters in fn space.
- //
- // The first step then is to categorize the segments appropriately.
-
- let tcx = self.tcx();
-
- assert!(!segments.is_empty());
- let last = segments.len() - 1;
-
- let mut path_segs = vec![];
-
- match kind {
- // Case 1. Reference to a struct constructor.
- DefKind::Ctor(CtorOf::Struct, ..) => {
- // Everything but the final segment should have no
- // parameters at all.
- let generics = tcx.generics_of(def_id);
- // Variant and struct constructors use the
- // generics of their parent type definition.
- let generics_def_id = generics.parent.unwrap_or(def_id);
- path_segs.push(PathSeg(generics_def_id, last));
- }
-
- // Case 2. Reference to a variant constructor.
- DefKind::Ctor(CtorOf::Variant, ..) | DefKind::Variant => {
- let adt_def = self_ty.map(|t| t.ty_adt_def().unwrap());
- let (generics_def_id, index) = if let Some(adt_def) = adt_def {
- debug_assert!(adt_def.is_enum());
- (adt_def.did(), last)
- } else if last >= 1 && segments[last - 1].args.is_some() {
- // Everything but the penultimate segment should have no
- // parameters at all.
- let mut def_id = def_id;
-
- // `DefKind::Ctor` -> `DefKind::Variant`
- if let DefKind::Ctor(..) = kind {
- def_id = tcx.parent(def_id);
- }
-
- // `DefKind::Variant` -> `DefKind::Enum`
- let enum_def_id = tcx.parent(def_id);
- (enum_def_id, last - 1)
- } else {
- // FIXME: lint here recommending `Enum::<...>::Variant` form
- // instead of `Enum::Variant::<...>` form.
-
- // Everything but the final segment should have no
- // parameters at all.
- let generics = tcx.generics_of(def_id);
- // Variant and struct constructors use the
- // generics of their parent type definition.
- (generics.parent.unwrap_or(def_id), last)
- };
- path_segs.push(PathSeg(generics_def_id, index));
- }
-
- // Case 3. Reference to a top-level value.
- DefKind::Fn | DefKind::Const | DefKind::ConstParam | DefKind::Static(_) => {
- path_segs.push(PathSeg(def_id, last));
- }
-
- // Case 4. Reference to a method or associated const.
- DefKind::AssocFn | DefKind::AssocConst => {
- if segments.len() >= 2 {
- let generics = tcx.generics_of(def_id);
- path_segs.push(PathSeg(generics.parent.unwrap(), last - 1));
- }
- path_segs.push(PathSeg(def_id, last));
- }
-
- kind => bug!("unexpected definition kind {:?} for {:?}", kind, def_id),
- }
-
- debug!("path_segs = {:?}", path_segs);
-
- path_segs
- }
-
- // Check a type `Path` and convert it to a `Ty`.
- pub fn res_to_ty(
- &self,
- opt_self_ty: Option<Ty<'tcx>>,
- path: &hir::Path<'_>,
- permit_variants: bool,
- ) -> Ty<'tcx> {
- let tcx = self.tcx();
-
- debug!(
- "res_to_ty(res={:?}, opt_self_ty={:?}, path_segments={:?})",
- path.res, opt_self_ty, path.segments
- );
-
- let span = path.span;
- match path.res {
- Res::Def(DefKind::OpaqueTy, did) => {
- // Check for desugared `impl Trait`.
- assert!(ty::is_impl_trait_defn(tcx, did).is_none());
- let item_segment = path.segments.split_last().unwrap();
- self.prohibit_generics(item_segment.1.iter(), |err| {
- err.note("`impl Trait` types can't have type parameters");
- });
- let substs = self.ast_path_substs_for_ty(span, did, item_segment.0);
- self.normalize_ty(span, tcx.mk_opaque(did, substs))
- }
- Res::Def(
- DefKind::Enum
- | DefKind::TyAlias
- | DefKind::Struct
- | DefKind::Union
- | DefKind::ForeignTy,
- did,
- ) => {
- assert_eq!(opt_self_ty, None);
- self.prohibit_generics(path.segments.split_last().unwrap().1.iter(), |_| {});
- self.ast_path_to_ty(span, did, path.segments.last().unwrap())
- }
- Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => {
- // Convert "variant type" as if it were a real type.
- // The resulting `Ty` is type of the variant's enum for now.
- assert_eq!(opt_self_ty, None);
-
- let path_segs =
- self.def_ids_for_value_path_segments(path.segments, None, kind, def_id);
- let generic_segs: FxHashSet<_> =
- path_segs.iter().map(|PathSeg(_, index)| index).collect();
- self.prohibit_generics(
- path.segments.iter().enumerate().filter_map(|(index, seg)| {
- if !generic_segs.contains(&index) { Some(seg) } else { None }
- }),
- |err| {
- err.note("enum variants can't have type parameters");
- },
- );
-
- let PathSeg(def_id, index) = path_segs.last().unwrap();
- self.ast_path_to_ty(span, *def_id, &path.segments[*index])
- }
- Res::Def(DefKind::TyParam, def_id) => {
- assert_eq!(opt_self_ty, None);
- self.prohibit_generics(path.segments.iter(), |err| {
- if let Some(span) = tcx.def_ident_span(def_id) {
- let name = tcx.item_name(def_id);
- err.span_note(span, &format!("type parameter `{name}` defined here"));
- }
- });
-
- let def_id = def_id.expect_local();
- let item_def_id = tcx.hir().ty_param_owner(def_id);
- let generics = tcx.generics_of(item_def_id);
- let index = generics.param_def_id_to_index[&def_id.to_def_id()];
- tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id))
- }
- Res::SelfTy { trait_: Some(_), alias_to: None } => {
- // `Self` in trait or type alias.
- assert_eq!(opt_self_ty, None);
- self.prohibit_generics(path.segments.iter(), |err| {
- if let [hir::PathSegment { args: Some(args), ident, .. }] = &path.segments[..] {
- err.span_suggestion_verbose(
- ident.span.shrink_to_hi().to(args.span_ext),
- "the `Self` type doesn't accept type parameters",
- "",
- Applicability::MaybeIncorrect,
- );
- }
- });
- tcx.types.self_param
- }
- Res::SelfTy { trait_: _, alias_to: Some((def_id, forbid_generic)) } => {
- // `Self` in impl (we know the concrete type).
- assert_eq!(opt_self_ty, None);
- // Try to evaluate any array length constants.
- let ty = tcx.at(span).type_of(def_id);
- let span_of_impl = tcx.span_of_impl(def_id);
- self.prohibit_generics(path.segments.iter(), |err| {
- let def_id = match *ty.kind() {
- ty::Adt(self_def, _) => self_def.did(),
- _ => return,
- };
-
- let type_name = tcx.item_name(def_id);
- let span_of_ty = tcx.def_ident_span(def_id);
- let generics = tcx.generics_of(def_id).count();
-
- let msg = format!("`Self` is of type `{ty}`");
- if let (Ok(i_sp), Some(t_sp)) = (span_of_impl, span_of_ty) {
- let mut span: MultiSpan = vec![t_sp].into();
- span.push_span_label(
- i_sp,
- &format!("`Self` is on type `{type_name}` in this `impl`"),
- );
- let mut postfix = "";
- if generics == 0 {
- postfix = ", which doesn't have generic parameters";
- }
- span.push_span_label(
- t_sp,
- &format!("`Self` corresponds to this type{postfix}"),
- );
- err.span_note(span, &msg);
- } else {
- err.note(&msg);
- }
- for segment in path.segments {
- if let Some(args) = segment.args && segment.ident.name == kw::SelfUpper {
- if generics == 0 {
- // FIXME(estebank): we could also verify that the arguments being
- // work for the `enum`, instead of just looking if it takes *any*.
- err.span_suggestion_verbose(
- segment.ident.span.shrink_to_hi().to(args.span_ext),
- "the `Self` type doesn't accept type parameters",
- "",
- Applicability::MachineApplicable,
- );
- return;
- } else {
- err.span_suggestion_verbose(
- segment.ident.span,
- format!(
- "the `Self` type doesn't accept type parameters, use the \
- concrete type's name `{type_name}` instead if you want to \
- specify its type parameters"
- ),
- type_name,
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
- });
- // HACK(min_const_generics): Forbid generic `Self` types
- // here as we can't easily do that during nameres.
- //
- // We do this before normalization as we otherwise allow
- // ```rust
- // trait AlwaysApplicable { type Assoc; }
- // impl<T: ?Sized> AlwaysApplicable for T { type Assoc = usize; }
- //
- // trait BindsParam<T> {
- // type ArrayTy;
- // }
- // impl<T> BindsParam<T> for <T as AlwaysApplicable>::Assoc {
- // type ArrayTy = [u8; Self::MAX];
- // }
- // ```
- // Note that the normalization happens in the param env of
- // the anon const, which is empty. This is why the
- // `AlwaysApplicable` impl needs a `T: ?Sized` bound for
- // this to compile if we were to normalize here.
- if forbid_generic && ty.needs_subst() {
- let mut err = tcx.sess.struct_span_err(
- path.span,
- "generic `Self` types are currently not permitted in anonymous constants",
- );
- if let Some(hir::Node::Item(&hir::Item {
- kind: hir::ItemKind::Impl(ref impl_),
- ..
- })) = tcx.hir().get_if_local(def_id)
- {
- err.span_note(impl_.self_ty.span, "not a concrete type");
- }
- err.emit();
- tcx.ty_error()
- } else {
- self.normalize_ty(span, ty)
- }
- }
- Res::Def(DefKind::AssocTy, def_id) => {
- debug_assert!(path.segments.len() >= 2);
- self.prohibit_generics(path.segments[..path.segments.len() - 2].iter(), |_| {});
- self.qpath_to_ty(
- span,
- opt_self_ty,
- def_id,
- &path.segments[path.segments.len() - 2],
- path.segments.last().unwrap(),
- )
- }
- Res::PrimTy(prim_ty) => {
- assert_eq!(opt_self_ty, None);
- self.prohibit_generics(path.segments.iter(), |err| {
- let name = prim_ty.name_str();
- for segment in path.segments {
- if let Some(args) = segment.args {
- err.span_suggestion_verbose(
- segment.ident.span.shrink_to_hi().to(args.span_ext),
- &format!("primitive type `{name}` doesn't have generic parameters"),
- "",
- Applicability::MaybeIncorrect,
- );
- }
- }
- });
- match prim_ty {
- hir::PrimTy::Bool => tcx.types.bool,
- hir::PrimTy::Char => tcx.types.char,
- hir::PrimTy::Int(it) => tcx.mk_mach_int(ty::int_ty(it)),
- hir::PrimTy::Uint(uit) => tcx.mk_mach_uint(ty::uint_ty(uit)),
- hir::PrimTy::Float(ft) => tcx.mk_mach_float(ty::float_ty(ft)),
- hir::PrimTy::Str => tcx.types.str_,
- }
- }
- Res::Err => {
- self.set_tainted_by_errors();
- self.tcx().ty_error()
- }
- _ => span_bug!(span, "unexpected resolution: {:?}", path.res),
- }
- }
-
- /// Parses the programmer's textual representation of a type into our
- /// internal notion of a type.
- pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
- self.ast_ty_to_ty_inner(ast_ty, false, false)
- }
-
- /// Parses the programmer's textual representation of a type into our
- /// internal notion of a type. This is meant to be used within a path.
- pub fn ast_ty_to_ty_in_path(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
- self.ast_ty_to_ty_inner(ast_ty, false, true)
- }
-
- /// Turns a `hir::Ty` into a `Ty`. For diagnostics' purposes we keep track of whether trait
- /// objects are borrowed like `&dyn Trait` to avoid emitting redundant errors.
- #[tracing::instrument(level = "debug", skip(self))]
- fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool, in_path: bool) -> Ty<'tcx> {
- let tcx = self.tcx();
-
- let result_ty = match ast_ty.kind {
- hir::TyKind::Slice(ref ty) => tcx.mk_slice(self.ast_ty_to_ty(ty)),
- hir::TyKind::Ptr(ref mt) => {
- tcx.mk_ptr(ty::TypeAndMut { ty: self.ast_ty_to_ty(mt.ty), mutbl: mt.mutbl })
- }
- hir::TyKind::Rptr(ref region, ref mt) => {
- let r = self.ast_region_to_region(region, None);
- debug!(?r);
- let t = self.ast_ty_to_ty_inner(mt.ty, true, false);
- tcx.mk_ref(r, ty::TypeAndMut { ty: t, mutbl: mt.mutbl })
- }
- hir::TyKind::Never => tcx.types.never,
- hir::TyKind::Tup(fields) => tcx.mk_tup(fields.iter().map(|t| self.ast_ty_to_ty(t))),
- hir::TyKind::BareFn(bf) => {
- require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, ast_ty.span);
-
- tcx.mk_fn_ptr(self.ty_of_fn(
- ast_ty.hir_id,
- bf.unsafety,
- bf.abi,
- bf.decl,
- None,
- Some(ast_ty),
- ))
- }
- hir::TyKind::TraitObject(bounds, ref lifetime, _) => {
- self.maybe_lint_bare_trait(ast_ty, in_path);
- self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed)
- }
- hir::TyKind::Path(hir::QPath::Resolved(ref maybe_qself, ref path)) => {
- debug!(?maybe_qself, ?path);
- let opt_self_ty = maybe_qself.as_ref().map(|qself| self.ast_ty_to_ty(qself));
- self.res_to_ty(opt_self_ty, path, false)
- }
- hir::TyKind::OpaqueDef(item_id, lifetimes) => {
- let opaque_ty = tcx.hir().item(item_id);
- let def_id = item_id.def_id.to_def_id();
-
- match opaque_ty.kind {
- hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
- self.impl_trait_ty_to_ty(def_id, lifetimes, origin)
- }
- ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
- }
- }
- hir::TyKind::Path(hir::QPath::TypeRelative(ref qself, ref segment)) => {
- debug!(?qself, ?segment);
- let ty = self.ast_ty_to_ty_inner(qself, false, true);
- self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, qself, segment, false)
- .map(|(ty, _, _)| ty)
- .unwrap_or_else(|_| tcx.ty_error())
- }
- hir::TyKind::Path(hir::QPath::LangItem(lang_item, span, _)) => {
- let def_id = tcx.require_lang_item(lang_item, Some(span));
- let (substs, _) = self.create_substs_for_ast_path(
- span,
- def_id,
- &[],
- &hir::PathSegment::invalid(),
- &GenericArgs::none(),
- true,
- None,
- );
- EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id)))
- .subst(tcx, substs)
- }
- hir::TyKind::Array(ref ty, ref length) => {
- let length = match length {
- &hir::ArrayLen::Infer(_, span) => self.ct_infer(tcx.types.usize, None, span),
- hir::ArrayLen::Body(constant) => {
- let length_def_id = tcx.hir().local_def_id(constant.hir_id);
- ty::Const::from_anon_const(tcx, length_def_id)
- }
- };
-
- let array_ty = tcx.mk_ty(ty::Array(self.ast_ty_to_ty(ty), length));
- self.normalize_ty(ast_ty.span, array_ty)
- }
- hir::TyKind::Typeof(ref e) => {
- let ty = tcx.type_of(tcx.hir().local_def_id(e.hir_id));
- let span = ast_ty.span;
- tcx.sess.emit_err(TypeofReservedKeywordUsed {
- span,
- ty,
- opt_sugg: Some((span, Applicability::MachineApplicable))
- .filter(|_| ty.is_suggestable(tcx, false)),
- });
-
- ty
- }
- hir::TyKind::Infer => {
- // Infer also appears as the type of arguments or return
- // values in an ExprKind::Closure, or as
- // the type of local variables. Both of these cases are
- // handled specially and will not descend into this routine.
- self.ty_infer(None, ast_ty.span)
- }
- hir::TyKind::Err => tcx.ty_error(),
- };
-
- debug!(?result_ty);
-
- self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span);
- result_ty
- }
-
- fn impl_trait_ty_to_ty(
- &self,
- def_id: DefId,
- lifetimes: &[hir::GenericArg<'_>],
- origin: OpaqueTyOrigin,
- ) -> Ty<'tcx> {
- debug!("impl_trait_ty_to_ty(def_id={:?}, lifetimes={:?})", def_id, lifetimes);
- let tcx = self.tcx();
-
- let generics = tcx.generics_of(def_id);
-
- debug!("impl_trait_ty_to_ty: generics={:?}", generics);
- let substs = InternalSubsts::for_item(tcx, def_id, |param, _| {
- if let Some(i) = (param.index as usize).checked_sub(generics.parent_count) {
- // Our own parameters are the resolved lifetimes.
- if let GenericParamDefKind::Lifetime = param.kind {
- if let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] {
- self.ast_region_to_region(lifetime, None).into()
- } else {
- bug!()
- }
- } else {
- bug!()
- }
- } else {
- match param.kind {
- // For RPIT (return position impl trait), only lifetimes
- // mentioned in the impl Trait predicate are captured by
- // the opaque type, so the lifetime parameters from the
- // parent item need to be replaced with `'static`.
- //
- // For `impl Trait` in the types of statics, constants,
- // locals and type aliases. These capture all parent
- // lifetimes, so they can use their identity subst.
- GenericParamDefKind::Lifetime
- if matches!(
- origin,
- hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..)
- ) =>
- {
- tcx.lifetimes.re_static.into()
- }
- _ => tcx.mk_param_from_def(param),
- }
- }
- });
- debug!("impl_trait_ty_to_ty: substs={:?}", substs);
-
- let ty = tcx.mk_opaque(def_id, substs);
- debug!("impl_trait_ty_to_ty: {}", ty);
- ty
- }
-
- pub fn ty_of_arg(&self, ty: &hir::Ty<'_>, expected_ty: Option<Ty<'tcx>>) -> Ty<'tcx> {
- match ty.kind {
- hir::TyKind::Infer if expected_ty.is_some() => {
- self.record_ty(ty.hir_id, expected_ty.unwrap(), ty.span);
- expected_ty.unwrap()
- }
- _ => self.ast_ty_to_ty(ty),
- }
- }
-
- pub fn ty_of_fn(
- &self,
- hir_id: hir::HirId,
- unsafety: hir::Unsafety,
- abi: abi::Abi,
- decl: &hir::FnDecl<'_>,
- generics: Option<&hir::Generics<'_>>,
- hir_ty: Option<&hir::Ty<'_>>,
- ) -> ty::PolyFnSig<'tcx> {
- debug!("ty_of_fn");
-
- let tcx = self.tcx();
- let bound_vars = tcx.late_bound_vars(hir_id);
- debug!(?bound_vars);
-
- // We proactively collect all the inferred type params to emit a single error per fn def.
- let mut visitor = HirPlaceholderCollector::default();
- let mut infer_replacements = vec![];
-
- if let Some(generics) = generics {
- walk_generics(&mut visitor, generics);
- }
-
- let input_tys: Vec<_> = decl
- .inputs
- .iter()
- .enumerate()
- .map(|(i, a)| {
- if let hir::TyKind::Infer = a.kind && !self.allow_ty_infer() {
- if let Some(suggested_ty) =
- self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i))
- {
- infer_replacements.push((a.span, suggested_ty.to_string()));
- return suggested_ty;
- }
- }
-
- // Only visit the type looking for `_` if we didn't fix the type above
- visitor.visit_ty(a);
- self.ty_of_arg(a, None)
- })
- .collect();
-
- let output_ty = match decl.output {
- hir::FnRetTy::Return(output) => {
- if let hir::TyKind::Infer = output.kind
- && !self.allow_ty_infer()
- && let Some(suggested_ty) =
- self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None)
- {
- infer_replacements.push((output.span, suggested_ty.to_string()));
- suggested_ty
- } else {
- visitor.visit_ty(output);
- self.ast_ty_to_ty(output)
- }
- }
- hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(),
- };
-
- debug!("ty_of_fn: output_ty={:?}", output_ty);
-
- let fn_ty = tcx.mk_fn_sig(input_tys.into_iter(), output_ty, decl.c_variadic, unsafety, abi);
- let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
-
- if !self.allow_ty_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) {
- // We always collect the spans for placeholder types when evaluating `fn`s, but we
- // only want to emit an error complaining about them if infer types (`_`) are not
- // allowed. `allow_ty_infer` gates this behavior. We check for the presence of
- // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.
-
- let mut diag = crate::collect::placeholder_type_error_diag(
- tcx,
- generics,
- visitor.0,
- infer_replacements.iter().map(|(s, _)| *s).collect(),
- true,
- hir_ty,
- "function",
- );
-
- if !infer_replacements.is_empty() {
- diag.multipart_suggestion(&format!(
- "try replacing `_` with the type{} in the corresponding trait method signature",
- rustc_errors::pluralize!(infer_replacements.len()),
- ), infer_replacements, Applicability::MachineApplicable);
- }
-
- diag.emit();
- }
-
- // Find any late-bound regions declared in return type that do
- // not appear in the arguments. These are not well-formed.
- //
- // Example:
- // for<'a> fn() -> &'a str <-- 'a is bad
- // for<'a> fn(&'a String) -> &'a str <-- 'a is ok
- let inputs = bare_fn_ty.inputs();
- let late_bound_in_args =
- tcx.collect_constrained_late_bound_regions(&inputs.map_bound(|i| i.to_owned()));
- let output = bare_fn_ty.output();
- let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output);
-
- self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| {
- struct_span_err!(
- tcx.sess,
- decl.output.span(),
- E0581,
- "return type references {}, which is not constrained by the fn input types",
- br_name
- )
- });
-
- bare_fn_ty
- }
-
- /// Given a fn_hir_id for a impl function, suggest the type that is found on the
- /// corresponding function in the trait that the impl implements, if it exists.
- /// If arg_idx is Some, then it corresponds to an input type index, otherwise it
- /// corresponds to the return type.
- fn suggest_trait_fn_ty_for_impl_fn_infer(
- &self,
- fn_hir_id: hir::HirId,
- arg_idx: Option<usize>,
- ) -> Option<Ty<'tcx>> {
- let tcx = self.tcx();
- let hir = tcx.hir();
-
- let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), ident, .. }) =
- hir.get(fn_hir_id) else { return None };
- let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) =
- hir.get(hir.get_parent_node(fn_hir_id)) else { bug!("ImplItem should have Impl parent") };
-
- let trait_ref =
- self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty));
-
- let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind(
- tcx,
- *ident,
- ty::AssocKind::Fn,
- trait_ref.def_id,
- )?;
-
- let fn_sig = tcx.bound_fn_sig(assoc.def_id).subst(
- tcx,
- trait_ref.substs.extend_to(tcx, assoc.def_id, |param, _| tcx.mk_param_from_def(param)),
- );
-
- let ty = if let Some(arg_idx) = arg_idx { fn_sig.input(arg_idx) } else { fn_sig.output() };
-
- Some(tcx.liberate_late_bound_regions(fn_hir_id.expect_owner().to_def_id(), ty))
- }
-
- fn validate_late_bound_regions(
- &self,
- constrained_regions: FxHashSet<ty::BoundRegionKind>,
- referenced_regions: FxHashSet<ty::BoundRegionKind>,
- generate_err: impl Fn(&str) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>,
- ) {
- for br in referenced_regions.difference(&constrained_regions) {
- let br_name = match *br {
- ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(_) | ty::BrEnv => {
- "an anonymous lifetime".to_string()
- }
- ty::BrNamed(_, name) => format!("lifetime `{}`", name),
- };
-
- let mut err = generate_err(&br_name);
-
- if let ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(_) = *br {
- // The only way for an anonymous lifetime to wind up
- // in the return type but **also** be unconstrained is
- // if it only appears in "associated types" in the
- // input. See #47511 and #62200 for examples. In this case,
- // though we can easily give a hint that ought to be
- // relevant.
- err.note(
- "lifetimes appearing in an associated type are not considered constrained",
- );
- }
-
- err.emit();
- }
- }
-
- /// Given the bounds on an object, determines what single region bound (if any) we can
- /// use to summarize this type. The basic idea is that we will use the bound the user
- /// provided, if they provided one, and otherwise search the supertypes of trait bounds
- /// for region bounds. It may be that we can derive no bound at all, in which case
- /// we return `None`.
- fn compute_object_lifetime_bound(
- &self,
- span: Span,
- existential_predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
- ) -> Option<ty::Region<'tcx>> // if None, use the default
- {
- let tcx = self.tcx();
-
- debug!("compute_opt_region_bound(existential_predicates={:?})", existential_predicates);
-
- // No explicit region bound specified. Therefore, examine trait
- // bounds and see if we can derive region bounds from those.
- let derived_region_bounds = object_region_bounds(tcx, existential_predicates);
-
- // If there are no derived region bounds, then report back that we
- // can find no region bound. The caller will use the default.
- if derived_region_bounds.is_empty() {
- return None;
- }
-
- // If any of the derived region bounds are 'static, that is always
- // the best choice.
- if derived_region_bounds.iter().any(|r| r.is_static()) {
- return Some(tcx.lifetimes.re_static);
- }
-
- // Determine whether there is exactly one unique region in the set
- // of derived region bounds. If so, use that. Otherwise, report an
- // error.
- let r = derived_region_bounds[0];
- if derived_region_bounds[1..].iter().any(|r1| r != *r1) {
- tcx.sess.emit_err(AmbiguousLifetimeBound { span });
- }
- Some(r)
- }
-
- /// Make sure that we are in the condition to suggest the blanket implementation.
- fn maybe_lint_blanket_trait_impl<T: rustc_errors::EmissionGuarantee>(
- &self,
- self_ty: &hir::Ty<'_>,
- diag: &mut DiagnosticBuilder<'_, T>,
- ) {
- let tcx = self.tcx();
- let parent_id = tcx.hir().get_parent_item(self_ty.hir_id);
- if let hir::Node::Item(hir::Item {
- kind:
- hir::ItemKind::Impl(hir::Impl {
- self_ty: impl_self_ty, of_trait: Some(of_trait_ref), generics, ..
- }),
- ..
- }) = tcx.hir().get_by_def_id(parent_id) && self_ty.hir_id == impl_self_ty.hir_id
- {
- if !of_trait_ref.trait_def_id().map_or(false, |def_id| def_id.is_local()) {
- return;
- }
- let of_trait_span = of_trait_ref.path.span;
- // make sure that we are not calling unwrap to abort during the compilation
- let Ok(impl_trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { return; };
- let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { return; };
- // check if the trait has generics, to make a correct suggestion
- let param_name = generics.params.next_type_param_name(None);
-
- let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() {
- (span, format!(", {}: {}", param_name, impl_trait_name))
- } else {
- (generics.span, format!("<{}: {}>", param_name, impl_trait_name))
- };
- diag.multipart_suggestion(
- format!("alternatively use a blanket \
- implementation to implement `{of_trait_name}` for \
- all types that also implement `{impl_trait_name}`"),
- vec![
- (self_ty.span, param_name),
- add_generic_sugg,
- ],
- Applicability::MaybeIncorrect,
- );
- }
- }
-
- fn maybe_lint_bare_trait(&self, self_ty: &hir::Ty<'_>, in_path: bool) {
- let tcx = self.tcx();
- if let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) =
- self_ty.kind
- {
- let needs_bracket = in_path
- && !tcx
- .sess
- .source_map()
- .span_to_prev_source(self_ty.span)
- .ok()
- .map_or(false, |s| s.trim_end().ends_with('<'));
-
- let is_global = poly_trait_ref.trait_ref.path.is_global();
- let sugg = Vec::from_iter([
- (
- self_ty.span.shrink_to_lo(),
- format!(
- "{}dyn {}",
- if needs_bracket { "<" } else { "" },
- if is_global { "(" } else { "" },
- ),
- ),
- (
- self_ty.span.shrink_to_hi(),
- format!(
- "{}{}",
- if is_global { ")" } else { "" },
- if needs_bracket { ">" } else { "" },
- ),
- ),
- ]);
- if self_ty.span.edition() >= Edition::Edition2021 {
- let msg = "trait objects must include the `dyn` keyword";
- let label = "add `dyn` keyword before this trait";
- let mut diag =
- rustc_errors::struct_span_err!(tcx.sess, self_ty.span, E0782, "{}", msg);
- diag.multipart_suggestion_verbose(label, sugg, Applicability::MachineApplicable);
- // check if the impl trait that we are considering is a impl of a local trait
- self.maybe_lint_blanket_trait_impl(&self_ty, &mut diag);
- diag.emit();
- } else {
- let msg = "trait objects without an explicit `dyn` are deprecated";
- tcx.struct_span_lint_hir(
- BARE_TRAIT_OBJECTS,
- self_ty.hir_id,
- self_ty.span,
- |lint| {
- let mut diag = lint.build(msg);
- diag.multipart_suggestion_verbose(
- "use `dyn`",
- sugg,
- Applicability::MachineApplicable,
- );
- self.maybe_lint_blanket_trait_impl::<()>(&self_ty, &mut diag);
- diag.emit();
- },
- );
- }
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs
deleted file mode 100644
index 75f5aced8..000000000
--- a/compiler/rustc_typeck/src/check/callee.rs
+++ /dev/null
@@ -1,675 +0,0 @@
-use super::method::MethodCallee;
-use super::{Expectation, FnCtxt, TupleArgumentsFlag};
-use crate::type_error_struct;
-
-use rustc_errors::{struct_span_err, Applicability, Diagnostic};
-use rustc_hir as hir;
-use rustc_hir::def::{self, Namespace, Res};
-use rustc_hir::def_id::DefId;
-use rustc_infer::{
- infer,
- traits::{self, Obligation},
-};
-use rustc_infer::{
- infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
- traits::ObligationCause,
-};
-use rustc_middle::ty::adjustment::{
- Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
-};
-use rustc_middle::ty::subst::{Subst, SubstsRef};
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
-use rustc_span::def_id::LocalDefId;
-use rustc_span::symbol::{sym, Ident};
-use rustc_span::Span;
-use rustc_target::spec::abi;
-use rustc_trait_selection::autoderef::Autoderef;
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
-
-use std::iter;
-
-/// Checks that it is legal to call methods of the trait corresponding
-/// to `trait_id` (this only cares about the trait, not the specific
-/// method that is called).
-pub fn check_legal_trait_for_method_call(
- tcx: TyCtxt<'_>,
- span: Span,
- receiver: Option<Span>,
- expr_span: Span,
- trait_id: DefId,
-) {
- if tcx.lang_items().drop_trait() == Some(trait_id) {
- let mut err = struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method");
- err.span_label(span, "explicit destructor calls not allowed");
-
- let (sp, suggestion) = receiver
- .and_then(|s| tcx.sess.source_map().span_to_snippet(s).ok())
- .filter(|snippet| !snippet.is_empty())
- .map(|snippet| (expr_span, format!("drop({snippet})")))
- .unwrap_or_else(|| (span, "drop".to_string()));
-
- err.span_suggestion(
- sp,
- "consider using `drop` function",
- suggestion,
- Applicability::MaybeIncorrect,
- );
-
- err.emit();
- }
-}
-
-enum CallStep<'tcx> {
- Builtin(Ty<'tcx>),
- DeferredClosure(LocalDefId, ty::FnSig<'tcx>),
- /// E.g., enum variant constructors.
- Overloaded(MethodCallee<'tcx>),
-}
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- pub fn check_call(
- &self,
- call_expr: &'tcx hir::Expr<'tcx>,
- callee_expr: &'tcx hir::Expr<'tcx>,
- arg_exprs: &'tcx [hir::Expr<'tcx>],
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- let original_callee_ty = match &callee_expr.kind {
- hir::ExprKind::Path(hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)) => self
- .check_expr_with_expectation_and_args(
- callee_expr,
- Expectation::NoExpectation,
- arg_exprs,
- ),
- _ => self.check_expr(callee_expr),
- };
-
- let expr_ty = self.structurally_resolved_type(call_expr.span, original_callee_ty);
-
- let mut autoderef = self.autoderef(callee_expr.span, expr_ty);
- let mut result = None;
- while result.is_none() && autoderef.next().is_some() {
- result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef);
- }
- self.register_predicates(autoderef.into_obligations());
-
- let output = match result {
- None => {
- // this will report an error since original_callee_ty is not a fn
- self.confirm_builtin_call(
- call_expr,
- callee_expr,
- original_callee_ty,
- arg_exprs,
- expected,
- )
- }
-
- Some(CallStep::Builtin(callee_ty)) => {
- self.confirm_builtin_call(call_expr, callee_expr, callee_ty, arg_exprs, expected)
- }
-
- Some(CallStep::DeferredClosure(def_id, fn_sig)) => {
- self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, def_id, fn_sig)
- }
-
- Some(CallStep::Overloaded(method_callee)) => {
- self.confirm_overloaded_call(call_expr, arg_exprs, expected, method_callee)
- }
- };
-
- // we must check that return type of called functions is WF:
- self.register_wf_obligation(output.into(), call_expr.span, traits::WellFormed(None));
-
- output
- }
-
- fn try_overloaded_call_step(
- &self,
- call_expr: &'tcx hir::Expr<'tcx>,
- callee_expr: &'tcx hir::Expr<'tcx>,
- arg_exprs: &'tcx [hir::Expr<'tcx>],
- autoderef: &Autoderef<'a, 'tcx>,
- ) -> Option<CallStep<'tcx>> {
- let adjusted_ty =
- self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
- debug!(
- "try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})",
- call_expr, adjusted_ty
- );
-
- // If the callee is a bare function or a closure, then we're all set.
- match *adjusted_ty.kind() {
- ty::FnDef(..) | ty::FnPtr(_) => {
- let adjustments = self.adjust_steps(autoderef);
- self.apply_adjustments(callee_expr, adjustments);
- return Some(CallStep::Builtin(adjusted_ty));
- }
-
- ty::Closure(def_id, substs) => {
- let def_id = def_id.expect_local();
-
- // Check whether this is a call to a closure where we
- // haven't yet decided on whether the closure is fn vs
- // fnmut vs fnonce. If so, we have to defer further processing.
- if self.closure_kind(substs).is_none() {
- let closure_sig = substs.as_closure().sig();
- let closure_sig = self.replace_bound_vars_with_fresh_vars(
- call_expr.span,
- infer::FnCall,
- closure_sig,
- );
- let adjustments = self.adjust_steps(autoderef);
- self.record_deferred_call_resolution(
- def_id,
- DeferredCallResolution {
- call_expr,
- callee_expr,
- adjusted_ty,
- adjustments,
- fn_sig: closure_sig,
- closure_substs: substs,
- },
- );
- return Some(CallStep::DeferredClosure(def_id, closure_sig));
- }
- }
-
- // Hack: we know that there are traits implementing Fn for &F
- // where F:Fn and so forth. In the particular case of types
- // like `x: &mut FnMut()`, if there is a call `x()`, we would
- // normally translate to `FnMut::call_mut(&mut x, ())`, but
- // that winds up requiring `mut x: &mut FnMut()`. A little
- // over the top. The simplest fix by far is to just ignore
- // this case and deref again, so we wind up with
- // `FnMut::call_mut(&mut *x, ())`.
- ty::Ref(..) if autoderef.step_count() == 0 => {
- return None;
- }
-
- _ => {}
- }
-
- // Now, we look for the implementation of a Fn trait on the object's type.
- // We first do it with the explicit instruction to look for an impl of
- // `Fn<Tuple>`, with the tuple `Tuple` having an arity corresponding
- // to the number of call parameters.
- // If that fails (or_else branch), we try again without specifying the
- // shape of the tuple (hence the None). This allows to detect an Fn trait
- // is implemented, and use this information for diagnostic.
- self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs))
- .or_else(|| self.try_overloaded_call_traits(call_expr, adjusted_ty, None))
- .map(|(autoref, method)| {
- let mut adjustments = self.adjust_steps(autoderef);
- adjustments.extend(autoref);
- self.apply_adjustments(callee_expr, adjustments);
- CallStep::Overloaded(method)
- })
- }
-
- fn try_overloaded_call_traits(
- &self,
- call_expr: &hir::Expr<'_>,
- adjusted_ty: Ty<'tcx>,
- opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>,
- ) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> {
- // Try the options that are least restrictive on the caller first.
- for (opt_trait_def_id, method_name, borrow) in [
- (self.tcx.lang_items().fn_trait(), Ident::with_dummy_span(sym::call), true),
- (self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true),
- (self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false),
- ] {
- let Some(trait_def_id) = opt_trait_def_id else { continue };
-
- let opt_input_types = opt_arg_exprs.map(|arg_exprs| {
- [self.tcx.mk_tup(arg_exprs.iter().map(|e| {
- self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::TypeInference,
- span: e.span,
- })
- }))]
- });
- let opt_input_types = opt_input_types.as_ref().map(AsRef::as_ref);
-
- if let Some(ok) = self.lookup_method_in_trait(
- call_expr.span,
- method_name,
- trait_def_id,
- adjusted_ty,
- opt_input_types,
- ) {
- let method = self.register_infer_ok_obligations(ok);
- let mut autoref = None;
- if borrow {
- // Check for &self vs &mut self in the method signature. Since this is either
- // the Fn or FnMut trait, it should be one of those.
- let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() else {
- // The `fn`/`fn_mut` lang item is ill-formed, which should have
- // caused an error elsewhere.
- self.tcx
- .sess
- .delay_span_bug(call_expr.span, "input to call/call_mut is not a ref?");
- return None;
- };
-
- let mutbl = match mutbl {
- hir::Mutability::Not => AutoBorrowMutability::Not,
- hir::Mutability::Mut => AutoBorrowMutability::Mut {
- // For initial two-phase borrow
- // deployment, conservatively omit
- // overloaded function call ops.
- allow_two_phase_borrow: AllowTwoPhase::No,
- },
- };
- autoref = Some(Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
- target: method.sig.inputs()[0],
- });
- }
- return Some((autoref, method));
- }
- }
-
- None
- }
-
- /// Give appropriate suggestion when encountering `||{/* not callable */}()`, where the
- /// likely intention is to call the closure, suggest `(||{})()`. (#55851)
- fn identify_bad_closure_def_and_call(
- &self,
- err: &mut Diagnostic,
- hir_id: hir::HirId,
- callee_node: &hir::ExprKind<'_>,
- callee_span: Span,
- ) {
- let hir = self.tcx.hir();
- let parent_hir_id = hir.get_parent_node(hir_id);
- let parent_node = hir.get(parent_hir_id);
- if let (
- hir::Node::Expr(hir::Expr {
- kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, body, .. }),
- ..
- }),
- hir::ExprKind::Block(..),
- ) = (parent_node, callee_node)
- {
- let fn_decl_span = if hir.body(body).generator_kind
- == Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure))
- {
- // Actually need to unwrap a few more layers of HIR to get to
- // the _real_ closure...
- let async_closure = hir.get_parent_node(hir.get_parent_node(parent_hir_id));
- if let hir::Node::Expr(hir::Expr {
- kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }),
- ..
- }) = hir.get(async_closure)
- {
- fn_decl_span
- } else {
- return;
- }
- } else {
- fn_decl_span
- };
-
- let start = fn_decl_span.shrink_to_lo();
- let end = callee_span.shrink_to_hi();
- err.multipart_suggestion(
- "if you meant to create this closure and immediately call it, surround the \
- closure with parentheses",
- vec![(start, "(".to_string()), (end, ")".to_string())],
- Applicability::MaybeIncorrect,
- );
- }
- }
-
- /// Give appropriate suggestion when encountering `[("a", 0) ("b", 1)]`, where the
- /// likely intention is to create an array containing tuples.
- fn maybe_suggest_bad_array_definition(
- &self,
- err: &mut Diagnostic,
- call_expr: &'tcx hir::Expr<'tcx>,
- callee_expr: &'tcx hir::Expr<'tcx>,
- ) -> bool {
- let hir_id = self.tcx.hir().get_parent_node(call_expr.hir_id);
- let parent_node = self.tcx.hir().get(hir_id);
- if let (
- hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Array(_), .. }),
- hir::ExprKind::Tup(exp),
- hir::ExprKind::Call(_, args),
- ) = (parent_node, &callee_expr.kind, &call_expr.kind)
- && args.len() == exp.len()
- {
- let start = callee_expr.span.shrink_to_hi();
- err.span_suggestion(
- start,
- "consider separating array elements with a comma",
- ",",
- Applicability::MaybeIncorrect,
- );
- return true;
- }
- false
- }
-
- fn confirm_builtin_call(
- &self,
- call_expr: &'tcx hir::Expr<'tcx>,
- callee_expr: &'tcx hir::Expr<'tcx>,
- callee_ty: Ty<'tcx>,
- arg_exprs: &'tcx [hir::Expr<'tcx>],
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- let (fn_sig, def_id) = match *callee_ty.kind() {
- ty::FnDef(def_id, subst) => {
- let fn_sig = self.tcx.bound_fn_sig(def_id).subst(self.tcx, subst);
-
- // Unit testing: function items annotated with
- // `#[rustc_evaluate_where_clauses]` trigger special output
- // to let us test the trait evaluation system.
- if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) {
- let predicates = self.tcx.predicates_of(def_id);
- let predicates = predicates.instantiate(self.tcx, subst);
- for (predicate, predicate_span) in
- predicates.predicates.iter().zip(&predicates.spans)
- {
- let obligation = Obligation::new(
- ObligationCause::dummy_with_span(callee_expr.span),
- self.param_env,
- *predicate,
- );
- let result = self.evaluate_obligation(&obligation);
- self.tcx
- .sess
- .struct_span_err(
- callee_expr.span,
- &format!("evaluate({:?}) = {:?}", predicate, result),
- )
- .span_label(*predicate_span, "predicate")
- .emit();
- }
- }
- (fn_sig, Some(def_id))
- }
- ty::FnPtr(sig) => (sig, None),
- _ => {
- let mut unit_variant = None;
- if let hir::ExprKind::Path(qpath) = &callee_expr.kind
- && let Res::Def(def::DefKind::Ctor(kind, def::CtorKind::Const), _)
- = self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id)
- // Only suggest removing parens if there are no arguments
- && arg_exprs.is_empty()
- {
- let descr = match kind {
- def::CtorOf::Struct => "struct",
- def::CtorOf::Variant => "enum variant",
- };
- let removal_span =
- callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi());
- unit_variant =
- Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(qpath)));
- }
-
- let callee_ty = self.resolve_vars_if_possible(callee_ty);
- let mut err = type_error_struct!(
- self.tcx.sess,
- callee_expr.span,
- callee_ty,
- E0618,
- "expected function, found {}",
- match &unit_variant {
- Some((_, kind, path)) => format!("{kind} `{path}`"),
- None => format!("`{callee_ty}`"),
- }
- );
-
- self.identify_bad_closure_def_and_call(
- &mut err,
- call_expr.hir_id,
- &callee_expr.kind,
- callee_expr.span,
- );
-
- if let Some((removal_span, kind, path)) = &unit_variant {
- err.span_suggestion_verbose(
- *removal_span,
- &format!(
- "`{path}` is a unit {kind}, and does not take parentheses to be constructed",
- ),
- "",
- Applicability::MachineApplicable,
- );
- }
-
- let mut inner_callee_path = None;
- let def = match callee_expr.kind {
- hir::ExprKind::Path(ref qpath) => {
- self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id)
- }
- hir::ExprKind::Call(ref inner_callee, _) => {
- // If the call spans more than one line and the callee kind is
- // itself another `ExprCall`, that's a clue that we might just be
- // missing a semicolon (Issue #51055)
- let call_is_multiline =
- self.tcx.sess.source_map().is_multiline(call_expr.span);
- if call_is_multiline {
- err.span_suggestion(
- callee_expr.span.shrink_to_hi(),
- "consider using a semicolon here",
- ";",
- Applicability::MaybeIncorrect,
- );
- }
- if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.kind {
- inner_callee_path = Some(inner_qpath);
- self.typeck_results.borrow().qpath_res(inner_qpath, inner_callee.hir_id)
- } else {
- Res::Err
- }
- }
- _ => Res::Err,
- };
-
- if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) {
- err.span_label(call_expr.span, "call expression requires function");
- }
-
- if let Some(span) = self.tcx.hir().res_span(def) {
- let callee_ty = callee_ty.to_string();
- let label = match (unit_variant, inner_callee_path) {
- (Some((_, kind, path)), _) => Some(format!("{kind} `{path}` defined here")),
- (_, Some(hir::QPath::Resolved(_, path))) => self
- .tcx
- .sess
- .source_map()
- .span_to_snippet(path.span)
- .ok()
- .map(|p| format!("`{p}` defined here returns `{callee_ty}`")),
- _ => {
- match def {
- // Emit a different diagnostic for local variables, as they are not
- // type definitions themselves, but rather variables *of* that type.
- Res::Local(hir_id) => Some(format!(
- "`{}` has type `{}`",
- self.tcx.hir().name(hir_id),
- callee_ty
- )),
- Res::Def(kind, def_id) if kind.ns() == Some(Namespace::ValueNS) => {
- Some(format!(
- "`{}` defined here",
- self.tcx.def_path_str(def_id),
- ))
- }
- _ => Some(format!("`{callee_ty}` defined here")),
- }
- }
- };
- if let Some(label) = label {
- err.span_label(span, label);
- }
- }
- err.emit();
-
- // This is the "default" function signature, used in case of error.
- // In that case, we check each argument against "error" in order to
- // set up all the node type bindings.
- (
- ty::Binder::dummy(self.tcx.mk_fn_sig(
- self.err_args(arg_exprs.len()).into_iter(),
- self.tcx.ty_error(),
- false,
- hir::Unsafety::Normal,
- abi::Abi::Rust,
- )),
- None,
- )
- }
- };
-
- // Replace any late-bound regions that appear in the function
- // signature with region variables. We also have to
- // renormalize the associated types at this point, since they
- // previously appeared within a `Binder<>` and hence would not
- // have been normalized before.
- let fn_sig = self.replace_bound_vars_with_fresh_vars(call_expr.span, infer::FnCall, fn_sig);
- let fn_sig = self.normalize_associated_types_in(call_expr.span, fn_sig);
-
- // Call the generic checker.
- let expected_arg_tys = self.expected_inputs_for_expected_output(
- call_expr.span,
- expected,
- fn_sig.output(),
- fn_sig.inputs(),
- );
- self.check_argument_types(
- call_expr.span,
- call_expr,
- fn_sig.inputs(),
- expected_arg_tys,
- arg_exprs,
- fn_sig.c_variadic,
- TupleArgumentsFlag::DontTupleArguments,
- def_id,
- );
-
- fn_sig.output()
- }
-
- fn confirm_deferred_closure_call(
- &self,
- call_expr: &'tcx hir::Expr<'tcx>,
- arg_exprs: &'tcx [hir::Expr<'tcx>],
- expected: Expectation<'tcx>,
- closure_def_id: LocalDefId,
- fn_sig: ty::FnSig<'tcx>,
- ) -> Ty<'tcx> {
- // `fn_sig` is the *signature* of the closure being called. We
- // don't know the full details yet (`Fn` vs `FnMut` etc), but we
- // do know the types expected for each argument and the return
- // type.
-
- let expected_arg_tys = self.expected_inputs_for_expected_output(
- call_expr.span,
- expected,
- fn_sig.output(),
- fn_sig.inputs(),
- );
-
- self.check_argument_types(
- call_expr.span,
- call_expr,
- fn_sig.inputs(),
- expected_arg_tys,
- arg_exprs,
- fn_sig.c_variadic,
- TupleArgumentsFlag::TupleArguments,
- Some(closure_def_id.to_def_id()),
- );
-
- fn_sig.output()
- }
-
- fn confirm_overloaded_call(
- &self,
- call_expr: &'tcx hir::Expr<'tcx>,
- arg_exprs: &'tcx [hir::Expr<'tcx>],
- expected: Expectation<'tcx>,
- method_callee: MethodCallee<'tcx>,
- ) -> Ty<'tcx> {
- let output_type = self.check_method_argument_types(
- call_expr.span,
- call_expr,
- Ok(method_callee),
- arg_exprs,
- TupleArgumentsFlag::TupleArguments,
- expected,
- );
-
- self.write_method_call(call_expr.hir_id, method_callee);
- output_type
- }
-}
-
-#[derive(Debug)]
-pub struct DeferredCallResolution<'tcx> {
- call_expr: &'tcx hir::Expr<'tcx>,
- callee_expr: &'tcx hir::Expr<'tcx>,
- adjusted_ty: Ty<'tcx>,
- adjustments: Vec<Adjustment<'tcx>>,
- fn_sig: ty::FnSig<'tcx>,
- closure_substs: SubstsRef<'tcx>,
-}
-
-impl<'a, 'tcx> DeferredCallResolution<'tcx> {
- pub fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) {
- debug!("DeferredCallResolution::resolve() {:?}", self);
-
- // we should not be invoked until the closure kind has been
- // determined by upvar inference
- assert!(fcx.closure_kind(self.closure_substs).is_some());
-
- // We may now know enough to figure out fn vs fnmut etc.
- match fcx.try_overloaded_call_traits(self.call_expr, self.adjusted_ty, None) {
- Some((autoref, method_callee)) => {
- // One problem is that when we get here, we are going
- // to have a newly instantiated function signature
- // from the call trait. This has to be reconciled with
- // the older function signature we had before. In
- // principle we *should* be able to fn_sigs(), but we
- // can't because of the annoying need for a TypeTrace.
- // (This always bites me, should find a way to
- // refactor it.)
- let method_sig = method_callee.sig;
-
- debug!("attempt_resolution: method_callee={:?}", method_callee);
-
- for (method_arg_ty, self_arg_ty) in
- iter::zip(method_sig.inputs().iter().skip(1), self.fn_sig.inputs())
- {
- fcx.demand_eqtype(self.call_expr.span, *self_arg_ty, *method_arg_ty);
- }
-
- fcx.demand_eqtype(self.call_expr.span, method_sig.output(), self.fn_sig.output());
-
- let mut adjustments = self.adjustments;
- adjustments.extend(autoref);
- fcx.apply_adjustments(self.callee_expr, adjustments);
-
- fcx.write_method_call(self.call_expr.hir_id, method_callee);
- }
- None => {
- // This can happen if `#![no_core]` is used and the `fn/fn_mut/fn_once`
- // lang items are not defined (issue #86238).
- let mut err = fcx.inh.tcx.sess.struct_span_err(
- self.call_expr.span,
- "failed to find an overloaded call trait for closure call",
- );
- err.help(
- "make sure the `fn`/`fn_mut`/`fn_once` lang items are defined \
- and have associated `call`/`call_mut`/`call_once` functions",
- );
- err.emit();
- }
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs
deleted file mode 100644
index 9c1fd9b30..000000000
--- a/compiler/rustc_typeck/src/check/check.rs
+++ /dev/null
@@ -1,1712 +0,0 @@
-use crate::check::intrinsicck::InlineAsmCtxt;
-
-use super::coercion::CoerceMany;
-use super::compare_method::check_type_bounds;
-use super::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl};
-use super::*;
-use rustc_attr as attr;
-use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan};
-use rustc_hir as hir;
-use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::intravisit::Visitor;
-use rustc_hir::lang_items::LangItem;
-use rustc_hir::{ItemKind, Node, PathSegment};
-use rustc_infer::infer::outlives::env::OutlivesEnvironment;
-use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt};
-use rustc_infer::traits::Obligation;
-use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
-use rustc_middle::hir::nested_filter;
-use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
-use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::util::{Discr, IntTypeExt};
-use rustc_middle::ty::{
- self, ParamEnv, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
-};
-use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS};
-use rustc_span::symbol::sym;
-use rustc_span::{self, Span};
-use rustc_target::spec::abi::Abi;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
-use rustc_trait_selection::traits::{self, ObligationCtxt};
-use rustc_ty_utils::representability::{self, Representability};
-
-use std::iter;
-use std::ops::ControlFlow;
-
-pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) {
- match tcx.sess.target.is_abi_supported(abi) {
- Some(true) => (),
- Some(false) => {
- struct_span_err!(
- tcx.sess,
- span,
- E0570,
- "`{abi}` is not a supported ABI for the current target",
- )
- .emit();
- }
- None => {
- tcx.struct_span_lint_hir(UNSUPPORTED_CALLING_CONVENTIONS, hir_id, span, |lint| {
- lint.build("use of calling convention not supported on this target").emit();
- });
- }
- }
-
- // This ABI is only allowed on function pointers
- if abi == Abi::CCmseNonSecureCall {
- struct_span_err!(
- tcx.sess,
- span,
- E0781,
- "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"
- )
- .emit();
- }
-}
-
-/// Helper used for fns and closures. Does the grungy work of checking a function
-/// body and returns the function context used for that purpose, since in the case of a fn item
-/// there is still a bit more to do.
-///
-/// * ...
-/// * inherited: other fields inherited from the enclosing fn (if any)
-#[instrument(skip(inherited, body), level = "debug")]
-pub(super) fn check_fn<'a, 'tcx>(
- inherited: &'a Inherited<'a, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- fn_sig: ty::FnSig<'tcx>,
- decl: &'tcx hir::FnDecl<'tcx>,
- fn_id: hir::HirId,
- body: &'tcx hir::Body<'tcx>,
- can_be_generator: Option<hir::Movability>,
- return_type_pre_known: bool,
-) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) {
- // Create the function context. This is either derived from scratch or,
- // in the case of closures, based on the outer context.
- let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id);
- fcx.ps.set(UnsafetyState::function(fn_sig.unsafety, fn_id));
- fcx.return_type_pre_known = return_type_pre_known;
-
- let tcx = fcx.tcx;
- let hir = tcx.hir();
-
- let declared_ret_ty = fn_sig.output();
-
- let ret_ty =
- fcx.register_infer_ok_obligations(fcx.infcx.replace_opaque_types_with_inference_vars(
- declared_ret_ty,
- body.value.hir_id,
- decl.output.span(),
- param_env,
- ));
- // If we replaced declared_ret_ty with infer vars, then we must be infering
- // an opaque type, so set a flag so we can improve diagnostics.
- fcx.return_type_has_opaque = ret_ty != declared_ret_ty;
-
- fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
- fcx.ret_type_span = Some(decl.output.span());
-
- let span = body.value.span;
-
- fn_maybe_err(tcx, span, fn_sig.abi);
-
- if fn_sig.abi == Abi::RustCall {
- let expected_args = if let ImplicitSelfKind::None = decl.implicit_self { 1 } else { 2 };
-
- let err = || {
- let item = match tcx.hir().get(fn_id) {
- Node::Item(hir::Item { kind: ItemKind::Fn(header, ..), .. }) => Some(header),
- Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Fn(header, ..), ..
- }) => Some(header),
- Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Fn(header, ..),
- ..
- }) => Some(header),
- // Closures are RustCall, but they tuple their arguments, so shouldn't be checked
- Node::Expr(hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => None,
- node => bug!("Item being checked wasn't a function/closure: {:?}", node),
- };
-
- if let Some(header) = item {
- tcx.sess.span_err(header.span, "functions with the \"rust-call\" ABI must take a single non-self argument that is a tuple");
- }
- };
-
- if fn_sig.inputs().len() != expected_args {
- err()
- } else {
- // FIXME(CraftSpider) Add a check on parameter expansion, so we don't just make the ICE happen later on
- // This will probably require wide-scale changes to support a TupleKind obligation
- // We can't resolve this without knowing the type of the param
- if !matches!(fn_sig.inputs()[expected_args - 1].kind(), ty::Tuple(_) | ty::Param(_)) {
- err()
- }
- }
- }
-
- if body.generator_kind.is_some() && can_be_generator.is_some() {
- let yield_ty = fcx
- .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span });
- fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
-
- // Resume type defaults to `()` if the generator has no argument.
- let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit());
-
- fcx.resume_yield_tys = Some((resume_ty, yield_ty));
- }
-
- GatherLocalsVisitor::new(&fcx).visit_body(body);
-
- // C-variadic fns also have a `VaList` input that's not listed in `fn_sig`
- // (as it's created inside the body itself, not passed in from outside).
- let maybe_va_list = if fn_sig.c_variadic {
- let span = body.params.last().unwrap().span;
- let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span));
- let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span));
-
- Some(tcx.bound_type_of(va_list_did).subst(tcx, &[region.into()]))
- } else {
- None
- };
-
- // Add formal parameters.
- let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs);
- let inputs_fn = fn_sig.inputs().iter().copied();
- for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
- // Check the pattern.
- let ty_span = try { inputs_hir?.get(idx)?.span };
- fcx.check_pat_top(&param.pat, param_ty, ty_span, false);
-
- // Check that argument is Sized.
- // The check for a non-trivial pattern is a hack to avoid duplicate warnings
- // for simple cases like `fn foo(x: Trait)`,
- // where we would error once on the parameter as a whole, and once on the binding `x`.
- if param.pat.simple_ident().is_none() && !tcx.features().unsized_fn_params {
- fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span));
- }
-
- fcx.write_ty(param.hir_id, param_ty);
- }
-
- inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig);
-
- fcx.in_tail_expr = true;
- if let ty::Dynamic(..) = declared_ret_ty.kind() {
- // FIXME: We need to verify that the return type is `Sized` after the return expression has
- // been evaluated so that we have types available for all the nodes being returned, but that
- // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this
- // causes unsized errors caused by the `declared_ret_ty` to point at the return expression,
- // while keeping the current ordering we will ignore the tail expression's type because we
- // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr`
- // because we will trigger "unreachable expression" lints unconditionally.
- // Because of all of this, we perform a crude check to know whether the simplest `!Sized`
- // case that a newcomer might make, returning a bare trait, and in that case we populate
- // the tail expression's type so that the suggestion will be correct, but ignore all other
- // possible cases.
- fcx.check_expr(&body.value);
- fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
- } else {
- fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
- fcx.check_return_expr(&body.value, false);
- }
- fcx.in_tail_expr = false;
-
- // We insert the deferred_generator_interiors entry after visiting the body.
- // This ensures that all nested generators appear before the entry of this generator.
- // resolve_generator_interiors relies on this property.
- let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) {
- let interior = fcx
- .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span });
- fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind));
-
- let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap();
- Some(GeneratorTypes {
- resume_ty,
- yield_ty,
- interior,
- movability: can_be_generator.unwrap(),
- })
- } else {
- None
- };
-
- // Finalize the return check by taking the LUB of the return types
- // we saw and assigning it to the expected return type. This isn't
- // really expected to fail, since the coercions would have failed
- // earlier when trying to find a LUB.
- let coercion = fcx.ret_coercion.take().unwrap().into_inner();
- let mut actual_return_ty = coercion.complete(&fcx);
- debug!("actual_return_ty = {:?}", actual_return_ty);
- if let ty::Dynamic(..) = declared_ret_ty.kind() {
- // We have special-cased the case where the function is declared
- // `-> dyn Foo` and we don't actually relate it to the
- // `fcx.ret_coercion`, so just substitute a type variable.
- actual_return_ty =
- fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::DynReturnFn, span });
- debug!("actual_return_ty replaced with {:?}", actual_return_ty);
- }
-
- // HACK(oli-obk, compiler-errors): We should be comparing this against
- // `declared_ret_ty`, but then anything uninferred would be inferred to
- // the opaque type itself. That again would cause writeback to assume
- // we have a recursive call site and do the sadly stabilized fallback to `()`.
- fcx.demand_suptype(span, ret_ty, actual_return_ty);
-
- // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !`
- if let Some(panic_impl_did) = tcx.lang_items().panic_impl()
- && panic_impl_did == hir.local_def_id(fn_id).to_def_id()
- {
- check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty);
- }
-
- // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !`
- if let Some(alloc_error_handler_did) = tcx.lang_items().oom()
- && alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id()
- {
- check_alloc_error_fn(tcx, alloc_error_handler_did.expect_local(), fn_sig, decl, declared_ret_ty);
- }
-
- (fcx, gen_ty)
-}
-
-fn check_panic_info_fn(
- tcx: TyCtxt<'_>,
- fn_id: LocalDefId,
- fn_sig: ty::FnSig<'_>,
- decl: &hir::FnDecl<'_>,
- declared_ret_ty: Ty<'_>,
-) {
- let Some(panic_info_did) = tcx.lang_items().panic_info() else {
- tcx.sess.err("language item required, but not found: `panic_info`");
- return;
- };
-
- if *declared_ret_ty.kind() != ty::Never {
- tcx.sess.span_err(decl.output.span(), "return type should be `!`");
- }
-
- let inputs = fn_sig.inputs();
- if inputs.len() != 1 {
- tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument");
- return;
- }
-
- let arg_is_panic_info = match *inputs[0].kind() {
- ty::Ref(region, ty, mutbl) => match *ty.kind() {
- ty::Adt(ref adt, _) => {
- adt.did() == panic_info_did && mutbl == hir::Mutability::Not && !region.is_static()
- }
- _ => false,
- },
- _ => false,
- };
-
- if !arg_is_panic_info {
- tcx.sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`");
- }
-
- let DefKind::Fn = tcx.def_kind(fn_id) else {
- let span = tcx.def_span(fn_id);
- tcx.sess.span_err(span, "should be a function");
- return;
- };
-
- let generic_counts = tcx.generics_of(fn_id).own_counts();
- if generic_counts.types != 0 {
- let span = tcx.def_span(fn_id);
- tcx.sess.span_err(span, "should have no type parameters");
- }
- if generic_counts.consts != 0 {
- let span = tcx.def_span(fn_id);
- tcx.sess.span_err(span, "should have no const parameters");
- }
-}
-
-fn check_alloc_error_fn(
- tcx: TyCtxt<'_>,
- fn_id: LocalDefId,
- fn_sig: ty::FnSig<'_>,
- decl: &hir::FnDecl<'_>,
- declared_ret_ty: Ty<'_>,
-) {
- let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() else {
- tcx.sess.err("language item required, but not found: `alloc_layout`");
- return;
- };
-
- if *declared_ret_ty.kind() != ty::Never {
- tcx.sess.span_err(decl.output.span(), "return type should be `!`");
- }
-
- let inputs = fn_sig.inputs();
- if inputs.len() != 1 {
- tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument");
- return;
- }
-
- let arg_is_alloc_layout = match inputs[0].kind() {
- ty::Adt(ref adt, _) => adt.did() == alloc_layout_did,
- _ => false,
- };
-
- if !arg_is_alloc_layout {
- tcx.sess.span_err(decl.inputs[0].span, "argument should be `Layout`");
- }
-
- let DefKind::Fn = tcx.def_kind(fn_id) else {
- let span = tcx.def_span(fn_id);
- tcx.sess.span_err(span, "`#[alloc_error_handler]` should be a function");
- return;
- };
-
- let generic_counts = tcx.generics_of(fn_id).own_counts();
- if generic_counts.types != 0 {
- let span = tcx.def_span(fn_id);
- tcx.sess.span_err(span, "`#[alloc_error_handler]` function should have no type parameters");
- }
- if generic_counts.consts != 0 {
- let span = tcx.def_span(fn_id);
- tcx.sess
- .span_err(span, "`#[alloc_error_handler]` function should have no const parameters");
- }
-}
-
-fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
- let def = tcx.adt_def(def_id);
- let span = tcx.def_span(def_id);
- def.destructor(tcx); // force the destructor to be evaluated
- check_representable(tcx, span, def_id);
-
- if def.repr().simd() {
- check_simd(tcx, span, def_id);
- }
-
- check_transparent(tcx, span, def);
- check_packed(tcx, span, def);
-}
-
-fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
- let def = tcx.adt_def(def_id);
- let span = tcx.def_span(def_id);
- def.destructor(tcx); // force the destructor to be evaluated
- check_representable(tcx, span, def_id);
- check_transparent(tcx, span, def);
- check_union_fields(tcx, span, def_id);
- check_packed(tcx, span, def);
-}
-
-/// Check that the fields of the `union` do not need dropping.
-fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool {
- let item_type = tcx.type_of(item_def_id);
- if let ty::Adt(def, substs) = item_type.kind() {
- assert!(def.is_union());
-
- fn allowed_union_field<'tcx>(
- ty: Ty<'tcx>,
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- span: Span,
- ) -> bool {
- // We don't just accept all !needs_drop fields, due to semver concerns.
- match ty.kind() {
- ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check)
- ty::Tuple(tys) => {
- // allow tuples of allowed types
- tys.iter().all(|ty| allowed_union_field(ty, tcx, param_env, span))
- }
- ty::Array(elem, _len) => {
- // Like `Copy`, we do *not* special-case length 0.
- allowed_union_field(*elem, tcx, param_env, span)
- }
- _ => {
- // Fallback case: allow `ManuallyDrop` and things that are `Copy`.
- ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop())
- || ty.is_copy_modulo_regions(tcx.at(span), param_env)
- }
- }
- }
-
- let param_env = tcx.param_env(item_def_id);
- for field in &def.non_enum_variant().fields {
- let field_ty = field.ty(tcx, substs);
-
- if !allowed_union_field(field_ty, tcx, param_env, span) {
- let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) {
- // We are currently checking the type this field came from, so it must be local.
- Some(Node::Field(field)) => (field.span, field.ty.span),
- _ => unreachable!("mir field has to correspond to hir field"),
- };
- struct_span_err!(
- tcx.sess,
- field_span,
- E0740,
- "unions cannot contain fields that may need dropping"
- )
- .note(
- "a type is guaranteed not to need dropping \
- when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type",
- )
- .multipart_suggestion_verbose(
- "when the type does not implement `Copy`, \
- wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped",
- vec![
- (ty_span.shrink_to_lo(), "std::mem::ManuallyDrop<".into()),
- (ty_span.shrink_to_hi(), ">".into()),
- ],
- Applicability::MaybeIncorrect,
- )
- .emit();
- return false;
- } else if field_ty.needs_drop(tcx, param_env) {
- // This should never happen. But we can get here e.g. in case of name resolution errors.
- tcx.sess.delay_span_bug(span, "we should never accept maybe-dropping union fields");
- }
- }
- } else {
- span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind());
- }
- true
-}
-
-/// Check that a `static` is inhabited.
-fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
- // Make sure statics are inhabited.
- // Other parts of the compiler assume that there are no uninhabited places. In principle it
- // would be enough to check this for `extern` statics, as statics with an initializer will
- // have UB during initialization if they are uninhabited, but there also seems to be no good
- // reason to allow any statics to be uninhabited.
- let ty = tcx.type_of(def_id);
- let span = tcx.def_span(def_id);
- let layout = match tcx.layout_of(ParamEnv::reveal_all().and(ty)) {
- Ok(l) => l,
- // Foreign statics that overflow their allowed size should emit an error
- Err(LayoutError::SizeOverflow(_))
- if {
- let node = tcx.hir().get_by_def_id(def_id);
- matches!(
- node,
- hir::Node::ForeignItem(hir::ForeignItem {
- kind: hir::ForeignItemKind::Static(..),
- ..
- })
- )
- } =>
- {
- tcx.sess
- .struct_span_err(span, "extern static is too large for the current architecture")
- .emit();
- return;
- }
- // Generic statics are rejected, but we still reach this case.
- Err(e) => {
- tcx.sess.delay_span_bug(span, &e.to_string());
- return;
- }
- };
- if layout.abi.is_uninhabited() {
- tcx.struct_span_lint_hir(
- UNINHABITED_STATIC,
- tcx.hir().local_def_id_to_hir_id(def_id),
- span,
- |lint| {
- lint.build("static of uninhabited type")
- .note("uninhabited statics cannot be initialized, and any access would be an immediate error")
- .emit();
- },
- );
- }
-}
-
-/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
-/// projections that would result in "inheriting lifetimes".
-pub(super) fn check_opaque<'tcx>(
- tcx: TyCtxt<'tcx>,
- def_id: LocalDefId,
- substs: SubstsRef<'tcx>,
- origin: &hir::OpaqueTyOrigin,
-) {
- let span = tcx.def_span(def_id);
- check_opaque_for_inheriting_lifetimes(tcx, def_id, span);
- if tcx.type_of(def_id).references_error() {
- return;
- }
- if check_opaque_for_cycles(tcx, def_id, substs, span, origin).is_err() {
- return;
- }
- check_opaque_meets_bounds(tcx, def_id, substs, span, origin);
-}
-
-/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
-/// in "inheriting lifetimes".
-#[instrument(level = "debug", skip(tcx, span))]
-pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(
- tcx: TyCtxt<'tcx>,
- def_id: LocalDefId,
- span: Span,
-) {
- let item = tcx.hir().expect_item(def_id);
- debug!(?item, ?span);
-
- struct FoundParentLifetime;
- struct FindParentLifetimeVisitor<'tcx>(&'tcx ty::Generics);
- impl<'tcx> ty::visit::TypeVisitor<'tcx> for FindParentLifetimeVisitor<'tcx> {
- type BreakTy = FoundParentLifetime;
-
- fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- debug!("FindParentLifetimeVisitor: r={:?}", r);
- if let ty::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = *r {
- if index < self.0.parent_count as u32 {
- return ControlFlow::Break(FoundParentLifetime);
- } else {
- return ControlFlow::CONTINUE;
- }
- }
-
- r.super_visit_with(self)
- }
-
- fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
- if let ty::ConstKind::Unevaluated(..) = c.kind() {
- // FIXME(#72219) We currently don't detect lifetimes within substs
- // which would violate this check. Even though the particular substitution is not used
- // within the const, this should still be fixed.
- return ControlFlow::CONTINUE;
- }
- c.super_visit_with(self)
- }
- }
-
- struct ProhibitOpaqueVisitor<'tcx> {
- tcx: TyCtxt<'tcx>,
- opaque_identity_ty: Ty<'tcx>,
- generics: &'tcx ty::Generics,
- selftys: Vec<(Span, Option<String>)>,
- }
-
- impl<'tcx> ty::visit::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> {
- type BreakTy = Ty<'tcx>;
-
- fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
- debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t);
- if t == self.opaque_identity_ty {
- ControlFlow::CONTINUE
- } else {
- t.super_visit_with(&mut FindParentLifetimeVisitor(self.generics))
- .map_break(|FoundParentLifetime| t)
- }
- }
- }
-
- impl<'tcx> Visitor<'tcx> for ProhibitOpaqueVisitor<'tcx> {
- type NestedFilter = nested_filter::OnlyBodies;
-
- fn nested_visit_map(&mut self) -> Self::Map {
- self.tcx.hir()
- }
-
- fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
- match arg.kind {
- hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
- [
- PathSegment {
- res: Some(Res::SelfTy { trait_: _, alias_to: impl_ref }),
- ..
- },
- ] => {
- let impl_ty_name =
- impl_ref.map(|(def_id, _)| self.tcx.def_path_str(def_id));
- self.selftys.push((path.span, impl_ty_name));
- }
- _ => {}
- },
- _ => {}
- }
- hir::intravisit::walk_ty(self, arg);
- }
- }
-
- if let ItemKind::OpaqueTy(hir::OpaqueTy {
- origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
- ..
- }) = item.kind
- {
- let mut visitor = ProhibitOpaqueVisitor {
- opaque_identity_ty: tcx.mk_opaque(
- def_id.to_def_id(),
- InternalSubsts::identity_for_item(tcx, def_id.to_def_id()),
- ),
- generics: tcx.generics_of(def_id),
- tcx,
- selftys: vec![],
- };
- let prohibit_opaque = tcx
- .explicit_item_bounds(def_id)
- .iter()
- .try_for_each(|(predicate, _)| predicate.visit_with(&mut visitor));
- debug!(
- "check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor.opaque_identity_ty={:?}, visitor.generics={:?}",
- prohibit_opaque, visitor.opaque_identity_ty, visitor.generics
- );
-
- if let Some(ty) = prohibit_opaque.break_value() {
- visitor.visit_item(&item);
- let is_async = match item.kind {
- ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
- matches!(origin, hir::OpaqueTyOrigin::AsyncFn(..))
- }
- _ => unreachable!(),
- };
-
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0760,
- "`{}` return type cannot contain a projection or `Self` that references lifetimes from \
- a parent scope",
- if is_async { "async fn" } else { "impl Trait" },
- );
-
- for (span, name) in visitor.selftys {
- err.span_suggestion(
- span,
- "consider spelling out the type instead",
- name.unwrap_or_else(|| format!("{:?}", ty)),
- Applicability::MaybeIncorrect,
- );
- }
- err.emit();
- }
- }
-}
-
-/// Checks that an opaque type does not contain cycles.
-pub(super) fn check_opaque_for_cycles<'tcx>(
- tcx: TyCtxt<'tcx>,
- def_id: LocalDefId,
- substs: SubstsRef<'tcx>,
- span: Span,
- origin: &hir::OpaqueTyOrigin,
-) -> Result<(), ErrorGuaranteed> {
- if tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs).is_err() {
- let reported = match origin {
- hir::OpaqueTyOrigin::AsyncFn(..) => async_opaque_type_cycle_error(tcx, span),
- _ => opaque_type_cycle_error(tcx, def_id, span),
- };
- Err(reported)
- } else {
- Ok(())
- }
-}
-
-/// Check that the concrete type behind `impl Trait` actually implements `Trait`.
-///
-/// This is mostly checked at the places that specify the opaque type, but we
-/// check those cases in the `param_env` of that function, which may have
-/// bounds not on this opaque type:
-///
-/// type X<T> = impl Clone
-/// fn f<T: Clone>(t: T) -> X<T> {
-/// t
-/// }
-///
-/// Without this check the above code is incorrectly accepted: we would ICE if
-/// some tried, for example, to clone an `Option<X<&mut ()>>`.
-#[instrument(level = "debug", skip(tcx))]
-fn check_opaque_meets_bounds<'tcx>(
- tcx: TyCtxt<'tcx>,
- def_id: LocalDefId,
- substs: SubstsRef<'tcx>,
- span: Span,
- origin: &hir::OpaqueTyOrigin,
-) {
- let hidden_type = tcx.bound_type_of(def_id.to_def_id()).subst(tcx, substs);
-
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
- let defining_use_anchor = match *origin {
- hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did,
- hir::OpaqueTyOrigin::TyAlias => def_id,
- };
- let param_env = tcx.param_env(defining_use_anchor);
-
- tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor)).enter(
- move |infcx| {
- let ocx = ObligationCtxt::new(&infcx);
- let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs);
-
- let misc_cause = traits::ObligationCause::misc(span, hir_id);
-
- match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_type) {
- Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok),
- Err(ty_err) => {
- tcx.sess.delay_span_bug(
- span,
- &format!("could not unify `{hidden_type}` with revealed type:\n{ty_err}"),
- );
- }
- }
-
- // Additionally require the hidden type to be well-formed with only the generics of the opaque type.
- // Defining use functions may have more bounds than the opaque type, which is ok, as long as the
- // hidden type is well formed even without those bounds.
- let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_type.into()))
- .to_predicate(tcx);
- ocx.register_obligation(Obligation::new(misc_cause, param_env, predicate));
-
- // Check that all obligations are satisfied by the implementation's
- // version.
- let errors = ocx.select_all_or_error();
- if !errors.is_empty() {
- infcx.report_fulfillment_errors(&errors, None, false);
- }
- match origin {
- // Checked when type checking the function containing them.
- hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {}
- // Can have different predicates to their defining use
- hir::OpaqueTyOrigin::TyAlias => {
- let outlives_environment = OutlivesEnvironment::new(param_env);
- infcx.check_region_obligations_and_report_errors(
- defining_use_anchor,
- &outlives_environment,
- );
- }
- }
- // Clean up after ourselves
- let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
- },
- );
-}
-
-fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
- debug!(
- "check_item_type(it.def_id={:?}, it.name={})",
- id.def_id,
- tcx.def_path_str(id.def_id.to_def_id())
- );
- let _indenter = indenter();
- match tcx.def_kind(id.def_id) {
- DefKind::Static(..) => {
- tcx.ensure().typeck(id.def_id);
- maybe_check_static_with_link_section(tcx, id.def_id);
- check_static_inhabited(tcx, id.def_id);
- }
- DefKind::Const => {
- tcx.ensure().typeck(id.def_id);
- }
- DefKind::Enum => {
- let item = tcx.hir().item(id);
- let hir::ItemKind::Enum(ref enum_definition, _) = item.kind else {
- return;
- };
- check_enum(tcx, &enum_definition.variants, item.def_id);
- }
- DefKind::Fn => {} // entirely within check_item_body
- DefKind::Impl => {
- let it = tcx.hir().item(id);
- let hir::ItemKind::Impl(ref impl_) = it.kind else {
- return;
- };
- debug!("ItemKind::Impl {} with id {:?}", it.ident, it.def_id);
- if let Some(impl_trait_ref) = tcx.impl_trait_ref(it.def_id) {
- check_impl_items_against_trait(
- tcx,
- it.span,
- it.def_id,
- impl_trait_ref,
- &impl_.items,
- );
- check_on_unimplemented(tcx, it);
- }
- }
- DefKind::Trait => {
- let it = tcx.hir().item(id);
- let hir::ItemKind::Trait(_, _, _, _, ref items) = it.kind else {
- return;
- };
- check_on_unimplemented(tcx, it);
-
- for item in items.iter() {
- let item = tcx.hir().trait_item(item.id);
- match item.kind {
- hir::TraitItemKind::Fn(ref sig, _) => {
- let abi = sig.header.abi;
- fn_maybe_err(tcx, item.ident.span, abi);
- }
- hir::TraitItemKind::Type(.., Some(default)) => {
- let assoc_item = tcx.associated_item(item.def_id);
- let trait_substs =
- InternalSubsts::identity_for_item(tcx, it.def_id.to_def_id());
- let _: Result<_, rustc_errors::ErrorGuaranteed> = check_type_bounds(
- tcx,
- assoc_item,
- assoc_item,
- default.span,
- ty::TraitRef { def_id: it.def_id.to_def_id(), substs: trait_substs },
- );
- }
- _ => {}
- }
- }
- }
- DefKind::Struct => {
- check_struct(tcx, id.def_id);
- }
- DefKind::Union => {
- check_union(tcx, id.def_id);
- }
- DefKind::OpaqueTy => {
- let item = tcx.hir().item(id);
- let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else {
- return;
- };
- // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting
- // `async-std` (and `pub async fn` in general).
- // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it!
- // See https://github.com/rust-lang/rust/issues/75100
- if !tcx.sess.opts.actually_rustdoc {
- let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id());
- check_opaque(tcx, item.def_id, substs, &origin);
- }
- }
- DefKind::TyAlias => {
- let pty_ty = tcx.type_of(id.def_id);
- let generics = tcx.generics_of(id.def_id);
- check_type_params_are_used(tcx, &generics, pty_ty);
- }
- DefKind::ForeignMod => {
- let it = tcx.hir().item(id);
- let hir::ItemKind::ForeignMod { abi, items } = it.kind else {
- return;
- };
- check_abi(tcx, it.hir_id(), it.span, abi);
-
- if abi == Abi::RustIntrinsic {
- for item in items {
- let item = tcx.hir().foreign_item(item.id);
- intrinsic::check_intrinsic_type(tcx, item);
- }
- } else if abi == Abi::PlatformIntrinsic {
- for item in items {
- let item = tcx.hir().foreign_item(item.id);
- intrinsic::check_platform_intrinsic_type(tcx, item);
- }
- } else {
- for item in items {
- let def_id = item.id.def_id;
- let generics = tcx.generics_of(def_id);
- let own_counts = generics.own_counts();
- if generics.params.len() - own_counts.lifetimes != 0 {
- let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) {
- (_, 0) => ("type", "types", Some("u32")),
- // We don't specify an example value, because we can't generate
- // a valid value for any type.
- (0, _) => ("const", "consts", None),
- _ => ("type or const", "types or consts", None),
- };
- struct_span_err!(
- tcx.sess,
- item.span,
- E0044,
- "foreign items may not have {kinds} parameters",
- )
- .span_label(item.span, &format!("can't have {kinds} parameters"))
- .help(
- // FIXME: once we start storing spans for type arguments, turn this
- // into a suggestion.
- &format!(
- "replace the {} parameters with concrete {}{}",
- kinds,
- kinds_pl,
- egs.map(|egs| format!(" like `{}`", egs)).unwrap_or_default(),
- ),
- )
- .emit();
- }
-
- let item = tcx.hir().foreign_item(item.id);
- match item.kind {
- hir::ForeignItemKind::Fn(ref fn_decl, _, _) => {
- require_c_abi_if_c_variadic(tcx, fn_decl, abi, item.span);
- }
- hir::ForeignItemKind::Static(..) => {
- check_static_inhabited(tcx, def_id);
- }
- _ => {}
- }
- }
- }
- }
- DefKind::GlobalAsm => {
- let it = tcx.hir().item(id);
- let hir::ItemKind::GlobalAsm(asm) = it.kind else { span_bug!(it.span, "DefKind::GlobalAsm but got {:#?}", it) };
- InlineAsmCtxt::new_global_asm(tcx).check_asm(asm, id.hir_id());
- }
- _ => {}
- }
-}
-
-pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
- // an error would be reported if this fails.
- let _ = traits::OnUnimplementedDirective::of_item(tcx, item.def_id.to_def_id());
-}
-
-pub(super) fn check_specialization_validity<'tcx>(
- tcx: TyCtxt<'tcx>,
- trait_def: &ty::TraitDef,
- trait_item: &ty::AssocItem,
- impl_id: DefId,
- impl_item: &hir::ImplItemRef,
-) {
- let Ok(ancestors) = trait_def.ancestors(tcx, impl_id) else { return };
- let mut ancestor_impls = ancestors.skip(1).filter_map(|parent| {
- if parent.is_from_trait() {
- None
- } else {
- Some((parent, parent.item(tcx, trait_item.def_id)))
- }
- });
-
- let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| {
- match parent_item {
- // Parent impl exists, and contains the parent item we're trying to specialize, but
- // doesn't mark it `default`.
- Some(parent_item) if traits::impl_item_is_final(tcx, &parent_item) => {
- Some(Err(parent_impl.def_id()))
- }
-
- // Parent impl contains item and makes it specializable.
- Some(_) => Some(Ok(())),
-
- // Parent impl doesn't mention the item. This means it's inherited from the
- // grandparent. In that case, if parent is a `default impl`, inherited items use the
- // "defaultness" from the grandparent, else they are final.
- None => {
- if tcx.impl_defaultness(parent_impl.def_id()).is_default() {
- None
- } else {
- Some(Err(parent_impl.def_id()))
- }
- }
- }
- });
-
- // If `opt_result` is `None`, we have only encountered `default impl`s that don't contain the
- // item. This is allowed, the item isn't actually getting specialized here.
- let result = opt_result.unwrap_or(Ok(()));
-
- if let Err(parent_impl) = result {
- report_forbidden_specialization(tcx, impl_item, parent_impl);
- }
-}
-
-fn check_impl_items_against_trait<'tcx>(
- tcx: TyCtxt<'tcx>,
- full_impl_span: Span,
- impl_id: LocalDefId,
- impl_trait_ref: ty::TraitRef<'tcx>,
- impl_item_refs: &[hir::ImplItemRef],
-) {
- // If the trait reference itself is erroneous (so the compilation is going
- // to fail), skip checking the items here -- the `impl_item` table in `tcx`
- // isn't populated for such impls.
- if impl_trait_ref.references_error() {
- return;
- }
-
- // Negative impls are not expected to have any items
- match tcx.impl_polarity(impl_id) {
- ty::ImplPolarity::Reservation | ty::ImplPolarity::Positive => {}
- ty::ImplPolarity::Negative => {
- if let [first_item_ref, ..] = impl_item_refs {
- let first_item_span = tcx.hir().impl_item(first_item_ref.id).span;
- struct_span_err!(
- tcx.sess,
- first_item_span,
- E0749,
- "negative impls cannot have any items"
- )
- .emit();
- }
- return;
- }
- }
-
- let trait_def = tcx.trait_def(impl_trait_ref.def_id);
-
- for impl_item in impl_item_refs {
- let ty_impl_item = tcx.associated_item(impl_item.id.def_id);
- let ty_trait_item = if let Some(trait_item_id) = ty_impl_item.trait_item_def_id {
- tcx.associated_item(trait_item_id)
- } else {
- // Checked in `associated_item`.
- tcx.sess.delay_span_bug(impl_item.span, "missing associated item in trait");
- continue;
- };
- let impl_item_full = tcx.hir().impl_item(impl_item.id);
- match impl_item_full.kind {
- hir::ImplItemKind::Const(..) => {
- // Find associated const definition.
- compare_const_impl(
- tcx,
- &ty_impl_item,
- impl_item.span,
- &ty_trait_item,
- impl_trait_ref,
- );
- }
- hir::ImplItemKind::Fn(..) => {
- let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
- compare_impl_method(
- tcx,
- &ty_impl_item,
- &ty_trait_item,
- impl_trait_ref,
- opt_trait_span,
- );
- }
- hir::ImplItemKind::TyAlias(impl_ty) => {
- let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
- compare_ty_impl(
- tcx,
- &ty_impl_item,
- impl_ty.span,
- &ty_trait_item,
- impl_trait_ref,
- opt_trait_span,
- );
- }
- }
-
- check_specialization_validity(
- tcx,
- trait_def,
- &ty_trait_item,
- impl_id.to_def_id(),
- impl_item,
- );
- }
-
- if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) {
- // Check for missing items from trait
- let mut missing_items = Vec::new();
-
- let mut must_implement_one_of: Option<&[Ident]> =
- trait_def.must_implement_one_of.as_deref();
-
- for &trait_item_id in tcx.associated_item_def_ids(impl_trait_ref.def_id) {
- let is_implemented = ancestors
- .leaf_def(tcx, trait_item_id)
- .map_or(false, |node_item| node_item.item.defaultness(tcx).has_value());
-
- if !is_implemented && tcx.impl_defaultness(impl_id).is_final() {
- missing_items.push(tcx.associated_item(trait_item_id));
- }
-
- if let Some(required_items) = &must_implement_one_of {
- // true if this item is specifically implemented in this impl
- let is_implemented_here = ancestors
- .leaf_def(tcx, trait_item_id)
- .map_or(false, |node_item| !node_item.defining_node.is_from_trait());
-
- if is_implemented_here {
- let trait_item = tcx.associated_item(trait_item_id);
- if required_items.contains(&trait_item.ident(tcx)) {
- must_implement_one_of = None;
- }
- }
- }
- }
-
- if !missing_items.is_empty() {
- missing_items_err(tcx, tcx.def_span(impl_id), &missing_items, full_impl_span);
- }
-
- if let Some(missing_items) = must_implement_one_of {
- let attr_span = tcx
- .get_attr(impl_trait_ref.def_id, sym::rustc_must_implement_one_of)
- .map(|attr| attr.span);
-
- missing_items_must_implement_one_of_err(
- tcx,
- tcx.def_span(impl_id),
- missing_items,
- attr_span,
- );
- }
- }
-}
-
-/// Checks whether a type can be represented in memory. In particular, it
-/// identifies types that contain themselves without indirection through a
-/// pointer, which would mean their size is unbounded.
-pub(super) fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalDefId) -> bool {
- let rty = tcx.type_of(item_def_id);
-
- // Check that it is possible to represent this type. This call identifies
- // (1) types that contain themselves and (2) types that contain a different
- // recursive type. It is only necessary to throw an error on those that
- // contain themselves. For case 2, there must be an inner type that will be
- // caught by case 1.
- match representability::ty_is_representable(tcx, rty, sp, None) {
- Representability::SelfRecursive(spans) => {
- recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans);
- return false;
- }
- Representability::Representable | Representability::ContainsRecursive => (),
- }
- true
-}
-
-pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
- let t = tcx.type_of(def_id);
- if let ty::Adt(def, substs) = t.kind()
- && def.is_struct()
- {
- let fields = &def.non_enum_variant().fields;
- if fields.is_empty() {
- struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit();
- return;
- }
- let e = fields[0].ty(tcx, substs);
- if !fields.iter().all(|f| f.ty(tcx, substs) == e) {
- struct_span_err!(tcx.sess, sp, E0076, "SIMD vector should be homogeneous")
- .span_label(sp, "SIMD elements must have the same type")
- .emit();
- return;
- }
-
- let len = if let ty::Array(_ty, c) = e.kind() {
- c.try_eval_usize(tcx, tcx.param_env(def.did()))
- } else {
- Some(fields.len() as u64)
- };
- if let Some(len) = len {
- if len == 0 {
- struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit();
- return;
- } else if len > MAX_SIMD_LANES {
- struct_span_err!(
- tcx.sess,
- sp,
- E0075,
- "SIMD vector cannot have more than {MAX_SIMD_LANES} elements",
- )
- .emit();
- return;
- }
- }
-
- // Check that we use types valid for use in the lanes of a SIMD "vector register"
- // These are scalar types which directly match a "machine" type
- // Yes: Integers, floats, "thin" pointers
- // No: char, "fat" pointers, compound types
- match e.kind() {
- ty::Param(_) => (), // pass struct<T>(T, T, T, T) through, let monomorphization catch errors
- ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_) => (), // struct(u8, u8, u8, u8) is ok
- ty::Array(t, _) if matches!(t.kind(), ty::Param(_)) => (), // pass struct<T>([T; N]) through, let monomorphization catch errors
- ty::Array(t, _clen)
- if matches!(
- t.kind(),
- ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_)
- ) =>
- { /* struct([f32; 4]) is ok */ }
- _ => {
- struct_span_err!(
- tcx.sess,
- sp,
- E0077,
- "SIMD vector element type should be a \
- primitive scalar (integer/float/pointer) type"
- )
- .emit();
- return;
- }
- }
- }
-}
-
-pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
- let repr = def.repr();
- if repr.packed() {
- for attr in tcx.get_attrs(def.did(), sym::repr) {
- for r in attr::parse_repr_attr(&tcx.sess, attr) {
- if let attr::ReprPacked(pack) = r
- && let Some(repr_pack) = repr.pack
- && pack as u64 != repr_pack.bytes()
- {
- struct_span_err!(
- tcx.sess,
- sp,
- E0634,
- "type has conflicting packed representation hints"
- )
- .emit();
- }
- }
- }
- if repr.align.is_some() {
- struct_span_err!(
- tcx.sess,
- sp,
- E0587,
- "type has conflicting packed and align representation hints"
- )
- .emit();
- } else {
- if let Some(def_spans) = check_packed_inner(tcx, def.did(), &mut vec![]) {
- let mut err = struct_span_err!(
- tcx.sess,
- sp,
- E0588,
- "packed type cannot transitively contain a `#[repr(align)]` type"
- );
-
- err.span_note(
- tcx.def_span(def_spans[0].0),
- &format!(
- "`{}` has a `#[repr(align)]` attribute",
- tcx.item_name(def_spans[0].0)
- ),
- );
-
- if def_spans.len() > 2 {
- let mut first = true;
- for (adt_def, span) in def_spans.iter().skip(1).rev() {
- let ident = tcx.item_name(*adt_def);
- err.span_note(
- *span,
- &if first {
- format!(
- "`{}` contains a field of type `{}`",
- tcx.type_of(def.did()),
- ident
- )
- } else {
- format!("...which contains a field of type `{ident}`")
- },
- );
- first = false;
- }
- }
-
- err.emit();
- }
- }
- }
-}
-
-pub(super) fn check_packed_inner(
- tcx: TyCtxt<'_>,
- def_id: DefId,
- stack: &mut Vec<DefId>,
-) -> Option<Vec<(DefId, Span)>> {
- if let ty::Adt(def, substs) = tcx.type_of(def_id).kind() {
- if def.is_struct() || def.is_union() {
- if def.repr().align.is_some() {
- return Some(vec![(def.did(), DUMMY_SP)]);
- }
-
- stack.push(def_id);
- for field in &def.non_enum_variant().fields {
- if let ty::Adt(def, _) = field.ty(tcx, substs).kind()
- && !stack.contains(&def.did())
- && let Some(mut defs) = check_packed_inner(tcx, def.did(), stack)
- {
- defs.push((def.did(), field.ident(tcx).span));
- return Some(defs);
- }
- }
- stack.pop();
- }
- }
-
- None
-}
-
-pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtDef<'tcx>) {
- if !adt.repr().transparent() {
- return;
- }
-
- if adt.is_union() && !tcx.features().transparent_unions {
- feature_err(
- &tcx.sess.parse_sess,
- sym::transparent_unions,
- sp,
- "transparent unions are unstable",
- )
- .emit();
- }
-
- if adt.variants().len() != 1 {
- bad_variant_count(tcx, adt, sp, adt.did());
- if adt.variants().is_empty() {
- // Don't bother checking the fields. No variants (and thus no fields) exist.
- return;
- }
- }
-
- // For each field, figure out if it's known to be a ZST and align(1), with "known"
- // respecting #[non_exhaustive] attributes.
- let field_infos = adt.all_fields().map(|field| {
- let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did));
- let param_env = tcx.param_env(field.did);
- let layout = tcx.layout_of(param_env.and(ty));
- // We are currently checking the type this field came from, so it must be local
- let span = tcx.hir().span_if_local(field.did).unwrap();
- let zst = layout.map_or(false, |layout| layout.is_zst());
- let align1 = layout.map_or(false, |layout| layout.align.abi.bytes() == 1);
- if !zst {
- return (span, zst, align1, None);
- }
-
- fn check_non_exhaustive<'tcx>(
- tcx: TyCtxt<'tcx>,
- t: Ty<'tcx>,
- ) -> ControlFlow<(&'static str, DefId, SubstsRef<'tcx>, bool)> {
- match t.kind() {
- ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)),
- ty::Array(ty, _) => check_non_exhaustive(tcx, *ty),
- ty::Adt(def, subst) => {
- if !def.did().is_local() {
- let non_exhaustive = def.is_variant_list_non_exhaustive()
- || def
- .variants()
- .iter()
- .any(ty::VariantDef::is_field_list_non_exhaustive);
- let has_priv = def.all_fields().any(|f| !f.vis.is_public());
- if non_exhaustive || has_priv {
- return ControlFlow::Break((
- def.descr(),
- def.did(),
- subst,
- non_exhaustive,
- ));
- }
- }
- def.all_fields()
- .map(|field| field.ty(tcx, subst))
- .try_for_each(|t| check_non_exhaustive(tcx, t))
- }
- _ => ControlFlow::Continue(()),
- }
- }
-
- (span, zst, align1, check_non_exhaustive(tcx, ty).break_value())
- });
-
- let non_zst_fields = field_infos
- .clone()
- .filter_map(|(span, zst, _align1, _non_exhaustive)| if !zst { Some(span) } else { None });
- let non_zst_count = non_zst_fields.clone().count();
- if non_zst_count >= 2 {
- bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp);
- }
- let incompatible_zst_fields =
- field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count();
- let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2;
- for (span, zst, align1, non_exhaustive) in field_infos {
- if zst && !align1 {
- struct_span_err!(
- tcx.sess,
- span,
- E0691,
- "zero-sized field in transparent {} has alignment larger than 1",
- adt.descr(),
- )
- .span_label(span, "has alignment larger than 1")
- .emit();
- }
- if incompat && let Some((descr, def_id, substs, non_exhaustive)) = non_exhaustive {
- tcx.struct_span_lint_hir(
- REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
- tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
- span,
- |lint| {
- let note = if non_exhaustive {
- "is marked with `#[non_exhaustive]`"
- } else {
- "contains private fields"
- };
- let field_ty = tcx.def_path_str_with_substs(def_id, substs);
- lint.build("zero-sized fields in repr(transparent) cannot contain external non-exhaustive types")
- .note(format!("this {descr} contains `{field_ty}`, which {note}, \
- and makes it not a breaking change to become non-zero-sized in the future."))
- .emit();
- },
- )
- }
- }
-}
-
-#[allow(trivial_numeric_casts)]
-fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: LocalDefId) {
- let def = tcx.adt_def(def_id);
- let sp = tcx.def_span(def_id);
- def.destructor(tcx); // force the destructor to be evaluated
-
- if vs.is_empty() {
- if let Some(attr) = tcx.get_attr(def_id.to_def_id(), sym::repr) {
- struct_span_err!(
- tcx.sess,
- attr.span,
- E0084,
- "unsupported representation for zero-variant enum"
- )
- .span_label(sp, "zero-variant enum")
- .emit();
- }
- }
-
- let repr_type_ty = def.repr().discr_type().to_ty(tcx);
- if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 {
- if !tcx.features().repr128 {
- feature_err(
- &tcx.sess.parse_sess,
- sym::repr128,
- sp,
- "repr with 128-bit type is unstable",
- )
- .emit();
- }
- }
-
- for v in vs {
- if let Some(ref e) = v.disr_expr {
- tcx.ensure().typeck(tcx.hir().local_def_id(e.hir_id));
- }
- }
-
- if tcx.adt_def(def_id).repr().int.is_none() && tcx.features().arbitrary_enum_discriminant {
- let is_unit = |var: &hir::Variant<'_>| matches!(var.data, hir::VariantData::Unit(..));
-
- let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some();
- let has_non_units = vs.iter().any(|var| !is_unit(var));
- let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var));
- let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var));
-
- if disr_non_unit || (disr_units && has_non_units) {
- let mut err =
- struct_span_err!(tcx.sess, sp, E0732, "`#[repr(inttype)]` must be specified");
- err.emit();
- }
- }
-
- let mut disr_vals: Vec<Discr<'tcx>> = Vec::with_capacity(vs.len());
- // This tracks the previous variant span (in the loop) incase we need it for diagnostics
- let mut prev_variant_span: Span = DUMMY_SP;
- for ((_, discr), v) in iter::zip(def.discriminants(tcx), vs) {
- // Check for duplicate discriminant values
- if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) {
- let variant_did = def.variant(VariantIdx::new(i)).def_id;
- let variant_i_hir_id = tcx.hir().local_def_id_to_hir_id(variant_did.expect_local());
- let variant_i = tcx.hir().expect_variant(variant_i_hir_id);
- let i_span = match variant_i.disr_expr {
- Some(ref expr) => tcx.hir().span(expr.hir_id),
- None => tcx.def_span(variant_did),
- };
- let span = match v.disr_expr {
- Some(ref expr) => tcx.hir().span(expr.hir_id),
- None => v.span,
- };
- let display_discr = format_discriminant_overflow(tcx, v, discr);
- let display_discr_i = format_discriminant_overflow(tcx, variant_i, disr_vals[i]);
- let no_disr = v.disr_expr.is_none();
- let mut err = struct_span_err!(
- tcx.sess,
- sp,
- E0081,
- "discriminant value `{}` assigned more than once",
- discr,
- );
-
- err.span_label(i_span, format!("first assignment of {display_discr_i}"));
- err.span_label(span, format!("second assignment of {display_discr}"));
-
- if no_disr {
- err.span_label(
- prev_variant_span,
- format!(
- "assigned discriminant for `{}` was incremented from this discriminant",
- v.ident
- ),
- );
- }
- err.emit();
- }
-
- disr_vals.push(discr);
- prev_variant_span = v.span;
- }
-
- check_representable(tcx, sp, def_id);
- check_transparent(tcx, sp, def);
-}
-
-/// In the case that a discriminant is both a duplicate and an overflowing literal,
-/// we insert both the assigned discriminant and the literal it overflowed from into the formatted
-/// output. Otherwise we format the discriminant normally.
-fn format_discriminant_overflow<'tcx>(
- tcx: TyCtxt<'tcx>,
- variant: &hir::Variant<'_>,
- dis: Discr<'tcx>,
-) -> String {
- if let Some(expr) = &variant.disr_expr {
- let body = &tcx.hir().body(expr.body).value;
- if let hir::ExprKind::Lit(lit) = &body.kind
- && let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node
- && dis.val != *lit_value
- {
- return format!("`{dis}` (overflowed from `{lit_value}`)");
- }
- }
-
- format!("`{dis}`")
-}
-
-pub(super) fn check_type_params_are_used<'tcx>(
- tcx: TyCtxt<'tcx>,
- generics: &ty::Generics,
- ty: Ty<'tcx>,
-) {
- debug!("check_type_params_are_used(generics={:?}, ty={:?})", generics, ty);
-
- assert_eq!(generics.parent, None);
-
- if generics.own_counts().types == 0 {
- return;
- }
-
- let mut params_used = BitSet::new_empty(generics.params.len());
-
- if ty.references_error() {
- // If there is already another error, do not emit
- // an error for not using a type parameter.
- assert!(tcx.sess.has_errors().is_some());
- return;
- }
-
- for leaf in ty.walk() {
- if let GenericArgKind::Type(leaf_ty) = leaf.unpack()
- && let ty::Param(param) = leaf_ty.kind()
- {
- debug!("found use of ty param {:?}", param);
- params_used.insert(param.index);
- }
- }
-
- for param in &generics.params {
- if !params_used.contains(param.index)
- && let ty::GenericParamDefKind::Type { .. } = param.kind
- {
- let span = tcx.def_span(param.def_id);
- struct_span_err!(
- tcx.sess,
- span,
- E0091,
- "type parameter `{}` is unused",
- param.name,
- )
- .span_label(span, "unused type parameter")
- .emit();
- }
- }
-}
-
-pub(super) fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
- let module = tcx.hir_module_items(module_def_id);
- for id in module.items() {
- check_item_type(tcx, id);
- }
-}
-
-fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed {
- struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing")
- .span_label(span, "recursive `async fn`")
- .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
- .note(
- "consider using the `async_recursion` crate: https://crates.io/crates/async_recursion",
- )
- .emit()
-}
-
-/// Emit an error for recursive opaque types.
-///
-/// If this is a return `impl Trait`, find the item's return expressions and point at them. For
-/// direct recursion this is enough, but for indirect recursion also point at the last intermediary
-/// `impl Trait`.
-///
-/// If all the return expressions evaluate to `!`, then we explain that the error will go away
-/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder.
-fn opaque_type_cycle_error(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) -> ErrorGuaranteed {
- let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type");
-
- let mut label = false;
- if let Some((def_id, visitor)) = get_owner_return_paths(tcx, def_id) {
- let typeck_results = tcx.typeck(def_id);
- if visitor
- .returns
- .iter()
- .filter_map(|expr| typeck_results.node_type_opt(expr.hir_id))
- .all(|ty| matches!(ty.kind(), ty::Never))
- {
- let spans = visitor
- .returns
- .iter()
- .filter(|expr| typeck_results.node_type_opt(expr.hir_id).is_some())
- .map(|expr| expr.span)
- .collect::<Vec<Span>>();
- let span_len = spans.len();
- if span_len == 1 {
- err.span_label(spans[0], "this returned value is of `!` type");
- } else {
- let mut multispan: MultiSpan = spans.clone().into();
- for span in spans {
- multispan.push_span_label(span, "this returned value is of `!` type");
- }
- err.span_note(multispan, "these returned values have a concrete \"never\" type");
- }
- err.help("this error will resolve once the item's body returns a concrete type");
- } else {
- let mut seen = FxHashSet::default();
- seen.insert(span);
- err.span_label(span, "recursive opaque type");
- label = true;
- for (sp, ty) in visitor
- .returns
- .iter()
- .filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t)))
- .filter(|(_, ty)| !matches!(ty.kind(), ty::Never))
- {
- struct OpaqueTypeCollector(Vec<DefId>);
- impl<'tcx> ty::visit::TypeVisitor<'tcx> for OpaqueTypeCollector {
- fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
- match *t.kind() {
- ty::Opaque(def, _) => {
- self.0.push(def);
- ControlFlow::CONTINUE
- }
- _ => t.super_visit_with(self),
- }
- }
- }
- let mut visitor = OpaqueTypeCollector(vec![]);
- ty.visit_with(&mut visitor);
- for def_id in visitor.0 {
- let ty_span = tcx.def_span(def_id);
- if !seen.contains(&ty_span) {
- err.span_label(ty_span, &format!("returning this opaque type `{ty}`"));
- seen.insert(ty_span);
- }
- err.span_label(sp, &format!("returning here with type `{ty}`"));
- }
- }
- }
- }
- if !label {
- err.span_label(span, "cannot resolve opaque type");
- }
- err.emit()
-}
diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs
deleted file mode 100644
index fee872155..000000000
--- a/compiler/rustc_typeck/src/check/closure.rs
+++ /dev/null
@@ -1,805 +0,0 @@
-//! Code for type-checking closure expressions.
-
-use super::{check_fn, Expectation, FnCtxt, GeneratorTypes};
-
-use crate::astconv::AstConv;
-use crate::rustc_middle::ty::subst::Subst;
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_hir::lang_items::LangItem;
-use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_infer::infer::LateBoundRegionConversionTime;
-use rustc_infer::infer::{InferOk, InferResult};
-use rustc_middle::ty::subst::InternalSubsts;
-use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{self, Ty};
-use rustc_span::source_map::Span;
-use rustc_target::spec::abi::Abi;
-use rustc_trait_selection::traits::error_reporting::ArgKind;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
-use std::cmp;
-use std::iter;
-
-/// What signature do we *expect* the closure to have from context?
-#[derive(Debug)]
-struct ExpectedSig<'tcx> {
- /// Span that gave us this expectation, if we know that.
- cause_span: Option<Span>,
- sig: ty::PolyFnSig<'tcx>,
-}
-
-struct ClosureSignatures<'tcx> {
- bound_sig: ty::PolyFnSig<'tcx>,
- liberated_sig: ty::FnSig<'tcx>,
-}
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- #[instrument(skip(self, expr, _capture, decl, body_id), level = "debug")]
- pub fn check_expr_closure(
- &self,
- expr: &hir::Expr<'_>,
- _capture: hir::CaptureBy,
- decl: &'tcx hir::FnDecl<'tcx>,
- body_id: hir::BodyId,
- gen: Option<hir::Movability>,
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- trace!("decl = {:#?}", decl);
- trace!("expr = {:#?}", expr);
-
- // It's always helpful for inference if we know the kind of
- // closure sooner rather than later, so first examine the expected
- // type, and see if can glean a closure kind from there.
- let (expected_sig, expected_kind) = match expected.to_option(self) {
- Some(ty) => self.deduce_expectations_from_expected_type(ty),
- None => (None, None),
- };
- let body = self.tcx.hir().body(body_id);
- self.check_closure(expr, expected_kind, decl, body, gen, expected_sig)
- }
-
- #[instrument(skip(self, expr, body, decl), level = "debug")]
- fn check_closure(
- &self,
- expr: &hir::Expr<'_>,
- opt_kind: Option<ty::ClosureKind>,
- decl: &'tcx hir::FnDecl<'tcx>,
- body: &'tcx hir::Body<'tcx>,
- gen: Option<hir::Movability>,
- expected_sig: Option<ExpectedSig<'tcx>>,
- ) -> Ty<'tcx> {
- trace!("decl = {:#?}", decl);
- let expr_def_id = self.tcx.hir().local_def_id(expr.hir_id);
- debug!(?expr_def_id);
-
- let ClosureSignatures { bound_sig, liberated_sig } =
- self.sig_of_closure(expr.hir_id, expr_def_id.to_def_id(), decl, body, expected_sig);
-
- debug!(?bound_sig, ?liberated_sig);
-
- let return_type_pre_known = !liberated_sig.output().is_ty_infer();
-
- let generator_types = check_fn(
- self,
- self.param_env.without_const(),
- liberated_sig,
- decl,
- expr.hir_id,
- body,
- gen,
- return_type_pre_known,
- )
- .1;
-
- let parent_substs = InternalSubsts::identity_for_item(
- self.tcx,
- self.tcx.typeck_root_def_id(expr_def_id.to_def_id()),
- );
-
- let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::ClosureSynthetic,
- span: self.tcx.hir().span(expr.hir_id),
- });
-
- if let Some(GeneratorTypes { resume_ty, yield_ty, interior, movability }) = generator_types
- {
- let generator_substs = ty::GeneratorSubsts::new(
- self.tcx,
- ty::GeneratorSubstsParts {
- parent_substs,
- resume_ty,
- yield_ty,
- return_ty: liberated_sig.output(),
- witness: interior,
- tupled_upvars_ty,
- },
- );
-
- return self.tcx.mk_generator(
- expr_def_id.to_def_id(),
- generator_substs.substs,
- movability,
- );
- }
-
- // Tuple up the arguments and insert the resulting function type into
- // the `closures` table.
- let sig = bound_sig.map_bound(|sig| {
- self.tcx.mk_fn_sig(
- iter::once(self.tcx.intern_tup(sig.inputs())),
- sig.output(),
- sig.c_variadic,
- sig.unsafety,
- sig.abi,
- )
- });
-
- debug!(?sig, ?opt_kind);
-
- let closure_kind_ty = match opt_kind {
- Some(kind) => kind.to_ty(self.tcx),
-
- // Create a type variable (for now) to represent the closure kind.
- // It will be unified during the upvar inference phase (`upvar.rs`)
- None => self.next_ty_var(TypeVariableOrigin {
- // FIXME(eddyb) distinguish closure kind inference variables from the rest.
- kind: TypeVariableOriginKind::ClosureSynthetic,
- span: expr.span,
- }),
- };
-
- let closure_substs = ty::ClosureSubsts::new(
- self.tcx,
- ty::ClosureSubstsParts {
- parent_substs,
- closure_kind_ty,
- closure_sig_as_fn_ptr_ty: self.tcx.mk_fn_ptr(sig),
- tupled_upvars_ty,
- },
- );
-
- let closure_type = self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs);
-
- debug!(?expr.hir_id, ?closure_type);
-
- closure_type
- }
-
- /// Given the expected type, figures out what it can about this closure we
- /// are about to type check:
- #[instrument(skip(self), level = "debug")]
- fn deduce_expectations_from_expected_type(
- &self,
- expected_ty: Ty<'tcx>,
- ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
- match *expected_ty.kind() {
- ty::Opaque(def_id, substs) => {
- let bounds = self.tcx.bound_explicit_item_bounds(def_id);
- let sig = bounds
- .transpose_iter()
- .map(|e| e.map_bound(|e| *e).transpose_tuple2())
- .find_map(|(pred, span)| match pred.0.kind().skip_binder() {
- ty::PredicateKind::Projection(proj_predicate) => self
- .deduce_sig_from_projection(
- Some(span.0),
- pred.0
- .kind()
- .rebind(pred.rebind(proj_predicate).subst(self.tcx, substs)),
- ),
- _ => None,
- });
-
- let kind = bounds
- .transpose_iter()
- .map(|e| e.map_bound(|e| *e).transpose_tuple2())
- .filter_map(|(pred, _)| match pred.0.kind().skip_binder() {
- ty::PredicateKind::Trait(tp) => {
- self.tcx.fn_trait_kind_from_lang_item(tp.def_id())
- }
- _ => None,
- })
- .fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur))));
- trace!(?sig, ?kind);
- (sig, kind)
- }
- ty::Dynamic(ref object_type, ..) => {
- let sig = object_type.projection_bounds().find_map(|pb| {
- let pb = pb.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self);
- self.deduce_sig_from_projection(None, pb)
- });
- let kind = object_type
- .principal_def_id()
- .and_then(|did| self.tcx.fn_trait_kind_from_lang_item(did));
- (sig, kind)
- }
- ty::Infer(ty::TyVar(vid)) => self.deduce_expectations_from_obligations(vid),
- ty::FnPtr(sig) => {
- let expected_sig = ExpectedSig { cause_span: None, sig };
- (Some(expected_sig), Some(ty::ClosureKind::Fn))
- }
- _ => (None, None),
- }
- }
-
- fn deduce_expectations_from_obligations(
- &self,
- expected_vid: ty::TyVid,
- ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
- let expected_sig =
- self.obligations_for_self_ty(expected_vid).find_map(|(_, obligation)| {
- debug!(?obligation.predicate);
-
- let bound_predicate = obligation.predicate.kind();
- if let ty::PredicateKind::Projection(proj_predicate) =
- obligation.predicate.kind().skip_binder()
- {
- // Given a Projection predicate, we can potentially infer
- // the complete signature.
- self.deduce_sig_from_projection(
- Some(obligation.cause.span),
- bound_predicate.rebind(proj_predicate),
- )
- } else {
- None
- }
- });
-
- // Even if we can't infer the full signature, we may be able to
- // infer the kind. This can occur when we elaborate a predicate
- // like `F : Fn<A>`. Note that due to subtyping we could encounter
- // many viable options, so pick the most restrictive.
- let expected_kind = self
- .obligations_for_self_ty(expected_vid)
- .filter_map(|(tr, _)| self.tcx.fn_trait_kind_from_lang_item(tr.def_id()))
- .fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur))));
-
- (expected_sig, expected_kind)
- }
-
- /// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce
- /// everything we need to know about a closure or generator.
- ///
- /// The `cause_span` should be the span that caused us to
- /// have this expected signature, or `None` if we can't readily
- /// know that.
- #[instrument(level = "debug", skip(self, cause_span))]
- fn deduce_sig_from_projection(
- &self,
- cause_span: Option<Span>,
- projection: ty::PolyProjectionPredicate<'tcx>,
- ) -> Option<ExpectedSig<'tcx>> {
- let tcx = self.tcx;
-
- let trait_def_id = projection.trait_def_id(tcx);
-
- let is_fn = tcx.fn_trait_kind_from_lang_item(trait_def_id).is_some();
- let gen_trait = tcx.require_lang_item(LangItem::Generator, cause_span);
- let is_gen = gen_trait == trait_def_id;
- if !is_fn && !is_gen {
- debug!("not fn or generator");
- return None;
- }
-
- if is_gen {
- // Check that we deduce the signature from the `<_ as std::ops::Generator>::Return`
- // associated item and not yield.
- let return_assoc_item = self.tcx.associated_item_def_ids(gen_trait)[1];
- if return_assoc_item != projection.projection_def_id() {
- debug!("not return assoc item of generator");
- return None;
- }
- }
-
- let input_tys = if is_fn {
- let arg_param_ty = projection.skip_binder().projection_ty.substs.type_at(1);
- let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty);
- debug!(?arg_param_ty);
-
- match arg_param_ty.kind() {
- &ty::Tuple(tys) => tys,
- _ => return None,
- }
- } else {
- // Generators with a `()` resume type may be defined with 0 or 1 explicit arguments,
- // else they must have exactly 1 argument. For now though, just give up in this case.
- return None;
- };
-
- // Since this is a return parameter type it is safe to unwrap.
- let ret_param_ty = projection.skip_binder().term.ty().unwrap();
- let ret_param_ty = self.resolve_vars_if_possible(ret_param_ty);
- debug!(?ret_param_ty);
-
- let sig = projection.rebind(self.tcx.mk_fn_sig(
- input_tys.iter(),
- ret_param_ty,
- false,
- hir::Unsafety::Normal,
- Abi::Rust,
- ));
- debug!(?sig);
-
- Some(ExpectedSig { cause_span, sig })
- }
-
- fn sig_of_closure(
- &self,
- hir_id: hir::HirId,
- expr_def_id: DefId,
- decl: &hir::FnDecl<'_>,
- body: &hir::Body<'_>,
- expected_sig: Option<ExpectedSig<'tcx>>,
- ) -> ClosureSignatures<'tcx> {
- if let Some(e) = expected_sig {
- self.sig_of_closure_with_expectation(hir_id, expr_def_id, decl, body, e)
- } else {
- self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body)
- }
- }
-
- /// If there is no expected signature, then we will convert the
- /// types that the user gave into a signature.
- #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")]
- fn sig_of_closure_no_expectation(
- &self,
- hir_id: hir::HirId,
- expr_def_id: DefId,
- decl: &hir::FnDecl<'_>,
- body: &hir::Body<'_>,
- ) -> ClosureSignatures<'tcx> {
- let bound_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body);
-
- self.closure_sigs(expr_def_id, body, bound_sig)
- }
-
- /// Invoked to compute the signature of a closure expression. This
- /// combines any user-provided type annotations (e.g., `|x: u32|
- /// -> u32 { .. }`) with the expected signature.
- ///
- /// The approach is as follows:
- ///
- /// - Let `S` be the (higher-ranked) signature that we derive from the user's annotations.
- /// - Let `E` be the (higher-ranked) signature that we derive from the expectations, if any.
- /// - If we have no expectation `E`, then the signature of the closure is `S`.
- /// - Otherwise, the signature of the closure is E. Moreover:
- /// - Skolemize the late-bound regions in `E`, yielding `E'`.
- /// - Instantiate all the late-bound regions bound in the closure within `S`
- /// with fresh (existential) variables, yielding `S'`
- /// - Require that `E' = S'`
- /// - We could use some kind of subtyping relationship here,
- /// I imagine, but equality is easier and works fine for
- /// our purposes.
- ///
- /// The key intuition here is that the user's types must be valid
- /// from "the inside" of the closure, but the expectation
- /// ultimately drives the overall signature.
- ///
- /// # Examples
- ///
- /// ```ignore (illustrative)
- /// fn with_closure<F>(_: F)
- /// where F: Fn(&u32) -> &u32 { .. }
- ///
- /// with_closure(|x: &u32| { ... })
- /// ```
- ///
- /// Here:
- /// - E would be `fn(&u32) -> &u32`.
- /// - S would be `fn(&u32) ->
- /// - E' is `&'!0 u32 -> &'!0 u32`
- /// - S' is `&'?0 u32 -> ?T`
- ///
- /// S' can be unified with E' with `['?0 = '!0, ?T = &'!10 u32]`.
- ///
- /// # Arguments
- ///
- /// - `expr_def_id`: the `DefId` of the closure expression
- /// - `decl`: the HIR declaration of the closure
- /// - `body`: the body of the closure
- /// - `expected_sig`: the expected signature (if any). Note that
- /// this is missing a binder: that is, there may be late-bound
- /// regions with depth 1, which are bound then by the closure.
- #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")]
- fn sig_of_closure_with_expectation(
- &self,
- hir_id: hir::HirId,
- expr_def_id: DefId,
- decl: &hir::FnDecl<'_>,
- body: &hir::Body<'_>,
- expected_sig: ExpectedSig<'tcx>,
- ) -> ClosureSignatures<'tcx> {
- // Watch out for some surprises and just ignore the
- // expectation if things don't see to match up with what we
- // expect.
- if expected_sig.sig.c_variadic() != decl.c_variadic {
- return self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body);
- } else if expected_sig.sig.skip_binder().inputs_and_output.len() != decl.inputs.len() + 1 {
- return self.sig_of_closure_with_mismatched_number_of_arguments(
- expr_def_id,
- decl,
- body,
- expected_sig,
- );
- }
-
- // Create a `PolyFnSig`. Note the oddity that late bound
- // regions appearing free in `expected_sig` are now bound up
- // in this binder we are creating.
- assert!(!expected_sig.sig.skip_binder().has_vars_bound_above(ty::INNERMOST));
- let bound_sig = expected_sig.sig.map_bound(|sig| {
- self.tcx.mk_fn_sig(
- sig.inputs().iter().cloned(),
- sig.output(),
- sig.c_variadic,
- hir::Unsafety::Normal,
- Abi::RustCall,
- )
- });
-
- // `deduce_expectations_from_expected_type` introduces
- // late-bound lifetimes defined elsewhere, which we now
- // anonymize away, so as not to confuse the user.
- let bound_sig = self.tcx.anonymize_late_bound_regions(bound_sig);
-
- let closure_sigs = self.closure_sigs(expr_def_id, body, bound_sig);
-
- // Up till this point, we have ignored the annotations that the user
- // gave. This function will check that they unify successfully.
- // Along the way, it also writes out entries for types that the user
- // wrote into our typeck results, which are then later used by the privacy
- // check.
- match self.check_supplied_sig_against_expectation(
- hir_id,
- expr_def_id,
- decl,
- body,
- &closure_sigs,
- ) {
- Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok),
- Err(_) => return self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body),
- }
-
- closure_sigs
- }
-
- fn sig_of_closure_with_mismatched_number_of_arguments(
- &self,
- expr_def_id: DefId,
- decl: &hir::FnDecl<'_>,
- body: &hir::Body<'_>,
- expected_sig: ExpectedSig<'tcx>,
- ) -> ClosureSignatures<'tcx> {
- let hir = self.tcx.hir();
- let expr_map_node = hir.get_if_local(expr_def_id).unwrap();
- let expected_args: Vec<_> = expected_sig
- .sig
- .skip_binder()
- .inputs()
- .iter()
- .map(|ty| ArgKind::from_expected_ty(*ty, None))
- .collect();
- let (closure_span, found_args) = match self.get_fn_like_arguments(expr_map_node) {
- Some((sp, args)) => (Some(sp), args),
- None => (None, Vec::new()),
- };
- let expected_span =
- expected_sig.cause_span.unwrap_or_else(|| hir.span_if_local(expr_def_id).unwrap());
- self.report_arg_count_mismatch(
- expected_span,
- closure_span,
- expected_args,
- found_args,
- true,
- )
- .emit();
-
- let error_sig = self.error_sig_of_closure(decl);
-
- self.closure_sigs(expr_def_id, body, error_sig)
- }
-
- /// Enforce the user's types against the expectation. See
- /// `sig_of_closure_with_expectation` for details on the overall
- /// strategy.
- fn check_supplied_sig_against_expectation(
- &self,
- hir_id: hir::HirId,
- expr_def_id: DefId,
- decl: &hir::FnDecl<'_>,
- body: &hir::Body<'_>,
- expected_sigs: &ClosureSignatures<'tcx>,
- ) -> InferResult<'tcx, ()> {
- // Get the signature S that the user gave.
- //
- // (See comment on `sig_of_closure_with_expectation` for the
- // meaning of these letters.)
- let supplied_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body);
-
- debug!("check_supplied_sig_against_expectation: supplied_sig={:?}", supplied_sig);
-
- // FIXME(#45727): As discussed in [this comment][c1], naively
- // forcing equality here actually results in suboptimal error
- // messages in some cases. For now, if there would have been
- // an obvious error, we fallback to declaring the type of the
- // closure to be the one the user gave, which allows other
- // error message code to trigger.
- //
- // However, I think [there is potential to do even better
- // here][c2], since in *this* code we have the precise span of
- // the type parameter in question in hand when we report the
- // error.
- //
- // [c1]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341089706
- // [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796
- self.commit_if_ok(|_| {
- let mut all_obligations = vec![];
-
- // The liberated version of this signature should be a subtype
- // of the liberated form of the expectation.
- for ((hir_ty, &supplied_ty), expected_ty) in iter::zip(
- iter::zip(
- decl.inputs,
- supplied_sig.inputs().skip_binder(), // binder moved to (*) below
- ),
- expected_sigs.liberated_sig.inputs(), // `liberated_sig` is E'.
- ) {
- // Instantiate (this part of..) S to S', i.e., with fresh variables.
- let supplied_ty = self.replace_bound_vars_with_fresh_vars(
- hir_ty.span,
- LateBoundRegionConversionTime::FnCall,
- supplied_sig.inputs().rebind(supplied_ty),
- ); // recreated from (*) above
-
- // Check that E' = S'.
- let cause = self.misc(hir_ty.span);
- let InferOk { value: (), obligations } =
- self.at(&cause, self.param_env).eq(*expected_ty, supplied_ty)?;
- all_obligations.extend(obligations);
- }
-
- let supplied_output_ty = self.replace_bound_vars_with_fresh_vars(
- decl.output.span(),
- LateBoundRegionConversionTime::FnCall,
- supplied_sig.output(),
- );
- let cause = &self.misc(decl.output.span());
- let InferOk { value: (), obligations } = self
- .at(cause, self.param_env)
- .eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?;
- all_obligations.extend(obligations);
-
- Ok(InferOk { value: (), obligations: all_obligations })
- })
- }
-
- /// If there is no expected signature, then we will convert the
- /// types that the user gave into a signature.
- ///
- /// Also, record this closure signature for later.
- #[instrument(skip(self, decl, body), level = "debug")]
- fn supplied_sig_of_closure(
- &self,
- hir_id: hir::HirId,
- expr_def_id: DefId,
- decl: &hir::FnDecl<'_>,
- body: &hir::Body<'_>,
- ) -> ty::PolyFnSig<'tcx> {
- let astconv: &dyn AstConv<'_> = self;
-
- trace!("decl = {:#?}", decl);
- debug!(?body.generator_kind);
-
- let bound_vars = self.tcx.late_bound_vars(hir_id);
-
- // First, convert the types that the user supplied (if any).
- let supplied_arguments = decl.inputs.iter().map(|a| astconv.ast_ty_to_ty(a));
- let supplied_return = match decl.output {
- hir::FnRetTy::Return(ref output) => astconv.ast_ty_to_ty(&output),
- hir::FnRetTy::DefaultReturn(_) => match body.generator_kind {
- // In the case of the async block that we create for a function body,
- // we expect the return type of the block to match that of the enclosing
- // function.
- Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn)) => {
- debug!("closure is async fn body");
- self.deduce_future_output_from_obligations(expr_def_id, body.id().hir_id)
- .unwrap_or_else(|| {
- // AFAIK, deducing the future output
- // always succeeds *except* in error cases
- // like #65159. I'd like to return Error
- // here, but I can't because I can't
- // easily (and locally) prove that we
- // *have* reported an
- // error. --nikomatsakis
- astconv.ty_infer(None, decl.output.span())
- })
- }
-
- _ => astconv.ty_infer(None, decl.output.span()),
- },
- };
-
- let result = ty::Binder::bind_with_vars(
- self.tcx.mk_fn_sig(
- supplied_arguments,
- supplied_return,
- decl.c_variadic,
- hir::Unsafety::Normal,
- Abi::RustCall,
- ),
- bound_vars,
- );
-
- debug!(?result);
-
- let c_result = self.inh.infcx.canonicalize_response(result);
- self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result);
-
- result
- }
-
- /// Invoked when we are translating the generator that results
- /// from desugaring an `async fn`. Returns the "sugared" return
- /// type of the `async fn` -- that is, the return type that the
- /// user specified. The "desugared" return type is an `impl
- /// Future<Output = T>`, so we do this by searching through the
- /// obligations to extract the `T`.
- #[instrument(skip(self), level = "debug")]
- fn deduce_future_output_from_obligations(
- &self,
- expr_def_id: DefId,
- body_id: hir::HirId,
- ) -> Option<Ty<'tcx>> {
- let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| {
- span_bug!(self.tcx.def_span(expr_def_id), "async fn generator outside of a fn")
- });
-
- let ret_ty = ret_coercion.borrow().expected_ty();
- let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
-
- let get_future_output = |predicate: ty::Predicate<'tcx>, span| {
- // Search for a pending obligation like
- //
- // `<R as Future>::Output = T`
- //
- // where R is the return type we are expecting. This type `T`
- // will be our output.
- let bound_predicate = predicate.kind();
- if let ty::PredicateKind::Projection(proj_predicate) = bound_predicate.skip_binder() {
- self.deduce_future_output_from_projection(
- span,
- bound_predicate.rebind(proj_predicate),
- )
- } else {
- None
- }
- };
-
- let output_ty = match *ret_ty.kind() {
- ty::Infer(ty::TyVar(ret_vid)) => {
- self.obligations_for_self_ty(ret_vid).find_map(|(_, obligation)| {
- get_future_output(obligation.predicate, obligation.cause.span)
- })?
- }
- ty::Opaque(def_id, substs) => self
- .tcx
- .bound_explicit_item_bounds(def_id)
- .transpose_iter()
- .map(|e| e.map_bound(|e| *e).transpose_tuple2())
- .find_map(|(p, s)| get_future_output(p.subst(self.tcx, substs), s.0))?,
- ty::Error(_) => return None,
- _ => span_bug!(
- self.tcx.def_span(expr_def_id),
- "async fn generator return type not an inference variable"
- ),
- };
-
- // async fn that have opaque types in their return type need to redo the conversion to inference variables
- // as they fetch the still opaque version from the signature.
- let InferOk { value: output_ty, obligations } = self
- .replace_opaque_types_with_inference_vars(
- output_ty,
- body_id,
- self.tcx.def_span(expr_def_id),
- self.param_env,
- );
- self.register_predicates(obligations);
-
- debug!("deduce_future_output_from_obligations: output_ty={:?}", output_ty);
- Some(output_ty)
- }
-
- /// Given a projection like
- ///
- /// `<X as Future>::Output = T`
- ///
- /// where `X` is some type that has no late-bound regions, returns
- /// `Some(T)`. If the projection is for some other trait, returns
- /// `None`.
- fn deduce_future_output_from_projection(
- &self,
- cause_span: Span,
- predicate: ty::PolyProjectionPredicate<'tcx>,
- ) -> Option<Ty<'tcx>> {
- debug!("deduce_future_output_from_projection(predicate={:?})", predicate);
-
- // We do not expect any bound regions in our predicate, so
- // skip past the bound vars.
- let Some(predicate) = predicate.no_bound_vars() else {
- debug!("deduce_future_output_from_projection: has late-bound regions");
- return None;
- };
-
- // Check that this is a projection from the `Future` trait.
- let trait_def_id = predicate.projection_ty.trait_def_id(self.tcx);
- let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(cause_span));
- if trait_def_id != future_trait {
- debug!("deduce_future_output_from_projection: not a future");
- return None;
- }
-
- // The `Future` trait has only one associated item, `Output`,
- // so check that this is what we see.
- let output_assoc_item = self.tcx.associated_item_def_ids(future_trait)[0];
- if output_assoc_item != predicate.projection_ty.item_def_id {
- span_bug!(
- cause_span,
- "projecting associated item `{:?}` from future, which is not Output `{:?}`",
- predicate.projection_ty.item_def_id,
- output_assoc_item,
- );
- }
-
- // Extract the type from the projection. Note that there can
- // be no bound variables in this type because the "self type"
- // does not have any regions in it.
- let output_ty = self.resolve_vars_if_possible(predicate.term);
- debug!("deduce_future_output_from_projection: output_ty={:?}", output_ty);
- // This is a projection on a Fn trait so will always be a type.
- Some(output_ty.ty().unwrap())
- }
-
- /// Converts the types that the user supplied, in case that doing
- /// so should yield an error, but returns back a signature where
- /// all parameters are of type `TyErr`.
- fn error_sig_of_closure(&self, decl: &hir::FnDecl<'_>) -> ty::PolyFnSig<'tcx> {
- let astconv: &dyn AstConv<'_> = self;
-
- let supplied_arguments = decl.inputs.iter().map(|a| {
- // Convert the types that the user supplied (if any), but ignore them.
- astconv.ast_ty_to_ty(a);
- self.tcx.ty_error()
- });
-
- if let hir::FnRetTy::Return(ref output) = decl.output {
- astconv.ast_ty_to_ty(&output);
- }
-
- let result = ty::Binder::dummy(self.tcx.mk_fn_sig(
- supplied_arguments,
- self.tcx.ty_error(),
- decl.c_variadic,
- hir::Unsafety::Normal,
- Abi::RustCall,
- ));
-
- debug!("supplied_sig_of_closure: result={:?}", result);
-
- result
- }
-
- fn closure_sigs(
- &self,
- expr_def_id: DefId,
- body: &hir::Body<'_>,
- bound_sig: ty::PolyFnSig<'tcx>,
- ) -> ClosureSignatures<'tcx> {
- let liberated_sig = self.tcx().liberate_late_bound_regions(expr_def_id, bound_sig);
- let liberated_sig = self.inh.normalize_associated_types_in(
- body.value.span,
- body.value.hir_id,
- self.param_env,
- liberated_sig,
- );
- ClosureSignatures { bound_sig, liberated_sig }
- }
-}
diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs
deleted file mode 100644
index 666498403..000000000
--- a/compiler/rustc_typeck/src/check/compare_method.rs
+++ /dev/null
@@ -1,1547 +0,0 @@
-use super::potentially_plural_count;
-use crate::check::regionck::OutlivesEnvironmentExt;
-use crate::check::wfcheck;
-use crate::errors::LifetimesOrBoundsMismatchOnTrait;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed};
-use rustc_hir as hir;
-use rustc_hir::def::{DefKind, Res};
-use rustc_hir::intravisit;
-use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind};
-use rustc_infer::infer::outlives::env::OutlivesEnvironment;
-use rustc_infer::infer::{self, TyCtxtInferExt};
-use rustc_infer::traits::util;
-use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use rustc_middle::ty::subst::{InternalSubsts, Subst};
-use rustc_middle::ty::util::ExplicitSelf;
-use rustc_middle::ty::{self, DefIdTree};
-use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt};
-use rustc_span::Span;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
-use rustc_trait_selection::traits::{
- self, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal,
-};
-use std::iter;
-
-/// Checks that a method from an impl conforms to the signature of
-/// the same method as declared in the trait.
-///
-/// # Parameters
-///
-/// - `impl_m`: type of the method we are checking
-/// - `impl_m_span`: span to use for reporting errors
-/// - `trait_m`: the method in the trait
-/// - `impl_trait_ref`: the TraitRef corresponding to the trait implementation
-pub(crate) fn compare_impl_method<'tcx>(
- tcx: TyCtxt<'tcx>,
- impl_m: &ty::AssocItem,
- trait_m: &ty::AssocItem,
- impl_trait_ref: ty::TraitRef<'tcx>,
- trait_item_span: Option<Span>,
-) {
- debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref);
-
- let impl_m_span = tcx.def_span(impl_m.def_id);
-
- if let Err(_) = compare_self_type(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref) {
- return;
- }
-
- if let Err(_) = compare_number_of_generics(tcx, impl_m, impl_m_span, trait_m, trait_item_span) {
- return;
- }
-
- if let Err(_) = compare_generic_param_kinds(tcx, impl_m, trait_m) {
- return;
- }
-
- if let Err(_) =
- compare_number_of_method_arguments(tcx, impl_m, impl_m_span, trait_m, trait_item_span)
- {
- return;
- }
-
- if let Err(_) = compare_synthetic_generics(tcx, impl_m, trait_m) {
- return;
- }
-
- if let Err(_) = compare_predicate_entailment(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref)
- {
- return;
- }
-}
-
-fn compare_predicate_entailment<'tcx>(
- tcx: TyCtxt<'tcx>,
- impl_m: &ty::AssocItem,
- impl_m_span: Span,
- trait_m: &ty::AssocItem,
- impl_trait_ref: ty::TraitRef<'tcx>,
-) -> Result<(), ErrorGuaranteed> {
- let trait_to_impl_substs = impl_trait_ref.substs;
-
- // This node-id should be used for the `body_id` field on each
- // `ObligationCause` (and the `FnCtxt`).
- //
- // FIXME(@lcnr): remove that after removing `cause.body_id` from
- // obligations.
- let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local());
- // We sometimes modify the span further down.
- let mut cause = ObligationCause::new(
- impl_m_span,
- impl_m_hir_id,
- ObligationCauseCode::CompareImplItemObligation {
- impl_item_def_id: impl_m.def_id.expect_local(),
- trait_item_def_id: trait_m.def_id,
- kind: impl_m.kind,
- },
- );
-
- // This code is best explained by example. Consider a trait:
- //
- // trait Trait<'t, T> {
- // fn method<'a, M>(t: &'t T, m: &'a M) -> Self;
- // }
- //
- // And an impl:
- //
- // impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
- // fn method<'b, N>(t: &'j &'i U, m: &'b N) -> Foo;
- // }
- //
- // We wish to decide if those two method types are compatible.
- //
- // We start out with trait_to_impl_substs, that maps the trait
- // type parameters to impl type parameters. This is taken from the
- // impl trait reference:
- //
- // trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
- //
- // We create a mapping `dummy_substs` that maps from the impl type
- // parameters to fresh types and regions. For type parameters,
- // this is the identity transform, but we could as well use any
- // placeholder types. For regions, we convert from bound to free
- // regions (Note: but only early-bound regions, i.e., those
- // declared on the impl or used in type parameter bounds).
- //
- // impl_to_placeholder_substs = {'i => 'i0, U => U0, N => N0 }
- //
- // Now we can apply placeholder_substs to the type of the impl method
- // to yield a new function type in terms of our fresh, placeholder
- // types:
- //
- // <'b> fn(t: &'i0 U0, m: &'b) -> Foo
- //
- // We now want to extract and substitute the type of the *trait*
- // method and compare it. To do so, we must create a compound
- // substitution by combining trait_to_impl_substs and
- // impl_to_placeholder_substs, and also adding a mapping for the method
- // type parameters. We extend the mapping to also include
- // the method parameters.
- //
- // trait_to_placeholder_substs = { T => &'i0 U0, Self => Foo, M => N0 }
- //
- // Applying this to the trait method type yields:
- //
- // <'a> fn(t: &'i0 U0, m: &'a) -> Foo
- //
- // This type is also the same but the name of the bound region ('a
- // vs 'b). However, the normal subtyping rules on fn types handle
- // this kind of equivalency just fine.
- //
- // We now use these substitutions to ensure that all declared bounds are
- // satisfied by the implementation's method.
- //
- // We do this by creating a parameter environment which contains a
- // substitution corresponding to impl_to_placeholder_substs. We then build
- // trait_to_placeholder_substs and use it to convert the predicates contained
- // in the trait_m.generics to the placeholder form.
- //
- // Finally we register each of these predicates as an obligation in
- // a fresh FulfillmentCtxt, and invoke select_all_or_error.
-
- // Create mapping from impl to placeholder.
- let impl_to_placeholder_substs = InternalSubsts::identity_for_item(tcx, impl_m.def_id);
-
- // Create mapping from trait to placeholder.
- let trait_to_placeholder_substs =
- impl_to_placeholder_substs.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_substs);
- debug!("compare_impl_method: trait_to_placeholder_substs={:?}", trait_to_placeholder_substs);
-
- let impl_m_generics = tcx.generics_of(impl_m.def_id);
- let trait_m_generics = tcx.generics_of(trait_m.def_id);
- let impl_m_predicates = tcx.predicates_of(impl_m.def_id);
- let trait_m_predicates = tcx.predicates_of(trait_m.def_id);
-
- // Check region bounds.
- check_region_bounds_on_impl_item(tcx, impl_m, trait_m, &trait_m_generics, &impl_m_generics)?;
-
- // Create obligations for each predicate declared by the impl
- // definition in the context of the trait's parameter
- // environment. We can't just use `impl_env.caller_bounds`,
- // however, because we want to replace all late-bound regions with
- // region variables.
- let impl_predicates = tcx.predicates_of(impl_m_predicates.parent.unwrap());
- let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
-
- debug!("compare_impl_method: impl_bounds={:?}", hybrid_preds);
-
- // This is the only tricky bit of the new way we check implementation methods
- // We need to build a set of predicates where only the method-level bounds
- // are from the trait and we assume all other bounds from the implementation
- // to be previously satisfied.
- //
- // We then register the obligations from the impl_m and check to see
- // if all constraints hold.
- hybrid_preds
- .predicates
- .extend(trait_m_predicates.instantiate_own(tcx, trait_to_placeholder_substs).predicates);
-
- // Construct trait parameter environment and then shift it into the placeholder viewpoint.
- // The key step here is to update the caller_bounds's predicates to be
- // the new hybrid bounds we computed.
- let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_hir_id);
- let param_env = ty::ParamEnv::new(
- tcx.intern_predicates(&hybrid_preds.predicates),
- Reveal::UserFacing,
- hir::Constness::NotConst,
- );
- let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
-
- tcx.infer_ctxt().enter(|ref infcx| {
- let ocx = ObligationCtxt::new(infcx);
-
- debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds());
-
- let mut selcx = traits::SelectionContext::new(&infcx);
- let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs);
- for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) {
- let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id);
- let traits::Normalized { value: predicate, obligations } =
- traits::normalize(&mut selcx, param_env, normalize_cause, predicate);
-
- ocx.register_obligations(obligations);
- let cause = ObligationCause::new(
- span,
- impl_m_hir_id,
- ObligationCauseCode::CompareImplItemObligation {
- impl_item_def_id: impl_m.def_id.expect_local(),
- trait_item_def_id: trait_m.def_id,
- kind: impl_m.kind,
- },
- );
- ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate));
- }
-
- // We now need to check that the signature of the impl method is
- // compatible with that of the trait method. We do this by
- // checking that `impl_fty <: trait_fty`.
- //
- // FIXME. Unfortunately, this doesn't quite work right now because
- // associated type normalization is not integrated into subtype
- // checks. For the comparison to be valid, we need to
- // normalize the associated types in the impl/trait methods
- // first. However, because function types bind regions, just
- // calling `normalize_associated_types_in` would have no effect on
- // any associated types appearing in the fn arguments or return
- // type.
-
- // Compute placeholder form of impl and trait method tys.
- let tcx = infcx.tcx;
-
- let mut wf_tys = FxHashSet::default();
-
- let impl_sig = infcx.replace_bound_vars_with_fresh_vars(
- impl_m_span,
- infer::HigherRankedType,
- tcx.fn_sig(impl_m.def_id),
- );
-
- let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id);
- let impl_sig = ocx.normalize(norm_cause.clone(), param_env, impl_sig);
- let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig));
- debug!("compare_impl_method: impl_fty={:?}", impl_fty);
-
- let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
- let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig);
- let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig);
- // Add the resulting inputs and output as well-formed.
- wf_tys.extend(trait_sig.inputs_and_output.iter());
- let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig));
-
- debug!("compare_impl_method: trait_fty={:?}", trait_fty);
-
- // FIXME: We'd want to keep more accurate spans than "the method signature" when
- // processing the comparison between the trait and impl fn, but we sadly lose them
- // and point at the whole signature when a trait bound or specific input or output
- // type would be more appropriate. In other places we have a `Vec<Span>`
- // corresponding to their `Vec<Predicate>`, but we don't have that here.
- // Fixing this would improve the output of test `issue-83765.rs`.
- let sub_result = infcx
- .at(&cause, param_env)
- .sup(trait_fty, impl_fty)
- .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok));
-
- if let Err(terr) = sub_result {
- debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty);
-
- let (impl_err_span, trait_err_span) =
- extract_spans_for_error_reporting(&infcx, &terr, &cause, impl_m, trait_m);
-
- cause.span = impl_err_span;
-
- let mut diag = struct_span_err!(
- tcx.sess,
- cause.span(),
- E0053,
- "method `{}` has an incompatible type for trait",
- trait_m.name
- );
- match &terr {
- TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0)
- if trait_m.fn_has_self_parameter =>
- {
- let ty = trait_sig.inputs()[0];
- let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty())
- {
- ExplicitSelf::ByValue => "self".to_owned(),
- ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
- ExplicitSelf::ByReference(_, hir::Mutability::Mut) => {
- "&mut self".to_owned()
- }
- _ => format!("self: {ty}"),
- };
-
- // When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the
- // span points only at the type `Box<Self`>, but we want to cover the whole
- // argument pattern and type.
- let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
- ImplItemKind::Fn(ref sig, body) => tcx
- .hir()
- .body_param_names(body)
- .zip(sig.decl.inputs.iter())
- .map(|(param, ty)| param.span.to(ty.span))
- .next()
- .unwrap_or(impl_err_span),
- _ => bug!("{:?} is not a method", impl_m),
- };
-
- diag.span_suggestion(
- span,
- "change the self-receiver type to match the trait",
- sugg,
- Applicability::MachineApplicable,
- );
- }
- TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => {
- if trait_sig.inputs().len() == *i {
- // Suggestion to change output type. We do not suggest in `async` functions
- // to avoid complex logic or incorrect output.
- match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
- ImplItemKind::Fn(ref sig, _)
- if sig.header.asyncness == hir::IsAsync::NotAsync =>
- {
- let msg = "change the output type to match the trait";
- let ap = Applicability::MachineApplicable;
- match sig.decl.output {
- hir::FnRetTy::DefaultReturn(sp) => {
- let sugg = format!("-> {} ", trait_sig.output());
- diag.span_suggestion_verbose(sp, msg, sugg, ap);
- }
- hir::FnRetTy::Return(hir_ty) => {
- let sugg = trait_sig.output();
- diag.span_suggestion(hir_ty.span, msg, sugg, ap);
- }
- };
- }
- _ => {}
- };
- } else if let Some(trait_ty) = trait_sig.inputs().get(*i) {
- diag.span_suggestion(
- impl_err_span,
- "change the parameter type to match the trait",
- trait_ty,
- Applicability::MachineApplicable,
- );
- }
- }
- _ => {}
- }
-
- infcx.note_type_err(
- &mut diag,
- &cause,
- trait_err_span.map(|sp| (sp, "type in trait".to_owned())),
- Some(infer::ValuePairs::Terms(ExpectedFound {
- expected: trait_fty.into(),
- found: impl_fty.into(),
- })),
- &terr,
- false,
- false,
- );
-
- return Err(diag.emit());
- }
-
- // Check that all obligations are satisfied by the implementation's
- // version.
- let errors = ocx.select_all_or_error();
- if !errors.is_empty() {
- let reported = infcx.report_fulfillment_errors(&errors, None, false);
- return Err(reported);
- }
-
- // Finally, resolve all regions. This catches wily misuses of
- // lifetime parameters.
- let mut outlives_environment = OutlivesEnvironment::new(param_env);
- outlives_environment.add_implied_bounds(infcx, wf_tys, impl_m_hir_id);
- infcx.check_region_obligations_and_report_errors(
- impl_m.def_id.expect_local(),
- &outlives_environment,
- );
-
- Ok(())
- })
-}
-
-fn check_region_bounds_on_impl_item<'tcx>(
- tcx: TyCtxt<'tcx>,
- impl_m: &ty::AssocItem,
- trait_m: &ty::AssocItem,
- trait_generics: &ty::Generics,
- impl_generics: &ty::Generics,
-) -> Result<(), ErrorGuaranteed> {
- let trait_params = trait_generics.own_counts().lifetimes;
- let impl_params = impl_generics.own_counts().lifetimes;
-
- debug!(
- "check_region_bounds_on_impl_item: \
- trait_generics={:?} \
- impl_generics={:?}",
- trait_generics, impl_generics
- );
-
- // Must have same number of early-bound lifetime parameters.
- // Unfortunately, if the user screws up the bounds, then this
- // will change classification between early and late. E.g.,
- // if in trait we have `<'a,'b:'a>`, and in impl we just have
- // `<'a,'b>`, then we have 2 early-bound lifetime parameters
- // in trait but 0 in the impl. But if we report "expected 2
- // but found 0" it's confusing, because it looks like there
- // are zero. Since I don't quite know how to phrase things at
- // the moment, give a kind of vague error message.
- if trait_params != impl_params {
- let span = tcx
- .hir()
- .get_generics(impl_m.def_id.expect_local())
- .expect("expected impl item to have generics or else we can't compare them")
- .span;
- let generics_span = if let Some(local_def_id) = trait_m.def_id.as_local() {
- Some(
- tcx.hir()
- .get_generics(local_def_id)
- .expect("expected trait item to have generics or else we can't compare them")
- .span,
- )
- } else {
- None
- };
-
- let reported = tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait {
- span,
- item_kind: assoc_item_kind_str(impl_m),
- ident: impl_m.ident(tcx),
- generics_span,
- });
- return Err(reported);
- }
-
- Ok(())
-}
-
-#[instrument(level = "debug", skip(infcx))]
-fn extract_spans_for_error_reporting<'a, 'tcx>(
- infcx: &infer::InferCtxt<'a, 'tcx>,
- terr: &TypeError<'_>,
- cause: &ObligationCause<'tcx>,
- impl_m: &ty::AssocItem,
- trait_m: &ty::AssocItem,
-) -> (Span, Option<Span>) {
- let tcx = infcx.tcx;
- let mut impl_args = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
- ImplItemKind::Fn(ref sig, _) => {
- sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
- }
- _ => bug!("{:?} is not a method", impl_m),
- };
- let trait_args =
- trait_m.def_id.as_local().map(|def_id| match tcx.hir().expect_trait_item(def_id).kind {
- TraitItemKind::Fn(ref sig, _) => {
- sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
- }
- _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m),
- });
-
- match *terr {
- TypeError::ArgumentMutability(i) => {
- (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
- }
- TypeError::ArgumentSorts(ExpectedFound { .. }, i) => {
- (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
- }
- _ => (cause.span(), tcx.hir().span_if_local(trait_m.def_id)),
- }
-}
-
-fn compare_self_type<'tcx>(
- tcx: TyCtxt<'tcx>,
- impl_m: &ty::AssocItem,
- impl_m_span: Span,
- trait_m: &ty::AssocItem,
- impl_trait_ref: ty::TraitRef<'tcx>,
-) -> Result<(), ErrorGuaranteed> {
- // Try to give more informative error messages about self typing
- // mismatches. Note that any mismatch will also be detected
- // below, where we construct a canonical function type that
- // includes the self parameter as a normal parameter. It's just
- // that the error messages you get out of this code are a bit more
- // inscrutable, particularly for cases where one method has no
- // self.
-
- let self_string = |method: &ty::AssocItem| {
- let untransformed_self_ty = match method.container {
- ty::ImplContainer => impl_trait_ref.self_ty(),
- ty::TraitContainer => tcx.types.self_param,
- };
- let self_arg_ty = tcx.fn_sig(method.def_id).input(0);
- let param_env = ty::ParamEnv::reveal_all();
-
- tcx.infer_ctxt().enter(|infcx| {
- let self_arg_ty = tcx.liberate_late_bound_regions(method.def_id, self_arg_ty);
- let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty).is_ok();
- match ExplicitSelf::determine(self_arg_ty, can_eq_self) {
- ExplicitSelf::ByValue => "self".to_owned(),
- ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
- ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(),
- _ => format!("self: {self_arg_ty}"),
- }
- })
- };
-
- match (trait_m.fn_has_self_parameter, impl_m.fn_has_self_parameter) {
- (false, false) | (true, true) => {}
-
- (false, true) => {
- let self_descr = self_string(impl_m);
- let mut err = struct_span_err!(
- tcx.sess,
- impl_m_span,
- E0185,
- "method `{}` has a `{}` declaration in the impl, but not in the trait",
- trait_m.name,
- self_descr
- );
- err.span_label(impl_m_span, format!("`{self_descr}` used in impl"));
- if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
- err.span_label(span, format!("trait method declared without `{self_descr}`"));
- } else {
- err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
- }
- let reported = err.emit();
- return Err(reported);
- }
-
- (true, false) => {
- let self_descr = self_string(trait_m);
- let mut err = struct_span_err!(
- tcx.sess,
- impl_m_span,
- E0186,
- "method `{}` has a `{}` declaration in the trait, but not in the impl",
- trait_m.name,
- self_descr
- );
- err.span_label(impl_m_span, format!("expected `{self_descr}` in impl"));
- if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
- err.span_label(span, format!("`{self_descr}` used in trait"));
- } else {
- err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
- }
- let reported = err.emit();
- return Err(reported);
- }
- }
-
- Ok(())
-}
-
-/// Checks that the number of generics on a given assoc item in a trait impl is the same
-/// as the number of generics on the respective assoc item in the trait definition.
-///
-/// For example this code emits the errors in the following code:
-/// ```
-/// trait Trait {
-/// fn foo();
-/// type Assoc<T>;
-/// }
-///
-/// impl Trait for () {
-/// fn foo<T>() {}
-/// //~^ error
-/// type Assoc = u32;
-/// //~^ error
-/// }
-/// ```
-///
-/// Notably this does not error on `foo<T>` implemented as `foo<const N: u8>` or
-/// `foo<const N: u8>` implemented as `foo<const N: u32>`. This is handled in
-/// [`compare_generic_param_kinds`]. This function also does not handle lifetime parameters
-fn compare_number_of_generics<'tcx>(
- tcx: TyCtxt<'tcx>,
- impl_: &ty::AssocItem,
- _impl_span: Span,
- trait_: &ty::AssocItem,
- trait_span: Option<Span>,
-) -> Result<(), ErrorGuaranteed> {
- let trait_own_counts = tcx.generics_of(trait_.def_id).own_counts();
- let impl_own_counts = tcx.generics_of(impl_.def_id).own_counts();
-
- // This avoids us erroring on `foo<T>` implemented as `foo<const N: u8>` as this is implemented
- // in `compare_generic_param_kinds` which will give a nicer error message than something like:
- // "expected 1 type parameter, found 0 type parameters"
- if (trait_own_counts.types + trait_own_counts.consts)
- == (impl_own_counts.types + impl_own_counts.consts)
- {
- return Ok(());
- }
-
- let matchings = [
- ("type", trait_own_counts.types, impl_own_counts.types),
- ("const", trait_own_counts.consts, impl_own_counts.consts),
- ];
-
- let item_kind = assoc_item_kind_str(impl_);
-
- let mut err_occurred = None;
- for (kind, trait_count, impl_count) in matchings {
- if impl_count != trait_count {
- let arg_spans = |kind: ty::AssocKind, generics: &hir::Generics<'_>| {
- let mut spans = generics
- .params
- .iter()
- .filter(|p| match p.kind {
- hir::GenericParamKind::Lifetime {
- kind: hir::LifetimeParamKind::Elided,
- } => {
- // A fn can have an arbitrary number of extra elided lifetimes for the
- // same signature.
- !matches!(kind, ty::AssocKind::Fn)
- }
- _ => true,
- })
- .map(|p| p.span)
- .collect::<Vec<Span>>();
- if spans.is_empty() {
- spans = vec![generics.span]
- }
- spans
- };
- let (trait_spans, impl_trait_spans) = if let Some(def_id) = trait_.def_id.as_local() {
- let trait_item = tcx.hir().expect_trait_item(def_id);
- let arg_spans: Vec<Span> = arg_spans(trait_.kind, trait_item.generics);
- let impl_trait_spans: Vec<Span> = trait_item
- .generics
- .params
- .iter()
- .filter_map(|p| match p.kind {
- GenericParamKind::Type { synthetic: true, .. } => Some(p.span),
- _ => None,
- })
- .collect();
- (Some(arg_spans), impl_trait_spans)
- } else {
- (trait_span.map(|s| vec![s]), vec![])
- };
-
- let impl_item = tcx.hir().expect_impl_item(impl_.def_id.expect_local());
- let impl_item_impl_trait_spans: Vec<Span> = impl_item
- .generics
- .params
- .iter()
- .filter_map(|p| match p.kind {
- GenericParamKind::Type { synthetic: true, .. } => Some(p.span),
- _ => None,
- })
- .collect();
- let spans = arg_spans(impl_.kind, impl_item.generics);
- let span = spans.first().copied();
-
- let mut err = tcx.sess.struct_span_err_with_code(
- spans,
- &format!(
- "{} `{}` has {} {kind} parameter{} but its trait \
- declaration has {} {kind} parameter{}",
- item_kind,
- trait_.name,
- impl_count,
- pluralize!(impl_count),
- trait_count,
- pluralize!(trait_count),
- kind = kind,
- ),
- DiagnosticId::Error("E0049".into()),
- );
-
- let mut suffix = None;
-
- if let Some(spans) = trait_spans {
- let mut spans = spans.iter();
- if let Some(span) = spans.next() {
- err.span_label(
- *span,
- format!(
- "expected {} {} parameter{}",
- trait_count,
- kind,
- pluralize!(trait_count),
- ),
- );
- }
- for span in spans {
- err.span_label(*span, "");
- }
- } else {
- suffix = Some(format!(", expected {trait_count}"));
- }
-
- if let Some(span) = span {
- err.span_label(
- span,
- format!(
- "found {} {} parameter{}{}",
- impl_count,
- kind,
- pluralize!(impl_count),
- suffix.unwrap_or_else(String::new),
- ),
- );
- }
-
- for span in impl_trait_spans.iter().chain(impl_item_impl_trait_spans.iter()) {
- err.span_label(*span, "`impl Trait` introduces an implicit type parameter");
- }
-
- let reported = err.emit();
- err_occurred = Some(reported);
- }
- }
-
- if let Some(reported) = err_occurred { Err(reported) } else { Ok(()) }
-}
-
-fn compare_number_of_method_arguments<'tcx>(
- tcx: TyCtxt<'tcx>,
- impl_m: &ty::AssocItem,
- impl_m_span: Span,
- trait_m: &ty::AssocItem,
- trait_item_span: Option<Span>,
-) -> Result<(), ErrorGuaranteed> {
- let impl_m_fty = tcx.fn_sig(impl_m.def_id);
- let trait_m_fty = tcx.fn_sig(trait_m.def_id);
- let trait_number_args = trait_m_fty.inputs().skip_binder().len();
- let impl_number_args = impl_m_fty.inputs().skip_binder().len();
- if trait_number_args != impl_number_args {
- let trait_span = if let Some(def_id) = trait_m.def_id.as_local() {
- match tcx.hir().expect_trait_item(def_id).kind {
- TraitItemKind::Fn(ref trait_m_sig, _) => {
- let pos = if trait_number_args > 0 { trait_number_args - 1 } else { 0 };
- if let Some(arg) = trait_m_sig.decl.inputs.get(pos) {
- Some(if pos == 0 {
- arg.span
- } else {
- arg.span.with_lo(trait_m_sig.decl.inputs[0].span.lo())
- })
- } else {
- trait_item_span
- }
- }
- _ => bug!("{:?} is not a method", impl_m),
- }
- } else {
- trait_item_span
- };
- let impl_span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
- ImplItemKind::Fn(ref impl_m_sig, _) => {
- let pos = if impl_number_args > 0 { impl_number_args - 1 } else { 0 };
- if let Some(arg) = impl_m_sig.decl.inputs.get(pos) {
- if pos == 0 {
- arg.span
- } else {
- arg.span.with_lo(impl_m_sig.decl.inputs[0].span.lo())
- }
- } else {
- impl_m_span
- }
- }
- _ => bug!("{:?} is not a method", impl_m),
- };
- let mut err = struct_span_err!(
- tcx.sess,
- impl_span,
- E0050,
- "method `{}` has {} but the declaration in trait `{}` has {}",
- trait_m.name,
- potentially_plural_count(impl_number_args, "parameter"),
- tcx.def_path_str(trait_m.def_id),
- trait_number_args
- );
- if let Some(trait_span) = trait_span {
- err.span_label(
- trait_span,
- format!(
- "trait requires {}",
- potentially_plural_count(trait_number_args, "parameter")
- ),
- );
- } else {
- err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
- }
- err.span_label(
- impl_span,
- format!(
- "expected {}, found {}",
- potentially_plural_count(trait_number_args, "parameter"),
- impl_number_args
- ),
- );
- let reported = err.emit();
- return Err(reported);
- }
-
- Ok(())
-}
-
-fn compare_synthetic_generics<'tcx>(
- tcx: TyCtxt<'tcx>,
- impl_m: &ty::AssocItem,
- trait_m: &ty::AssocItem,
-) -> Result<(), ErrorGuaranteed> {
- // FIXME(chrisvittal) Clean up this function, list of FIXME items:
- // 1. Better messages for the span labels
- // 2. Explanation as to what is going on
- // If we get here, we already have the same number of generics, so the zip will
- // be okay.
- let mut error_found = None;
- let impl_m_generics = tcx.generics_of(impl_m.def_id);
- let trait_m_generics = tcx.generics_of(trait_m.def_id);
- let impl_m_type_params = impl_m_generics.params.iter().filter_map(|param| match param.kind {
- GenericParamDefKind::Type { synthetic, .. } => Some((param.def_id, synthetic)),
- GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => None,
- });
- let trait_m_type_params = trait_m_generics.params.iter().filter_map(|param| match param.kind {
- GenericParamDefKind::Type { synthetic, .. } => Some((param.def_id, synthetic)),
- GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => None,
- });
- for ((impl_def_id, impl_synthetic), (trait_def_id, trait_synthetic)) in
- iter::zip(impl_m_type_params, trait_m_type_params)
- {
- if impl_synthetic != trait_synthetic {
- let impl_def_id = impl_def_id.expect_local();
- let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_def_id);
- let impl_span = tcx.hir().span(impl_hir_id);
- let trait_span = tcx.def_span(trait_def_id);
- let mut err = struct_span_err!(
- tcx.sess,
- impl_span,
- E0643,
- "method `{}` has incompatible signature for trait",
- trait_m.name
- );
- err.span_label(trait_span, "declaration in trait here");
- match (impl_synthetic, trait_synthetic) {
- // The case where the impl method uses `impl Trait` but the trait method uses
- // explicit generics
- (true, false) => {
- err.span_label(impl_span, "expected generic parameter, found `impl Trait`");
- (|| {
- // try taking the name from the trait impl
- // FIXME: this is obviously suboptimal since the name can already be used
- // as another generic argument
- let new_name = tcx.sess.source_map().span_to_snippet(trait_span).ok()?;
- let trait_m = trait_m.def_id.as_local()?;
- let trait_m = tcx.hir().trait_item(hir::TraitItemId { def_id: trait_m });
-
- let impl_m = impl_m.def_id.as_local()?;
- let impl_m = tcx.hir().impl_item(hir::ImplItemId { def_id: impl_m });
-
- // in case there are no generics, take the spot between the function name
- // and the opening paren of the argument list
- let new_generics_span =
- tcx.sess.source_map().generate_fn_name_span(impl_span)?.shrink_to_hi();
- // in case there are generics, just replace them
- let generics_span =
- impl_m.generics.span.substitute_dummy(new_generics_span);
- // replace with the generics from the trait
- let new_generics =
- tcx.sess.source_map().span_to_snippet(trait_m.generics.span).ok()?;
-
- err.multipart_suggestion(
- "try changing the `impl Trait` argument to a generic parameter",
- vec![
- // replace `impl Trait` with `T`
- (impl_span, new_name),
- // replace impl method generics with trait method generics
- // This isn't quite right, as users might have changed the names
- // of the generics, but it works for the common case
- (generics_span, new_generics),
- ],
- Applicability::MaybeIncorrect,
- );
- Some(())
- })();
- }
- // The case where the trait method uses `impl Trait`, but the impl method uses
- // explicit generics.
- (false, true) => {
- err.span_label(impl_span, "expected `impl Trait`, found generic parameter");
- (|| {
- let impl_m = impl_m.def_id.as_local()?;
- let impl_m = tcx.hir().impl_item(hir::ImplItemId { def_id: impl_m });
- let input_tys = match impl_m.kind {
- hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs,
- _ => unreachable!(),
- };
- struct Visitor(Option<Span>, hir::def_id::LocalDefId);
- impl<'v> intravisit::Visitor<'v> for Visitor {
- fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
- intravisit::walk_ty(self, ty);
- if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) =
- ty.kind
- && let Res::Def(DefKind::TyParam, def_id) = path.res
- && def_id == self.1.to_def_id()
- {
- self.0 = Some(ty.span);
- }
- }
- }
- let mut visitor = Visitor(None, impl_def_id);
- for ty in input_tys {
- intravisit::Visitor::visit_ty(&mut visitor, ty);
- }
- let span = visitor.0?;
-
- let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds;
- let bounds = bounds.first()?.span().to(bounds.last()?.span());
- let bounds = tcx.sess.source_map().span_to_snippet(bounds).ok()?;
-
- err.multipart_suggestion(
- "try removing the generic parameter and using `impl Trait` instead",
- vec![
- // delete generic parameters
- (impl_m.generics.span, String::new()),
- // replace param usage with `impl Trait`
- (span, format!("impl {bounds}")),
- ],
- Applicability::MaybeIncorrect,
- );
- Some(())
- })();
- }
- _ => unreachable!(),
- }
- let reported = err.emit();
- error_found = Some(reported);
- }
- }
- if let Some(reported) = error_found { Err(reported) } else { Ok(()) }
-}
-
-/// Checks that all parameters in the generics of a given assoc item in a trait impl have
-/// the same kind as the respective generic parameter in the trait def.
-///
-/// For example all 4 errors in the following code are emitted here:
-/// ```
-/// trait Foo {
-/// fn foo<const N: u8>();
-/// type bar<const N: u8>;
-/// fn baz<const N: u32>();
-/// type blah<T>;
-/// }
-///
-/// impl Foo for () {
-/// fn foo<const N: u64>() {}
-/// //~^ error
-/// type bar<const N: u64> {}
-/// //~^ error
-/// fn baz<T>() {}
-/// //~^ error
-/// type blah<const N: i64> = u32;
-/// //~^ error
-/// }
-/// ```
-///
-/// This function does not handle lifetime parameters
-fn compare_generic_param_kinds<'tcx>(
- tcx: TyCtxt<'tcx>,
- impl_item: &ty::AssocItem,
- trait_item: &ty::AssocItem,
-) -> Result<(), ErrorGuaranteed> {
- assert_eq!(impl_item.kind, trait_item.kind);
-
- let ty_const_params_of = |def_id| {
- tcx.generics_of(def_id).params.iter().filter(|param| {
- matches!(
- param.kind,
- GenericParamDefKind::Const { .. } | GenericParamDefKind::Type { .. }
- )
- })
- };
-
- for (param_impl, param_trait) in
- iter::zip(ty_const_params_of(impl_item.def_id), ty_const_params_of(trait_item.def_id))
- {
- use GenericParamDefKind::*;
- if match (&param_impl.kind, &param_trait.kind) {
- (Const { .. }, Const { .. })
- if tcx.type_of(param_impl.def_id) != tcx.type_of(param_trait.def_id) =>
- {
- true
- }
- (Const { .. }, Type { .. }) | (Type { .. }, Const { .. }) => true,
- // this is exhaustive so that anyone adding new generic param kinds knows
- // to make sure this error is reported for them.
- (Const { .. }, Const { .. }) | (Type { .. }, Type { .. }) => false,
- (Lifetime { .. }, _) | (_, Lifetime { .. }) => unreachable!(),
- } {
- let param_impl_span = tcx.def_span(param_impl.def_id);
- let param_trait_span = tcx.def_span(param_trait.def_id);
-
- let mut err = struct_span_err!(
- tcx.sess,
- param_impl_span,
- E0053,
- "{} `{}` has an incompatible generic parameter for trait `{}`",
- assoc_item_kind_str(&impl_item),
- trait_item.name,
- &tcx.def_path_str(tcx.parent(trait_item.def_id))
- );
-
- let make_param_message = |prefix: &str, param: &ty::GenericParamDef| match param.kind {
- Const { .. } => {
- format!("{} const parameter of type `{}`", prefix, tcx.type_of(param.def_id))
- }
- Type { .. } => format!("{} type parameter", prefix),
- Lifetime { .. } => unreachable!(),
- };
-
- let trait_header_span = tcx.def_ident_span(tcx.parent(trait_item.def_id)).unwrap();
- err.span_label(trait_header_span, "");
- err.span_label(param_trait_span, make_param_message("expected", param_trait));
-
- let impl_header_span = tcx.def_span(tcx.parent(impl_item.def_id));
- err.span_label(impl_header_span, "");
- err.span_label(param_impl_span, make_param_message("found", param_impl));
-
- let reported = err.emit();
- return Err(reported);
- }
- }
-
- Ok(())
-}
-
-pub(crate) fn compare_const_impl<'tcx>(
- tcx: TyCtxt<'tcx>,
- impl_c: &ty::AssocItem,
- impl_c_span: Span,
- trait_c: &ty::AssocItem,
- impl_trait_ref: ty::TraitRef<'tcx>,
-) {
- debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref);
-
- tcx.infer_ctxt().enter(|infcx| {
- let param_env = tcx.param_env(impl_c.def_id);
- let ocx = ObligationCtxt::new(&infcx);
-
- // The below is for the most part highly similar to the procedure
- // for methods above. It is simpler in many respects, especially
- // because we shouldn't really have to deal with lifetimes or
- // predicates. In fact some of this should probably be put into
- // shared functions because of DRY violations...
- let trait_to_impl_substs = impl_trait_ref.substs;
-
- // Create a parameter environment that represents the implementation's
- // method.
- let impl_c_hir_id = tcx.hir().local_def_id_to_hir_id(impl_c.def_id.expect_local());
-
- // Compute placeholder form of impl and trait const tys.
- let impl_ty = tcx.type_of(impl_c.def_id);
- let trait_ty = tcx.bound_type_of(trait_c.def_id).subst(tcx, trait_to_impl_substs);
- let mut cause = ObligationCause::new(
- impl_c_span,
- impl_c_hir_id,
- ObligationCauseCode::CompareImplItemObligation {
- impl_item_def_id: impl_c.def_id.expect_local(),
- trait_item_def_id: trait_c.def_id,
- kind: impl_c.kind,
- },
- );
-
- // There is no "body" here, so just pass dummy id.
- let impl_ty = ocx.normalize(cause.clone(), param_env, impl_ty);
-
- debug!("compare_const_impl: impl_ty={:?}", impl_ty);
-
- let trait_ty = ocx.normalize(cause.clone(), param_env, trait_ty);
-
- debug!("compare_const_impl: trait_ty={:?}", trait_ty);
-
- let err = infcx
- .at(&cause, param_env)
- .sup(trait_ty, impl_ty)
- .map(|ok| ocx.register_infer_ok_obligations(ok));
-
- if let Err(terr) = err {
- debug!(
- "checking associated const for compatibility: impl ty {:?}, trait ty {:?}",
- impl_ty, trait_ty
- );
-
- // Locate the Span containing just the type of the offending impl
- match tcx.hir().expect_impl_item(impl_c.def_id.expect_local()).kind {
- ImplItemKind::Const(ref ty, _) => cause.span = ty.span,
- _ => bug!("{:?} is not a impl const", impl_c),
- }
-
- let mut diag = struct_span_err!(
- tcx.sess,
- cause.span,
- E0326,
- "implemented const `{}` has an incompatible type for trait",
- trait_c.name
- );
-
- let trait_c_span = trait_c.def_id.as_local().map(|trait_c_def_id| {
- // Add a label to the Span containing just the type of the const
- match tcx.hir().expect_trait_item(trait_c_def_id).kind {
- TraitItemKind::Const(ref ty, _) => ty.span,
- _ => bug!("{:?} is not a trait const", trait_c),
- }
- });
-
- infcx.note_type_err(
- &mut diag,
- &cause,
- trait_c_span.map(|span| (span, "type in trait".to_owned())),
- Some(infer::ValuePairs::Terms(ExpectedFound {
- expected: trait_ty.into(),
- found: impl_ty.into(),
- })),
- &terr,
- false,
- false,
- );
- diag.emit();
- }
-
- // Check that all obligations are satisfied by the implementation's
- // version.
- let errors = ocx.select_all_or_error();
- if !errors.is_empty() {
- infcx.report_fulfillment_errors(&errors, None, false);
- return;
- }
-
- let outlives_environment = OutlivesEnvironment::new(param_env);
- infcx.check_region_obligations_and_report_errors(
- impl_c.def_id.expect_local(),
- &outlives_environment,
- );
- });
-}
-
-pub(crate) fn compare_ty_impl<'tcx>(
- tcx: TyCtxt<'tcx>,
- impl_ty: &ty::AssocItem,
- impl_ty_span: Span,
- trait_ty: &ty::AssocItem,
- impl_trait_ref: ty::TraitRef<'tcx>,
- trait_item_span: Option<Span>,
-) {
- debug!("compare_impl_type(impl_trait_ref={:?})", impl_trait_ref);
-
- let _: Result<(), ErrorGuaranteed> = (|| {
- compare_number_of_generics(tcx, impl_ty, impl_ty_span, trait_ty, trait_item_span)?;
-
- compare_generic_param_kinds(tcx, impl_ty, trait_ty)?;
-
- let sp = tcx.def_span(impl_ty.def_id);
- compare_type_predicate_entailment(tcx, impl_ty, sp, trait_ty, impl_trait_ref)?;
-
- check_type_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref)
- })();
-}
-
-/// The equivalent of [compare_predicate_entailment], but for associated types
-/// instead of associated functions.
-fn compare_type_predicate_entailment<'tcx>(
- tcx: TyCtxt<'tcx>,
- impl_ty: &ty::AssocItem,
- impl_ty_span: Span,
- trait_ty: &ty::AssocItem,
- impl_trait_ref: ty::TraitRef<'tcx>,
-) -> Result<(), ErrorGuaranteed> {
- let impl_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
- let trait_to_impl_substs =
- impl_substs.rebase_onto(tcx, impl_ty.container_id(tcx), impl_trait_ref.substs);
-
- let impl_ty_generics = tcx.generics_of(impl_ty.def_id);
- let trait_ty_generics = tcx.generics_of(trait_ty.def_id);
- let impl_ty_predicates = tcx.predicates_of(impl_ty.def_id);
- let trait_ty_predicates = tcx.predicates_of(trait_ty.def_id);
-
- check_region_bounds_on_impl_item(
- tcx,
- impl_ty,
- trait_ty,
- &trait_ty_generics,
- &impl_ty_generics,
- )?;
-
- let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_substs);
-
- if impl_ty_own_bounds.is_empty() {
- // Nothing to check.
- return Ok(());
- }
-
- // This `HirId` should be used for the `body_id` field on each
- // `ObligationCause` (and the `FnCtxt`). This is what
- // `regionck_item` expects.
- let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
- debug!("compare_type_predicate_entailment: trait_to_impl_substs={:?}", trait_to_impl_substs);
-
- // The predicates declared by the impl definition, the trait and the
- // associated type in the trait are assumed.
- let impl_predicates = tcx.predicates_of(impl_ty_predicates.parent.unwrap());
- let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
- hybrid_preds
- .predicates
- .extend(trait_ty_predicates.instantiate_own(tcx, trait_to_impl_substs).predicates);
-
- debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds);
-
- let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id);
- let param_env = ty::ParamEnv::new(
- tcx.intern_predicates(&hybrid_preds.predicates),
- Reveal::UserFacing,
- hir::Constness::NotConst,
- );
- let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
- tcx.infer_ctxt().enter(|infcx| {
- let ocx = ObligationCtxt::new(&infcx);
-
- debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds());
-
- let mut selcx = traits::SelectionContext::new(&infcx);
-
- assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len());
- for (span, predicate) in
- std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates)
- {
- let cause = ObligationCause::misc(span, impl_ty_hir_id);
- let traits::Normalized { value: predicate, obligations } =
- traits::normalize(&mut selcx, param_env, cause, predicate);
-
- let cause = ObligationCause::new(
- span,
- impl_ty_hir_id,
- ObligationCauseCode::CompareImplItemObligation {
- impl_item_def_id: impl_ty.def_id.expect_local(),
- trait_item_def_id: trait_ty.def_id,
- kind: impl_ty.kind,
- },
- );
- ocx.register_obligations(obligations);
- ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate));
- }
-
- // Check that all obligations are satisfied by the implementation's
- // version.
- let errors = ocx.select_all_or_error();
- if !errors.is_empty() {
- let reported = infcx.report_fulfillment_errors(&errors, None, false);
- return Err(reported);
- }
-
- // Finally, resolve all regions. This catches wily misuses of
- // lifetime parameters.
- let outlives_environment = OutlivesEnvironment::new(param_env);
- infcx.check_region_obligations_and_report_errors(
- impl_ty.def_id.expect_local(),
- &outlives_environment,
- );
-
- Ok(())
- })
-}
-
-/// Validate that `ProjectionCandidate`s created for this associated type will
-/// be valid.
-///
-/// Usually given
-///
-/// trait X { type Y: Copy } impl X for T { type Y = S; }
-///
-/// We are able to normalize `<T as X>::U` to `S`, and so when we check the
-/// impl is well-formed we have to prove `S: Copy`.
-///
-/// For default associated types the normalization is not possible (the value
-/// from the impl could be overridden). We also can't normalize generic
-/// associated types (yet) because they contain bound parameters.
-#[tracing::instrument(level = "debug", skip(tcx))]
-pub fn check_type_bounds<'tcx>(
- tcx: TyCtxt<'tcx>,
- trait_ty: &ty::AssocItem,
- impl_ty: &ty::AssocItem,
- impl_ty_span: Span,
- impl_trait_ref: ty::TraitRef<'tcx>,
-) -> Result<(), ErrorGuaranteed> {
- // Given
- //
- // impl<A, B> Foo<u32> for (A, B) {
- // type Bar<C> =...
- // }
- //
- // - `impl_trait_ref` would be `<(A, B) as Foo<u32>>
- // - `impl_ty_substs` would be `[A, B, ^0.0]` (`^0.0` here is the bound var with db 0 and index 0)
- // - `rebased_substs` would be `[(A, B), u32, ^0.0]`, combining the substs from
- // the *trait* with the generic associated type parameters (as bound vars).
- //
- // A note regarding the use of bound vars here:
- // Imagine as an example
- // ```
- // trait Family {
- // type Member<C: Eq>;
- // }
- //
- // impl Family for VecFamily {
- // type Member<C: Eq> = i32;
- // }
- // ```
- // Here, we would generate
- // ```notrust
- // forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) }
- // ```
- // when we really would like to generate
- // ```notrust
- // forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) :- Implemented(C: Eq) }
- // ```
- // But, this is probably fine, because although the first clause can be used with types C that
- // do not implement Eq, for it to cause some kind of problem, there would have to be a
- // VecFamily::Member<X> for some type X where !(X: Eq), that appears in the value of type
- // Member<C: Eq> = .... That type would fail a well-formedness check that we ought to be doing
- // elsewhere, which would check that any <T as Family>::Member<X> meets the bounds declared in
- // the trait (notably, that X: Eq and T: Family).
- let defs: &ty::Generics = tcx.generics_of(impl_ty.def_id);
- let mut substs = smallvec::SmallVec::with_capacity(defs.count());
- if let Some(def_id) = defs.parent {
- let parent_defs = tcx.generics_of(def_id);
- InternalSubsts::fill_item(&mut substs, tcx, parent_defs, &mut |param, _| {
- tcx.mk_param_from_def(param)
- });
- }
- let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
- smallvec::SmallVec::with_capacity(defs.count());
- InternalSubsts::fill_single(&mut substs, defs, &mut |param, _| match param.kind {
- GenericParamDefKind::Type { .. } => {
- let kind = ty::BoundTyKind::Param(param.name);
- let bound_var = ty::BoundVariableKind::Ty(kind);
- bound_vars.push(bound_var);
- tcx.mk_ty(ty::Bound(
- ty::INNERMOST,
- ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
- ))
- .into()
- }
- GenericParamDefKind::Lifetime => {
- let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
- let bound_var = ty::BoundVariableKind::Region(kind);
- bound_vars.push(bound_var);
- tcx.mk_region(ty::ReLateBound(
- ty::INNERMOST,
- ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
- ))
- .into()
- }
- GenericParamDefKind::Const { .. } => {
- let bound_var = ty::BoundVariableKind::Const;
- bound_vars.push(bound_var);
- tcx.mk_const(ty::ConstS {
- ty: tcx.type_of(param.def_id),
- kind: ty::ConstKind::Bound(
- ty::INNERMOST,
- ty::BoundVar::from_usize(bound_vars.len() - 1),
- ),
- })
- .into()
- }
- });
- let bound_vars = tcx.mk_bound_variable_kinds(bound_vars.into_iter());
- let impl_ty_substs = tcx.intern_substs(&substs);
- let container_id = impl_ty.container_id(tcx);
-
- let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs);
- let impl_ty_value = tcx.type_of(impl_ty.def_id);
-
- let param_env = tcx.param_env(impl_ty.def_id);
-
- // When checking something like
- //
- // trait X { type Y: PartialEq<<Self as X>::Y> }
- // impl X for T { default type Y = S; }
- //
- // We will have to prove the bound S: PartialEq<<T as X>::Y>. In this case
- // we want <T as X>::Y to normalize to S. This is valid because we are
- // checking the default value specifically here. Add this equality to the
- // ParamEnv for normalization specifically.
- let normalize_param_env = {
- let mut predicates = param_env.caller_bounds().iter().collect::<Vec<_>>();
- match impl_ty_value.kind() {
- ty::Projection(proj)
- if proj.item_def_id == trait_ty.def_id && proj.substs == rebased_substs =>
- {
- // Don't include this predicate if the projected type is
- // exactly the same as the projection. This can occur in
- // (somewhat dubious) code like this:
- //
- // impl<T> X for T where T: X { type Y = <T as X>::Y; }
- }
- _ => predicates.push(
- ty::Binder::bind_with_vars(
- ty::ProjectionPredicate {
- projection_ty: ty::ProjectionTy {
- item_def_id: trait_ty.def_id,
- substs: rebased_substs,
- },
- term: impl_ty_value.into(),
- },
- bound_vars,
- )
- .to_predicate(tcx),
- ),
- };
- ty::ParamEnv::new(
- tcx.intern_predicates(&predicates),
- Reveal::UserFacing,
- param_env.constness(),
- )
- };
- debug!(?normalize_param_env);
-
- let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
- let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs);
-
- tcx.infer_ctxt().enter(move |infcx| {
- let ocx = ObligationCtxt::new(&infcx);
-
- let mut selcx = traits::SelectionContext::new(&infcx);
- let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
- let normalize_cause = ObligationCause::new(
- impl_ty_span,
- impl_ty_hir_id,
- ObligationCauseCode::CheckAssociatedTypeBounds {
- impl_item_def_id: impl_ty.def_id.expect_local(),
- trait_item_def_id: trait_ty.def_id,
- },
- );
- let mk_cause = |span: Span| {
- let code = if span.is_dummy() {
- traits::MiscObligation
- } else {
- traits::BindingObligation(trait_ty.def_id, span)
- };
- ObligationCause::new(impl_ty_span, impl_ty_hir_id, code)
- };
-
- let obligations = tcx
- .bound_explicit_item_bounds(trait_ty.def_id)
- .transpose_iter()
- .map(|e| e.map_bound(|e| *e).transpose_tuple2())
- .map(|(bound, span)| {
- debug!(?bound);
- // this is where opaque type is found
- let concrete_ty_bound = bound.subst(tcx, rebased_substs);
- debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
-
- traits::Obligation::new(mk_cause(span.0), param_env, concrete_ty_bound)
- })
- .collect();
- debug!("check_type_bounds: item_bounds={:?}", obligations);
-
- for mut obligation in util::elaborate_obligations(tcx, obligations) {
- let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize(
- &mut selcx,
- normalize_param_env,
- normalize_cause.clone(),
- obligation.predicate,
- );
- debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
- obligation.predicate = normalized_predicate;
-
- ocx.register_obligations(obligations);
- ocx.register_obligation(obligation);
- }
- // Check that all obligations are satisfied by the implementation's
- // version.
- let errors = ocx.select_all_or_error();
- if !errors.is_empty() {
- let reported = infcx.report_fulfillment_errors(&errors, None, false);
- return Err(reported);
- }
-
- // Finally, resolve all regions. This catches wily misuses of
- // lifetime parameters.
- let implied_bounds = match impl_ty.container {
- ty::TraitContainer => FxHashSet::default(),
- ty::ImplContainer => wfcheck::impl_implied_bounds(
- tcx,
- param_env,
- container_id.expect_local(),
- impl_ty_span,
- ),
- };
- let mut outlives_environment = OutlivesEnvironment::new(param_env);
- outlives_environment.add_implied_bounds(&infcx, implied_bounds, impl_ty_hir_id);
- infcx.check_region_obligations_and_report_errors(
- impl_ty.def_id.expect_local(),
- &outlives_environment,
- );
-
- let constraints = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
- for (key, value) in constraints {
- infcx
- .report_mismatched_types(
- &ObligationCause::misc(
- value.hidden_type.span,
- tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()),
- ),
- tcx.mk_opaque(key.def_id.to_def_id(), key.substs),
- value.hidden_type.ty,
- TypeError::Mismatch,
- )
- .emit();
- }
-
- Ok(())
- })
-}
-
-fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str {
- match impl_item.kind {
- ty::AssocKind::Const => "const",
- ty::AssocKind::Fn => "method",
- ty::AssocKind::Type => "type",
- }
-}
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
deleted file mode 100644
index 6e97b0bf2..000000000
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ /dev/null
@@ -1,2824 +0,0 @@
-//! Type checking expressions.
-//!
-//! See `mod.rs` for more context on type checking in general.
-
-use crate::astconv::AstConv as _;
-use crate::check::cast;
-use crate::check::coercion::CoerceMany;
-use crate::check::fatally_break_rust;
-use crate::check::method::SelfSource;
-use crate::check::report_unexpected_variant_res;
-use crate::check::BreakableCtxt;
-use crate::check::Diverges;
-use crate::check::DynamicCoerceMany;
-use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation};
-use crate::check::FnCtxt;
-use crate::check::Needs;
-use crate::check::TupleArgumentsFlag::DontTupleArguments;
-use crate::errors::{
- FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct,
- YieldExprOutsideOfGenerator,
-};
-use crate::type_error_struct;
-
-use super::suggest_call_constructor;
-use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
-use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::{
- pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
- EmissionGuarantee, ErrorGuaranteed,
-};
-use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, DefKind, Res};
-use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::Visitor;
-use rustc_hir::lang_items::LangItem;
-use rustc_hir::{Closure, ExprKind, HirId, QPath};
-use rustc_infer::infer;
-use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_infer::infer::InferOk;
-use rustc_infer::traits::ObligationCause;
-use rustc_middle::middle::stability;
-use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
-use rustc_middle::ty::error::TypeError::FieldMisMatch;
-use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable};
-use rustc_session::parse::feature_err;
-use rustc_span::hygiene::DesugaringKind;
-use rustc_span::lev_distance::find_best_match_for_name;
-use rustc_span::source_map::{Span, Spanned};
-use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{BytePos, Pos};
-use rustc_target::spec::abi::Abi::RustIntrinsic;
-use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::{self, ObligationCauseCode};
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- fn check_expr_eq_type(&self, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>) {
- let ty = self.check_expr_with_hint(expr, expected);
- self.demand_eqtype(expr.span, expected, ty);
- }
-
- pub fn check_expr_has_type_or_error(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- expected: Ty<'tcx>,
- extend_err: impl FnMut(&mut Diagnostic),
- ) -> Ty<'tcx> {
- self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected), extend_err)
- }
-
- fn check_expr_meets_expectation_or_error(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- expected: Expectation<'tcx>,
- mut extend_err: impl FnMut(&mut Diagnostic),
- ) -> Ty<'tcx> {
- let expected_ty = expected.to_option(&self).unwrap_or(self.tcx.types.bool);
- let mut ty = self.check_expr_with_expectation(expr, expected);
-
- // While we don't allow *arbitrary* coercions here, we *do* allow
- // coercions from ! to `expected`.
- if ty.is_never() {
- if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) {
- self.tcx().sess.delay_span_bug(
- expr.span,
- "expression with never type wound up being adjusted",
- );
- return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] {
- target.to_owned()
- } else {
- self.tcx().ty_error()
- };
- }
-
- let adj_ty = self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::AdjustmentType,
- span: expr.span,
- });
- self.apply_adjustments(
- expr,
- vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty }],
- );
- ty = adj_ty;
- }
-
- if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
- let expr = expr.peel_drop_temps();
- self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty, None);
- extend_err(&mut err);
- err.emit();
- }
- ty
- }
-
- pub(super) fn check_expr_coercable_to_type(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- expected: Ty<'tcx>,
- expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
- ) -> Ty<'tcx> {
- let ty = self.check_expr_with_hint(expr, expected);
- // checks don't need two phase
- self.demand_coerce(expr, ty, expected, expected_ty_expr, AllowTwoPhase::No)
- }
-
- pub(super) fn check_expr_with_hint(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- expected: Ty<'tcx>,
- ) -> Ty<'tcx> {
- self.check_expr_with_expectation(expr, ExpectHasType(expected))
- }
-
- fn check_expr_with_expectation_and_needs(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- expected: Expectation<'tcx>,
- needs: Needs,
- ) -> Ty<'tcx> {
- let ty = self.check_expr_with_expectation(expr, expected);
-
- // If the expression is used in a place whether mutable place is required
- // e.g. LHS of assignment, perform the conversion.
- if let Needs::MutPlace = needs {
- self.convert_place_derefs_to_mutable(expr);
- }
-
- ty
- }
-
- pub(super) fn check_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> {
- self.check_expr_with_expectation(expr, NoExpectation)
- }
-
- pub(super) fn check_expr_with_needs(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- needs: Needs,
- ) -> Ty<'tcx> {
- self.check_expr_with_expectation_and_needs(expr, NoExpectation, needs)
- }
-
- /// Invariant:
- /// If an expression has any sub-expressions that result in a type error,
- /// inspecting that expression's type with `ty.references_error()` will return
- /// true. Likewise, if an expression is known to diverge, inspecting its
- /// type with `ty::type_is_bot` will return true (n.b.: since Rust is
- /// strict, _|_ can appear in the type of an expression that does not,
- /// itself, diverge: for example, fn() -> _|_.)
- /// Note that inspecting a type's structure *directly* may expose the fact
- /// that there are actually multiple representations for `Error`, so avoid
- /// that when err needs to be handled differently.
- #[instrument(skip(self, expr), level = "debug")]
- pub(super) fn check_expr_with_expectation(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- self.check_expr_with_expectation_and_args(expr, expected, &[])
- }
-
- /// Same as `check_expr_with_expectation`, but allows us to pass in the arguments of a
- /// `ExprKind::Call` when evaluating its callee when it is an `ExprKind::Path`.
- pub(super) fn check_expr_with_expectation_and_args(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- expected: Expectation<'tcx>,
- args: &'tcx [hir::Expr<'tcx>],
- ) -> Ty<'tcx> {
- if self.tcx().sess.verbose() {
- // make this code only run with -Zverbose because it is probably slow
- if let Ok(lint_str) = self.tcx.sess.source_map().span_to_snippet(expr.span) {
- if !lint_str.contains('\n') {
- debug!("expr text: {lint_str}");
- } else {
- let mut lines = lint_str.lines();
- if let Some(line0) = lines.next() {
- let remaining_lines = lines.count();
- debug!("expr text: {line0}");
- debug!("expr text: ...(and {remaining_lines} more lines)");
- }
- }
- }
- }
-
- // True if `expr` is a `Try::from_ok(())` that is a result of desugaring a try block
- // without the final expr (e.g. `try { return; }`). We don't want to generate an
- // unreachable_code lint for it since warnings for autogenerated code are confusing.
- let is_try_block_generated_unit_expr = match expr.kind {
- ExprKind::Call(_, args) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {
- args.len() == 1 && args[0].span.is_desugaring(DesugaringKind::TryBlock)
- }
-
- _ => false,
- };
-
- // Warn for expressions after diverging siblings.
- if !is_try_block_generated_unit_expr {
- self.warn_if_unreachable(expr.hir_id, expr.span, "expression");
- }
-
- // Hide the outer diverging and has_errors flags.
- let old_diverges = self.diverges.replace(Diverges::Maybe);
- let old_has_errors = self.has_errors.replace(false);
-
- let ty = ensure_sufficient_stack(|| match &expr.kind {
- hir::ExprKind::Path(
- qpath @ hir::QPath::Resolved(..) | qpath @ hir::QPath::TypeRelative(..),
- ) => self.check_expr_path(qpath, expr, args),
- _ => self.check_expr_kind(expr, expected),
- });
-
- // Warn for non-block expressions with diverging children.
- match expr.kind {
- ExprKind::Block(..)
- | ExprKind::If(..)
- | ExprKind::Let(..)
- | ExprKind::Loop(..)
- | ExprKind::Match(..) => {}
- // If `expr` is a result of desugaring the try block and is an ok-wrapped
- // diverging expression (e.g. it arose from desugaring of `try { return }`),
- // we skip issuing a warning because it is autogenerated code.
- ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {}
- ExprKind::Call(callee, _) => self.warn_if_unreachable(expr.hir_id, callee.span, "call"),
- ExprKind::MethodCall(segment, ..) => {
- self.warn_if_unreachable(expr.hir_id, segment.ident.span, "call")
- }
- _ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"),
- }
-
- // Any expression that produces a value of type `!` must have diverged
- if ty.is_never() {
- self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
- }
-
- // Record the type, which applies it effects.
- // We need to do this after the warning above, so that
- // we don't warn for the diverging expression itself.
- self.write_ty(expr.hir_id, ty);
-
- // Combine the diverging and has_error flags.
- self.diverges.set(self.diverges.get() | old_diverges);
- self.has_errors.set(self.has_errors.get() | old_has_errors);
-
- debug!("type of {} is...", self.tcx.hir().node_to_string(expr.hir_id));
- debug!("... {:?}, expected is {:?}", ty, expected);
-
- ty
- }
-
- #[instrument(skip(self, expr), level = "debug")]
- fn check_expr_kind(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- trace!("expr={:#?}", expr);
-
- let tcx = self.tcx;
- match expr.kind {
- ExprKind::Box(subexpr) => self.check_expr_box(subexpr, expected),
- ExprKind::Lit(ref lit) => self.check_lit(&lit, expected),
- ExprKind::Binary(op, lhs, rhs) => self.check_binop(expr, op, lhs, rhs, expected),
- ExprKind::Assign(lhs, rhs, span) => {
- self.check_expr_assign(expr, expected, lhs, rhs, span)
- }
- ExprKind::AssignOp(op, lhs, rhs) => {
- self.check_binop_assign(expr, op, lhs, rhs, expected)
- }
- ExprKind::Unary(unop, oprnd) => self.check_expr_unary(unop, oprnd, expected, expr),
- ExprKind::AddrOf(kind, mutbl, oprnd) => {
- self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr)
- }
- ExprKind::Path(QPath::LangItem(lang_item, _, hir_id)) => {
- self.check_lang_item_path(lang_item, expr, hir_id)
- }
- ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, &[]),
- ExprKind::InlineAsm(asm) => {
- // We defer some asm checks as we may not have resolved the input and output types yet (they may still be infer vars).
- self.deferred_asm_checks.borrow_mut().push((asm, expr.hir_id));
- self.check_expr_asm(asm)
- }
- ExprKind::Break(destination, ref expr_opt) => {
- self.check_expr_break(destination, expr_opt.as_deref(), expr)
- }
- ExprKind::Continue(destination) => {
- if destination.target_id.is_ok() {
- tcx.types.never
- } else {
- // There was an error; make type-check fail.
- tcx.ty_error()
- }
- }
- ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr),
- ExprKind::Let(let_expr) => self.check_expr_let(let_expr),
- ExprKind::Loop(body, _, source, _) => {
- self.check_expr_loop(body, source, expected, expr)
- }
- ExprKind::Match(discrim, arms, match_src) => {
- self.check_match(expr, &discrim, arms, expected, match_src)
- }
- ExprKind::Closure(&Closure { capture_clause, fn_decl, body, movability, .. }) => {
- self.check_expr_closure(expr, capture_clause, &fn_decl, body, movability, expected)
- }
- ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected),
- ExprKind::Call(callee, args) => self.check_call(expr, &callee, args, expected),
- ExprKind::MethodCall(segment, args, _) => {
- self.check_method_call(expr, segment, args, expected)
- }
- ExprKind::Cast(e, t) => self.check_expr_cast(e, t, expr),
- ExprKind::Type(e, t) => {
- let ty = self.to_ty_saving_user_provided_ty(&t);
- self.check_expr_eq_type(&e, ty);
- ty
- }
- ExprKind::If(cond, then_expr, opt_else_expr) => {
- self.check_then_else(cond, then_expr, opt_else_expr, expr.span, expected)
- }
- ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected),
- ExprKind::Array(args) => self.check_expr_array(args, expected, expr),
- ExprKind::ConstBlock(ref anon_const) => {
- self.check_expr_const_block(anon_const, expected, expr)
- }
- ExprKind::Repeat(element, ref count) => {
- self.check_expr_repeat(element, count, expected, expr)
- }
- ExprKind::Tup(elts) => self.check_expr_tuple(elts, expected, expr),
- ExprKind::Struct(qpath, fields, ref base_expr) => {
- self.check_expr_struct(expr, expected, qpath, fields, base_expr)
- }
- ExprKind::Field(base, field) => self.check_field(expr, &base, field),
- ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr),
- ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src),
- hir::ExprKind::Err => tcx.ty_error(),
- }
- }
-
- fn check_expr_box(&self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>) -> Ty<'tcx> {
- let expected_inner = expected.to_option(self).map_or(NoExpectation, |ty| match ty.kind() {
- ty::Adt(def, _) if def.is_box() => Expectation::rvalue_hint(self, ty.boxed_ty()),
- _ => NoExpectation,
- });
- let referent_ty = self.check_expr_with_expectation(expr, expected_inner);
- self.require_type_is_sized(referent_ty, expr.span, traits::SizedBoxType);
- self.tcx.mk_box(referent_ty)
- }
-
- fn check_expr_unary(
- &self,
- unop: hir::UnOp,
- oprnd: &'tcx hir::Expr<'tcx>,
- expected: Expectation<'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- ) -> Ty<'tcx> {
- let tcx = self.tcx;
- let expected_inner = match unop {
- hir::UnOp::Not | hir::UnOp::Neg => expected,
- hir::UnOp::Deref => NoExpectation,
- };
- let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner);
-
- if !oprnd_t.references_error() {
- oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t);
- match unop {
- hir::UnOp::Deref => {
- if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) {
- oprnd_t = ty;
- } else {
- let mut err = type_error_struct!(
- tcx.sess,
- expr.span,
- oprnd_t,
- E0614,
- "type `{oprnd_t}` cannot be dereferenced",
- );
- let sp = tcx.sess.source_map().start_point(expr.span);
- if let Some(sp) =
- tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp)
- {
- tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp);
- }
- err.emit();
- oprnd_t = tcx.ty_error();
- }
- }
- hir::UnOp::Not => {
- let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner);
- // If it's builtin, we can reuse the type, this helps inference.
- if !(oprnd_t.is_integral() || *oprnd_t.kind() == ty::Bool) {
- oprnd_t = result;
- }
- }
- hir::UnOp::Neg => {
- let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner);
- // If it's builtin, we can reuse the type, this helps inference.
- if !oprnd_t.is_numeric() {
- oprnd_t = result;
- }
- }
- }
- }
- oprnd_t
- }
-
- fn check_expr_addr_of(
- &self,
- kind: hir::BorrowKind,
- mutbl: hir::Mutability,
- oprnd: &'tcx hir::Expr<'tcx>,
- expected: Expectation<'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- ) -> Ty<'tcx> {
- let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| {
- match ty.kind() {
- ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
- if oprnd.is_syntactic_place_expr() {
- // Places may legitimately have unsized types.
- // For example, dereferences of a fat pointer and
- // the last field of a struct can be unsized.
- ExpectHasType(*ty)
- } else {
- Expectation::rvalue_hint(self, *ty)
- }
- }
- _ => NoExpectation,
- }
- });
- let ty =
- self.check_expr_with_expectation_and_needs(&oprnd, hint, Needs::maybe_mut_place(mutbl));
-
- let tm = ty::TypeAndMut { ty, mutbl };
- match kind {
- _ if tm.ty.references_error() => self.tcx.ty_error(),
- hir::BorrowKind::Raw => {
- self.check_named_place_expr(oprnd);
- self.tcx.mk_ptr(tm)
- }
- hir::BorrowKind::Ref => {
- // Note: at this point, we cannot say what the best lifetime
- // is to use for resulting pointer. We want to use the
- // shortest lifetime possible so as to avoid spurious borrowck
- // errors. Moreover, the longest lifetime will depend on the
- // precise details of the value whose address is being taken
- // (and how long it is valid), which we don't know yet until
- // type inference is complete.
- //
- // Therefore, here we simply generate a region variable. The
- // region inferencer will then select a suitable value.
- // Finally, borrowck will infer the value of the region again,
- // this time with enough precision to check that the value
- // whose address was taken can actually be made to live as long
- // as it needs to live.
- let region = self.next_region_var(infer::AddrOfRegion(expr.span));
- self.tcx.mk_ref(region, tm)
- }
- }
- }
-
- /// Does this expression refer to a place that either:
- /// * Is based on a local or static.
- /// * Contains a dereference
- /// Note that the adjustments for the children of `expr` should already
- /// have been resolved.
- fn check_named_place_expr(&self, oprnd: &'tcx hir::Expr<'tcx>) {
- let is_named = oprnd.is_place_expr(|base| {
- // Allow raw borrows if there are any deref adjustments.
- //
- // const VAL: (i32,) = (0,);
- // const REF: &(i32,) = &(0,);
- //
- // &raw const VAL.0; // ERROR
- // &raw const REF.0; // OK, same as &raw const (*REF).0;
- //
- // This is maybe too permissive, since it allows
- // `let u = &raw const Box::new((1,)).0`, which creates an
- // immediately dangling raw pointer.
- self.typeck_results
- .borrow()
- .adjustments()
- .get(base.hir_id)
- .map_or(false, |x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
- });
- if !is_named {
- self.tcx.sess.emit_err(AddressOfTemporaryTaken { span: oprnd.span });
- }
- }
-
- fn check_lang_item_path(
- &self,
- lang_item: hir::LangItem,
- expr: &'tcx hir::Expr<'tcx>,
- hir_id: Option<hir::HirId>,
- ) -> Ty<'tcx> {
- self.resolve_lang_item_path(lang_item, expr.span, expr.hir_id, hir_id).1
- }
-
- pub(crate) fn check_expr_path(
- &self,
- qpath: &'tcx hir::QPath<'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- args: &'tcx [hir::Expr<'tcx>],
- ) -> Ty<'tcx> {
- let tcx = self.tcx;
- let (res, opt_ty, segs) =
- self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span);
- let ty = match res {
- Res::Err => {
- self.set_tainted_by_errors();
- tcx.ty_error()
- }
- Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) => {
- report_unexpected_variant_res(tcx, res, qpath, expr.span);
- tcx.ty_error()
- }
- _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0,
- };
-
- if let ty::FnDef(did, ..) = *ty.kind() {
- let fn_sig = ty.fn_sig(tcx);
- if tcx.fn_sig(did).abi() == RustIntrinsic && tcx.item_name(did) == sym::transmute {
- let from = fn_sig.inputs().skip_binder()[0];
- let to = fn_sig.output().skip_binder();
- // We defer the transmute to the end of typeck, once all inference vars have
- // been resolved or we errored. This is important as we can only check transmute
- // on concrete types, but the output type may not be known yet (it would only
- // be known if explicitly specified via turbofish).
- self.deferred_transmute_checks.borrow_mut().push((from, to, expr.span));
- }
- if !tcx.features().unsized_fn_params {
- // We want to remove some Sized bounds from std functions,
- // but don't want to expose the removal to stable Rust.
- // i.e., we don't want to allow
- //
- // ```rust
- // drop as fn(str);
- // ```
- //
- // to work in stable even if the Sized bound on `drop` is relaxed.
- for i in 0..fn_sig.inputs().skip_binder().len() {
- // We just want to check sizedness, so instead of introducing
- // placeholder lifetimes with probing, we just replace higher lifetimes
- // with fresh vars.
- let span = args.get(i).map(|a| a.span).unwrap_or(expr.span);
- let input = self.replace_bound_vars_with_fresh_vars(
- span,
- infer::LateBoundRegionConversionTime::FnCall,
- fn_sig.input(i),
- );
- self.require_type_is_sized_deferred(
- input,
- span,
- traits::SizedArgumentType(None),
- );
- }
- }
- // Here we want to prevent struct constructors from returning unsized types.
- // There were two cases this happened: fn pointer coercion in stable
- // and usual function call in presence of unsized_locals.
- // Also, as we just want to check sizedness, instead of introducing
- // placeholder lifetimes with probing, we just replace higher lifetimes
- // with fresh vars.
- let output = self.replace_bound_vars_with_fresh_vars(
- expr.span,
- infer::LateBoundRegionConversionTime::FnCall,
- fn_sig.output(),
- );
- self.require_type_is_sized_deferred(output, expr.span, traits::SizedReturnType);
- }
-
- // We always require that the type provided as the value for
- // a type parameter outlives the moment of instantiation.
- let substs = self.typeck_results.borrow().node_substs(expr.hir_id);
- self.add_wf_bounds(substs, expr);
-
- ty
- }
-
- fn check_expr_break(
- &self,
- destination: hir::Destination,
- expr_opt: Option<&'tcx hir::Expr<'tcx>>,
- expr: &'tcx hir::Expr<'tcx>,
- ) -> Ty<'tcx> {
- let tcx = self.tcx;
- if let Ok(target_id) = destination.target_id {
- let (e_ty, cause);
- if let Some(e) = expr_opt {
- // If this is a break with a value, we need to type-check
- // the expression. Get an expected type from the loop context.
- let opt_coerce_to = {
- // We should release `enclosing_breakables` before the `check_expr_with_hint`
- // below, so can't move this block of code to the enclosing scope and share
- // `ctxt` with the second `enclosing_breakables` borrow below.
- let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
- match enclosing_breakables.opt_find_breakable(target_id) {
- Some(ctxt) => ctxt.coerce.as_ref().map(|coerce| coerce.expected_ty()),
- None => {
- // Avoid ICE when `break` is inside a closure (#65383).
- return tcx.ty_error_with_message(
- expr.span,
- "break was outside loop, but no error was emitted",
- );
- }
- }
- };
-
- // If the loop context is not a `loop { }`, then break with
- // a value is illegal, and `opt_coerce_to` will be `None`.
- // Just set expectation to error in that case.
- let coerce_to = opt_coerce_to.unwrap_or_else(|| tcx.ty_error());
-
- // Recurse without `enclosing_breakables` borrowed.
- e_ty = self.check_expr_with_hint(e, coerce_to);
- cause = self.misc(e.span);
- } else {
- // Otherwise, this is a break *without* a value. That's
- // always legal, and is equivalent to `break ()`.
- e_ty = tcx.mk_unit();
- cause = self.misc(expr.span);
- }
-
- // Now that we have type-checked `expr_opt`, borrow
- // the `enclosing_loops` field and let's coerce the
- // type of `expr_opt` into what is expected.
- let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
- let Some(ctxt) = enclosing_breakables.opt_find_breakable(target_id) else {
- // Avoid ICE when `break` is inside a closure (#65383).
- return tcx.ty_error_with_message(
- expr.span,
- "break was outside loop, but no error was emitted",
- );
- };
-
- if let Some(ref mut coerce) = ctxt.coerce {
- if let Some(ref e) = expr_opt {
- coerce.coerce(self, &cause, e, e_ty);
- } else {
- assert!(e_ty.is_unit());
- let ty = coerce.expected_ty();
- coerce.coerce_forced_unit(
- self,
- &cause,
- &mut |mut err| {
- self.suggest_mismatched_types_on_tail(
- &mut err, expr, ty, e_ty, target_id,
- );
- if let Some(val) = ty_kind_suggestion(ty) {
- let label = destination
- .label
- .map(|l| format!(" {}", l.ident))
- .unwrap_or_else(String::new);
- err.span_suggestion(
- expr.span,
- "give it a value of the expected type",
- format!("break{label} {val}"),
- Applicability::HasPlaceholders,
- );
- }
- },
- false,
- );
- }
- } else {
- // If `ctxt.coerce` is `None`, we can just ignore
- // the type of the expression. This is because
- // either this was a break *without* a value, in
- // which case it is always a legal type (`()`), or
- // else an error would have been flagged by the
- // `loops` pass for using break with an expression
- // where you are not supposed to.
- assert!(expr_opt.is_none() || self.tcx.sess.has_errors().is_some());
- }
-
- // If we encountered a `break`, then (no surprise) it may be possible to break from the
- // loop... unless the value being returned from the loop diverges itself, e.g.
- // `break return 5` or `break loop {}`.
- ctxt.may_break |= !self.diverges.get().is_always();
-
- // the type of a `break` is always `!`, since it diverges
- tcx.types.never
- } else {
- // Otherwise, we failed to find the enclosing loop;
- // this can only happen if the `break` was not
- // inside a loop at all, which is caught by the
- // loop-checking pass.
- let err = self.tcx.ty_error_with_message(
- expr.span,
- "break was outside loop, but no error was emitted",
- );
-
- // We still need to assign a type to the inner expression to
- // prevent the ICE in #43162.
- if let Some(e) = expr_opt {
- self.check_expr_with_hint(e, err);
-
- // ... except when we try to 'break rust;'.
- // ICE this expression in particular (see #43162).
- if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind {
- if path.segments.len() == 1 && path.segments[0].ident.name == sym::rust {
- fatally_break_rust(self.tcx.sess);
- }
- }
- }
-
- // There was an error; make type-check fail.
- err
- }
- }
-
- fn check_expr_return(
- &self,
- expr_opt: Option<&'tcx hir::Expr<'tcx>>,
- expr: &'tcx hir::Expr<'tcx>,
- ) -> Ty<'tcx> {
- if self.ret_coercion.is_none() {
- let mut err = ReturnStmtOutsideOfFnBody {
- span: expr.span,
- encl_body_span: None,
- encl_fn_span: None,
- };
-
- let encl_item_id = self.tcx.hir().get_parent_item(expr.hir_id);
-
- if let Some(hir::Node::Item(hir::Item {
- kind: hir::ItemKind::Fn(..),
- span: encl_fn_span,
- ..
- }))
- | Some(hir::Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)),
- span: encl_fn_span,
- ..
- }))
- | Some(hir::Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Fn(..),
- span: encl_fn_span,
- ..
- })) = self.tcx.hir().find_by_def_id(encl_item_id)
- {
- // We are inside a function body, so reporting "return statement
- // outside of function body" needs an explanation.
-
- let encl_body_owner_id = self.tcx.hir().enclosing_body_owner(expr.hir_id);
-
- // If this didn't hold, we would not have to report an error in
- // the first place.
- assert_ne!(encl_item_id, encl_body_owner_id);
-
- let encl_body_id = self.tcx.hir().body_owned_by(encl_body_owner_id);
- let encl_body = self.tcx.hir().body(encl_body_id);
-
- err.encl_body_span = Some(encl_body.value.span);
- err.encl_fn_span = Some(*encl_fn_span);
- }
-
- self.tcx.sess.emit_err(err);
-
- if let Some(e) = expr_opt {
- // We still have to type-check `e` (issue #86188), but calling
- // `check_return_expr` only works inside fn bodies.
- self.check_expr(e);
- }
- } else if let Some(e) = expr_opt {
- if self.ret_coercion_span.get().is_none() {
- self.ret_coercion_span.set(Some(e.span));
- }
- self.check_return_expr(e, true);
- } else {
- let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut();
- if self.ret_coercion_span.get().is_none() {
- self.ret_coercion_span.set(Some(expr.span));
- }
- let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression);
- if let Some((fn_decl, _)) = self.get_fn_decl(expr.hir_id) {
- coercion.coerce_forced_unit(
- self,
- &cause,
- &mut |db| {
- let span = fn_decl.output.span();
- if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
- db.span_label(
- span,
- format!("expected `{snippet}` because of this return type"),
- );
- }
- },
- true,
- );
- } else {
- coercion.coerce_forced_unit(self, &cause, &mut |_| (), true);
- }
- }
- self.tcx.types.never
- }
-
- /// `explicit_return` is `true` if we're checking an explicit `return expr`,
- /// and `false` if we're checking a trailing expression.
- pub(super) fn check_return_expr(
- &self,
- return_expr: &'tcx hir::Expr<'tcx>,
- explicit_return: bool,
- ) {
- let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| {
- span_bug!(return_expr.span, "check_return_expr called outside fn body")
- });
-
- let ret_ty = ret_coercion.borrow().expected_ty();
- let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty);
- let mut span = return_expr.span;
- // Use the span of the trailing expression for our cause,
- // not the span of the entire function
- if !explicit_return {
- if let ExprKind::Block(body, _) = return_expr.kind && let Some(last_expr) = body.expr {
- span = last_expr.span;
- }
- }
- ret_coercion.borrow_mut().coerce(
- self,
- &self.cause(span, ObligationCauseCode::ReturnValue(return_expr.hir_id)),
- return_expr,
- return_expr_ty,
- );
-
- if self.return_type_has_opaque {
- // Point any obligations that were registered due to opaque type
- // inference at the return expression.
- self.select_obligations_where_possible(false, |errors| {
- self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty);
- });
- }
- }
-
- fn point_at_return_for_opaque_ty_error(
- &self,
- errors: &mut Vec<traits::FulfillmentError<'tcx>>,
- span: Span,
- return_expr_ty: Ty<'tcx>,
- ) {
- // Don't point at the whole block if it's empty
- if span == self.tcx.hir().span(self.body_id) {
- return;
- }
- for err in errors {
- let cause = &mut err.obligation.cause;
- if let ObligationCauseCode::OpaqueReturnType(None) = cause.code() {
- let new_cause = ObligationCause::new(
- cause.span,
- cause.body_id,
- ObligationCauseCode::OpaqueReturnType(Some((return_expr_ty, span))),
- );
- *cause = new_cause;
- }
- }
- }
-
- pub(crate) fn check_lhs_assignable(
- &self,
- lhs: &'tcx hir::Expr<'tcx>,
- err_code: &'static str,
- op_span: Span,
- adjust_err: impl FnOnce(&mut DiagnosticBuilder<'tcx, ErrorGuaranteed>),
- ) {
- if lhs.is_syntactic_place_expr() {
- return;
- }
-
- // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set.
- let mut err = self.tcx.sess.struct_span_err_with_code(
- op_span,
- "invalid left-hand side of assignment",
- DiagnosticId::Error(err_code.into()),
- );
- err.span_label(lhs.span, "cannot assign to this expression");
-
- self.comes_from_while_condition(lhs.hir_id, |expr| {
- err.span_suggestion_verbose(
- expr.span.shrink_to_lo(),
- "you might have meant to use pattern destructuring",
- "let ",
- Applicability::MachineApplicable,
- );
- });
-
- adjust_err(&mut err);
-
- err.emit();
- }
-
- // Check if an expression `original_expr_id` comes from the condition of a while loop,
- // as opposed from the body of a while loop, which we can naively check by iterating
- // parents until we find a loop...
- pub(super) fn comes_from_while_condition(
- &self,
- original_expr_id: HirId,
- then: impl FnOnce(&hir::Expr<'_>),
- ) {
- let mut parent = self.tcx.hir().get_parent_node(original_expr_id);
- while let Some(node) = self.tcx.hir().find(parent) {
- match node {
- hir::Node::Expr(hir::Expr {
- kind:
- hir::ExprKind::Loop(
- hir::Block {
- expr:
- Some(hir::Expr {
- kind:
- hir::ExprKind::Match(expr, ..) | hir::ExprKind::If(expr, ..),
- ..
- }),
- ..
- },
- _,
- hir::LoopSource::While,
- _,
- ),
- ..
- }) => {
- // Check if our original expression is a child of the condition of a while loop
- let expr_is_ancestor = std::iter::successors(Some(original_expr_id), |id| {
- self.tcx.hir().find_parent_node(*id)
- })
- .take_while(|id| *id != parent)
- .any(|id| id == expr.hir_id);
- // if it is, then we have a situation like `while Some(0) = value.get(0) {`,
- // where `while let` was more likely intended.
- if expr_is_ancestor {
- then(expr);
- }
- break;
- }
- hir::Node::Item(_)
- | hir::Node::ImplItem(_)
- | hir::Node::TraitItem(_)
- | hir::Node::Crate(_) => break,
- _ => {
- parent = self.tcx.hir().get_parent_node(parent);
- }
- }
- }
- }
-
- // A generic function for checking the 'then' and 'else' clauses in an 'if'
- // or 'if-else' expression.
- fn check_then_else(
- &self,
- cond_expr: &'tcx hir::Expr<'tcx>,
- then_expr: &'tcx hir::Expr<'tcx>,
- opt_else_expr: Option<&'tcx hir::Expr<'tcx>>,
- sp: Span,
- orig_expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- let cond_ty = self.check_expr_has_type_or_error(cond_expr, self.tcx.types.bool, |_| {});
-
- self.warn_if_unreachable(
- cond_expr.hir_id,
- then_expr.span,
- "block in `if` or `while` expression",
- );
-
- let cond_diverges = self.diverges.get();
- self.diverges.set(Diverges::Maybe);
-
- let expected = orig_expected.adjust_for_branches(self);
- let then_ty = self.check_expr_with_expectation(then_expr, expected);
- let then_diverges = self.diverges.get();
- self.diverges.set(Diverges::Maybe);
-
- // We've already taken the expected type's preferences
- // into account when typing the `then` branch. To figure
- // out the initial shot at a LUB, we thus only consider
- // `expected` if it represents a *hard* constraint
- // (`only_has_type`); otherwise, we just go with a
- // fresh type variable.
- let coerce_to_ty = expected.coercion_target_type(self, sp);
- let mut coerce: DynamicCoerceMany<'_> = CoerceMany::new(coerce_to_ty);
-
- coerce.coerce(self, &self.misc(sp), then_expr, then_ty);
-
- if let Some(else_expr) = opt_else_expr {
- let else_ty = self.check_expr_with_expectation(else_expr, expected);
- let else_diverges = self.diverges.get();
-
- let opt_suggest_box_span = self.opt_suggest_box_span(else_ty, orig_expected);
- let if_cause = self.if_cause(
- sp,
- cond_expr.span,
- then_expr,
- else_expr,
- then_ty,
- else_ty,
- opt_suggest_box_span,
- );
-
- coerce.coerce(self, &if_cause, else_expr, else_ty);
-
- // We won't diverge unless both branches do (or the condition does).
- self.diverges.set(cond_diverges | then_diverges & else_diverges);
- } else {
- self.if_fallback_coercion(sp, then_expr, &mut coerce);
-
- // If the condition is false we can't diverge.
- self.diverges.set(cond_diverges);
- }
-
- let result_ty = coerce.complete(self);
- if cond_ty.references_error() { self.tcx.ty_error() } else { result_ty }
- }
-
- /// Type check assignment expression `expr` of form `lhs = rhs`.
- /// The expected type is `()` and is passed to the function for the purposes of diagnostics.
- fn check_expr_assign(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- expected: Expectation<'tcx>,
- lhs: &'tcx hir::Expr<'tcx>,
- rhs: &'tcx hir::Expr<'tcx>,
- span: Span,
- ) -> Ty<'tcx> {
- let expected_ty = expected.coercion_target_type(self, expr.span);
- if expected_ty == self.tcx.types.bool {
- // The expected type is `bool` but this will result in `()` so we can reasonably
- // say that the user intended to write `lhs == rhs` instead of `lhs = rhs`.
- // The likely cause of this is `if foo = bar { .. }`.
- let actual_ty = self.tcx.mk_unit();
- let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap();
- let lhs_ty = self.check_expr(&lhs);
- let rhs_ty = self.check_expr(&rhs);
- let (applicability, eq) = if self.can_coerce(rhs_ty, lhs_ty) {
- (Applicability::MachineApplicable, true)
- } else {
- (Applicability::MaybeIncorrect, false)
- };
- if !lhs.is_syntactic_place_expr()
- && lhs.is_approximately_pattern()
- && !matches!(lhs.kind, hir::ExprKind::Lit(_))
- {
- // Do not suggest `if let x = y` as `==` is way more likely to be the intention.
- let hir = self.tcx.hir();
- if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) =
- hir.get(hir.get_parent_node(hir.get_parent_node(expr.hir_id)))
- {
- err.span_suggestion_verbose(
- expr.span.shrink_to_lo(),
- "you might have meant to use pattern matching",
- "let ",
- applicability,
- );
- };
- }
- if eq {
- err.span_suggestion_verbose(
- span,
- "you might have meant to compare for equality",
- "==",
- applicability,
- );
- }
-
- // If the assignment expression itself is ill-formed, don't
- // bother emitting another error
- if lhs_ty.references_error() || rhs_ty.references_error() {
- err.delay_as_bug()
- } else {
- err.emit();
- }
- return self.tcx.ty_error();
- }
-
- let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
-
- let suggest_deref_binop = |err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
- rhs_ty: Ty<'tcx>| {
- if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
- // Can only assign if the type is sized, so if `DerefMut` yields a type that is
- // unsized, do not suggest dereferencing it.
- let lhs_deref_ty_is_sized = self
- .infcx
- .type_implements_trait(
- self.tcx.lang_items().sized_trait().unwrap(),
- lhs_deref_ty,
- ty::List::empty(),
- self.param_env,
- )
- .may_apply();
- if lhs_deref_ty_is_sized && self.can_coerce(rhs_ty, lhs_deref_ty) {
- err.span_suggestion_verbose(
- lhs.span.shrink_to_lo(),
- "consider dereferencing here to assign to the mutably borrowed value",
- "*",
- Applicability::MachineApplicable,
- );
- }
- }
- };
-
- self.check_lhs_assignable(lhs, "E0070", span, |err| {
- let rhs_ty = self.check_expr(&rhs);
- suggest_deref_binop(err, rhs_ty);
- });
-
- // This is (basically) inlined `check_expr_coercable_to_type`, but we want
- // to suggest an additional fixup here in `suggest_deref_binop`.
- let rhs_ty = self.check_expr_with_hint(&rhs, lhs_ty);
- if let (_, Some(mut diag)) =
- self.demand_coerce_diag(rhs, rhs_ty, lhs_ty, Some(lhs), AllowTwoPhase::No)
- {
- suggest_deref_binop(&mut diag, rhs_ty);
- diag.emit();
- }
-
- self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized);
-
- if lhs_ty.references_error() || rhs_ty.references_error() {
- self.tcx.ty_error()
- } else {
- self.tcx.mk_unit()
- }
- }
-
- pub(super) fn check_expr_let(&self, let_expr: &'tcx hir::Let<'tcx>) -> Ty<'tcx> {
- // for let statements, this is done in check_stmt
- let init = let_expr.init;
- self.warn_if_unreachable(init.hir_id, init.span, "block in `let` expression");
- // otherwise check exactly as a let statement
- self.check_decl(let_expr.into());
- // but return a bool, for this is a boolean expression
- self.tcx.types.bool
- }
-
- fn check_expr_loop(
- &self,
- body: &'tcx hir::Block<'tcx>,
- source: hir::LoopSource,
- expected: Expectation<'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- ) -> Ty<'tcx> {
- let coerce = match source {
- // you can only use break with a value from a normal `loop { }`
- hir::LoopSource::Loop => {
- let coerce_to = expected.coercion_target_type(self, body.span);
- Some(CoerceMany::new(coerce_to))
- }
-
- hir::LoopSource::While | hir::LoopSource::ForLoop => None,
- };
-
- let ctxt = BreakableCtxt {
- coerce,
- may_break: false, // Will get updated if/when we find a `break`.
- };
-
- let (ctxt, ()) = self.with_breakable_ctxt(expr.hir_id, ctxt, || {
- self.check_block_no_value(&body);
- });
-
- if ctxt.may_break {
- // No way to know whether it's diverging because
- // of a `break` or an outer `break` or `return`.
- self.diverges.set(Diverges::Maybe);
- }
-
- // If we permit break with a value, then result type is
- // the LUB of the breaks (possibly ! if none); else, it
- // is nil. This makes sense because infinite loops
- // (which would have type !) are only possible iff we
- // permit break with a value [1].
- if ctxt.coerce.is_none() && !ctxt.may_break {
- // [1]
- self.tcx.sess.delay_span_bug(body.span, "no coercion, but loop may not break");
- }
- ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| self.tcx.mk_unit())
- }
-
- /// Checks a method call.
- fn check_method_call(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- segment: &hir::PathSegment<'_>,
- args: &'tcx [hir::Expr<'tcx>],
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- let rcvr = &args[0];
- let rcvr_t = self.check_expr(&rcvr);
- // no need to check for bot/err -- callee does that
- let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t);
- let span = segment.ident.span;
-
- let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr, args) {
- Ok(method) => {
- // We could add a "consider `foo::<params>`" suggestion here, but I wasn't able to
- // trigger this codepath causing `structurally_resolved_type` to emit an error.
-
- self.write_method_call(expr.hir_id, method);
- Ok(method)
- }
- Err(error) => {
- if segment.ident.name != kw::Empty {
- if let Some(mut err) = self.report_method_error(
- span,
- rcvr_t,
- segment.ident,
- SelfSource::MethodCall(&args[0]),
- error,
- Some(args),
- ) {
- err.emit();
- }
- }
- Err(())
- }
- };
-
- // Call the generic checker.
- self.check_method_argument_types(
- span,
- expr,
- method,
- &args[1..],
- DontTupleArguments,
- expected,
- )
- }
-
- fn check_expr_cast(
- &self,
- e: &'tcx hir::Expr<'tcx>,
- t: &'tcx hir::Ty<'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- ) -> Ty<'tcx> {
- // Find the type of `e`. Supply hints based on the type we are casting to,
- // if appropriate.
- let t_cast = self.to_ty_saving_user_provided_ty(t);
- let t_cast = self.resolve_vars_if_possible(t_cast);
- let t_expr = self.check_expr_with_expectation(e, ExpectCastableToType(t_cast));
- let t_expr = self.resolve_vars_if_possible(t_expr);
-
- // Eagerly check for some obvious errors.
- if t_expr.references_error() || t_cast.references_error() {
- self.tcx.ty_error()
- } else {
- // Defer other checks until we're done type checking.
- let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
- match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) {
- Ok(cast_check) => {
- debug!(
- "check_expr_cast: deferring cast from {:?} to {:?}: {:?}",
- t_cast, t_expr, cast_check,
- );
- deferred_cast_checks.push(cast_check);
- t_cast
- }
- Err(_) => self.tcx.ty_error(),
- }
- }
- }
-
- fn check_expr_array(
- &self,
- args: &'tcx [hir::Expr<'tcx>],
- expected: Expectation<'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- ) -> Ty<'tcx> {
- let element_ty = if !args.is_empty() {
- let coerce_to = expected
- .to_option(self)
- .and_then(|uty| match *uty.kind() {
- ty::Array(ty, _) | ty::Slice(ty) => Some(ty),
- _ => None,
- })
- .unwrap_or_else(|| {
- self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::TypeInference,
- span: expr.span,
- })
- });
- let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
- assert_eq!(self.diverges.get(), Diverges::Maybe);
- for e in args {
- let e_ty = self.check_expr_with_hint(e, coerce_to);
- let cause = self.misc(e.span);
- coerce.coerce(self, &cause, e, e_ty);
- }
- coerce.complete(self)
- } else {
- self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::TypeInference,
- span: expr.span,
- })
- };
- self.tcx.mk_array(element_ty, args.len() as u64)
- }
-
- fn check_expr_const_block(
- &self,
- anon_const: &'tcx hir::AnonConst,
- expected: Expectation<'tcx>,
- _expr: &'tcx hir::Expr<'tcx>,
- ) -> Ty<'tcx> {
- let body = self.tcx.hir().body(anon_const.body);
-
- // Create a new function context.
- let fcx = FnCtxt::new(self, self.param_env.with_const(), body.value.hir_id);
- crate::check::GatherLocalsVisitor::new(&fcx).visit_body(body);
-
- let ty = fcx.check_expr_with_expectation(&body.value, expected);
- fcx.require_type_is_sized(ty, body.value.span, traits::ConstSized);
- fcx.write_ty(anon_const.hir_id, ty);
- ty
- }
-
- fn check_expr_repeat(
- &self,
- element: &'tcx hir::Expr<'tcx>,
- count: &'tcx hir::ArrayLen,
- expected: Expectation<'tcx>,
- _expr: &'tcx hir::Expr<'tcx>,
- ) -> Ty<'tcx> {
- let tcx = self.tcx;
- let count = self.array_length_to_const(count);
-
- let uty = match expected {
- ExpectHasType(uty) => match *uty.kind() {
- ty::Array(ty, _) | ty::Slice(ty) => Some(ty),
- _ => None,
- },
- _ => None,
- };
-
- let (element_ty, t) = match uty {
- Some(uty) => {
- self.check_expr_coercable_to_type(&element, uty, None);
- (uty, uty)
- }
- None => {
- let ty = self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::MiscVariable,
- span: element.span,
- });
- let element_ty = self.check_expr_has_type_or_error(&element, ty, |_| {});
- (element_ty, ty)
- }
- };
-
- if element_ty.references_error() {
- return tcx.ty_error();
- }
-
- self.check_repeat_element_needs_copy_bound(element, count, element_ty);
-
- tcx.mk_ty(ty::Array(t, count))
- }
-
- fn check_repeat_element_needs_copy_bound(
- &self,
- element: &hir::Expr<'_>,
- count: ty::Const<'tcx>,
- element_ty: Ty<'tcx>,
- ) {
- let tcx = self.tcx;
- // Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy.
- match &element.kind {
- hir::ExprKind::ConstBlock(..) => return,
- hir::ExprKind::Path(qpath) => {
- let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id);
- if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res
- {
- return;
- }
- }
- _ => {}
- }
- // If someone calls a const fn, they can extract that call out into a separate constant (or a const
- // block in the future), so we check that to tell them that in the diagnostic. Does not affect typeck.
- let is_const_fn = match element.kind {
- hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
- ty::FnDef(def_id, _) => tcx.is_const_fn(def_id),
- _ => false,
- },
- _ => false,
- };
-
- // If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we
- // don't copy that one element, we move it. Only check for Copy if the length is larger.
- if count.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) {
- let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
- let code = traits::ObligationCauseCode::RepeatElementCopy { is_const_fn };
- self.require_type_meets(element_ty, element.span, code, lang_item);
- }
- }
-
- fn check_expr_tuple(
- &self,
- elts: &'tcx [hir::Expr<'tcx>],
- expected: Expectation<'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- ) -> Ty<'tcx> {
- let flds = expected.only_has_type(self).and_then(|ty| {
- let ty = self.resolve_vars_with_obligations(ty);
- match ty.kind() {
- ty::Tuple(flds) => Some(&flds[..]),
- _ => None,
- }
- });
-
- let elt_ts_iter = elts.iter().enumerate().map(|(i, e)| match flds {
- Some(fs) if i < fs.len() => {
- let ety = fs[i];
- self.check_expr_coercable_to_type(&e, ety, None);
- ety
- }
- _ => self.check_expr_with_expectation(&e, NoExpectation),
- });
- let tuple = self.tcx.mk_tup(elt_ts_iter);
- if tuple.references_error() {
- self.tcx.ty_error()
- } else {
- self.require_type_is_sized(tuple, expr.span, traits::TupleInitializerSized);
- tuple
- }
- }
-
- fn check_expr_struct(
- &self,
- expr: &hir::Expr<'_>,
- expected: Expectation<'tcx>,
- qpath: &QPath<'_>,
- fields: &'tcx [hir::ExprField<'tcx>],
- base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
- ) -> Ty<'tcx> {
- // Find the relevant variant
- let Some((variant, adt_ty)) = self.check_struct_path(qpath, expr.hir_id) else {
- self.check_struct_fields_on_error(fields, base_expr);
- return self.tcx.ty_error();
- };
-
- // Prohibit struct expressions when non-exhaustive flag is set.
- let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type");
- if !adt.did().is_local() && variant.is_field_list_non_exhaustive() {
- self.tcx
- .sess
- .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() });
- }
-
- self.check_expr_struct_fields(
- adt_ty,
- expected,
- expr.hir_id,
- qpath.span(),
- variant,
- fields,
- base_expr,
- expr.span,
- );
-
- self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized);
- adt_ty
- }
-
- fn check_expr_struct_fields(
- &self,
- adt_ty: Ty<'tcx>,
- expected: Expectation<'tcx>,
- expr_id: hir::HirId,
- span: Span,
- variant: &'tcx ty::VariantDef,
- ast_fields: &'tcx [hir::ExprField<'tcx>],
- base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
- expr_span: Span,
- ) {
- let tcx = self.tcx;
-
- let expected_inputs =
- self.expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty]);
- let adt_ty_hint = if let Some(expected_inputs) = expected_inputs {
- expected_inputs.get(0).cloned().unwrap_or(adt_ty)
- } else {
- adt_ty
- };
- // re-link the regions that EIfEO can erase.
- self.demand_eqtype(span, adt_ty_hint, adt_ty);
-
- let ty::Adt(adt, substs) = adt_ty.kind() else {
- span_bug!(span, "non-ADT passed to check_expr_struct_fields");
- };
- let adt_kind = adt.adt_kind();
-
- let mut remaining_fields = variant
- .fields
- .iter()
- .enumerate()
- .map(|(i, field)| (field.ident(tcx).normalize_to_macros_2_0(), (i, field)))
- .collect::<FxHashMap<_, _>>();
-
- let mut seen_fields = FxHashMap::default();
-
- let mut error_happened = false;
-
- // Type-check each field.
- for field in ast_fields {
- let ident = tcx.adjust_ident(field.ident, variant.def_id);
- let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) {
- seen_fields.insert(ident, field.span);
- self.write_field_index(field.hir_id, i);
-
- // We don't look at stability attributes on
- // struct-like enums (yet...), but it's definitely not
- // a bug to have constructed one.
- if adt_kind != AdtKind::Enum {
- tcx.check_stability(v_field.did, Some(expr_id), field.span, None);
- }
-
- self.field_ty(field.span, v_field, substs)
- } else {
- error_happened = true;
- if let Some(prev_span) = seen_fields.get(&ident) {
- tcx.sess.emit_err(FieldMultiplySpecifiedInInitializer {
- span: field.ident.span,
- prev_span: *prev_span,
- ident,
- });
- } else {
- self.report_unknown_field(
- adt_ty,
- variant,
- field,
- ast_fields,
- adt.variant_descr(),
- expr_span,
- );
- }
-
- tcx.ty_error()
- };
-
- // Make sure to give a type to the field even if there's
- // an error, so we can continue type-checking.
- self.check_expr_coercable_to_type(&field.expr, field_type, None);
- }
-
- // Make sure the programmer specified correct number of fields.
- if adt_kind == AdtKind::Union {
- if ast_fields.len() != 1 {
- struct_span_err!(
- tcx.sess,
- span,
- E0784,
- "union expressions should have exactly one field",
- )
- .emit();
- }
- }
-
- // If check_expr_struct_fields hit an error, do not attempt to populate
- // the fields with the base_expr. This could cause us to hit errors later
- // when certain fields are assumed to exist that in fact do not.
- if error_happened {
- return;
- }
-
- if let Some(base_expr) = base_expr {
- // FIXME: We are currently creating two branches here in order to maintain
- // consistency. But they should be merged as much as possible.
- let fru_tys = if self.tcx.features().type_changing_struct_update {
- if adt.is_struct() {
- // Make some fresh substitutions for our ADT type.
- let fresh_substs = self.fresh_substs_for_item(base_expr.span, adt.did());
- // We do subtyping on the FRU fields first, so we can
- // learn exactly what types we expect the base expr
- // needs constrained to be compatible with the struct
- // type we expect from the expectation value.
- let fru_tys = variant
- .fields
- .iter()
- .map(|f| {
- let fru_ty = self.normalize_associated_types_in(
- expr_span,
- self.field_ty(base_expr.span, f, fresh_substs),
- );
- let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id);
- if let Some(_) = remaining_fields.remove(&ident) {
- let target_ty = self.field_ty(base_expr.span, f, substs);
- let cause = self.misc(base_expr.span);
- match self.at(&cause, self.param_env).sup(target_ty, fru_ty) {
- Ok(InferOk { obligations, value: () }) => {
- self.register_predicates(obligations)
- }
- Err(_) => {
- // This should never happen, since we're just subtyping the
- // remaining_fields, but it's fine to emit this, I guess.
- self.report_mismatched_types(
- &cause,
- target_ty,
- fru_ty,
- FieldMisMatch(variant.name, ident.name),
- )
- .emit();
- }
- }
- }
- self.resolve_vars_if_possible(fru_ty)
- })
- .collect();
- // The use of fresh substs that we have subtyped against
- // our base ADT type's fields allows us to guide inference
- // along so that, e.g.
- // ```
- // MyStruct<'a, F1, F2, const C: usize> {
- // f: F1,
- // // Other fields that reference `'a`, `F2`, and `C`
- // }
- //
- // let x = MyStruct {
- // f: 1usize,
- // ..other_struct
- // };
- // ```
- // will have the `other_struct` expression constrained to
- // `MyStruct<'a, _, F2, C>`, as opposed to just `_`...
- // This is important to allow coercions to happen in
- // `other_struct` itself. See `coerce-in-base-expr.rs`.
- let fresh_base_ty = self.tcx.mk_adt(*adt, fresh_substs);
- self.check_expr_has_type_or_error(
- base_expr,
- self.resolve_vars_if_possible(fresh_base_ty),
- |_| {},
- );
- fru_tys
- } else {
- // Check the base_expr, regardless of a bad expected adt_ty, so we can get
- // type errors on that expression, too.
- self.check_expr(base_expr);
- self.tcx
- .sess
- .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
- return;
- }
- } else {
- self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {
- let base_ty = self.typeck_results.borrow().expr_ty(*base_expr);
- let same_adt = match (adt_ty.kind(), base_ty.kind()) {
- (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt => true,
- _ => false,
- };
- if self.tcx.sess.is_nightly_build() && same_adt {
- feature_err(
- &self.tcx.sess.parse_sess,
- sym::type_changing_struct_update,
- base_expr.span,
- "type changing struct updating is experimental",
- )
- .emit();
- }
- });
- match adt_ty.kind() {
- ty::Adt(adt, substs) if adt.is_struct() => variant
- .fields
- .iter()
- .map(|f| {
- self.normalize_associated_types_in(expr_span, f.ty(self.tcx, substs))
- })
- .collect(),
- _ => {
- self.tcx
- .sess
- .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
- return;
- }
- }
- };
- self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
- } else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() {
- debug!(?remaining_fields);
- let private_fields: Vec<&ty::FieldDef> = variant
- .fields
- .iter()
- .filter(|field| {
- !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
- })
- .collect();
-
- if !private_fields.is_empty() {
- self.report_private_fields(adt_ty, span, private_fields, ast_fields);
- } else {
- self.report_missing_fields(
- adt_ty,
- span,
- remaining_fields,
- variant,
- ast_fields,
- substs,
- );
- }
- }
- }
-
- fn check_struct_fields_on_error(
- &self,
- fields: &'tcx [hir::ExprField<'tcx>],
- base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
- ) {
- for field in fields {
- self.check_expr(&field.expr);
- }
- if let Some(base) = *base_expr {
- self.check_expr(&base);
- }
- }
-
- /// Report an error for a struct field expression when there are fields which aren't provided.
- ///
- /// ```text
- /// error: missing field `you_can_use_this_field` in initializer of `foo::Foo`
- /// --> src/main.rs:8:5
- /// |
- /// 8 | foo::Foo {};
- /// | ^^^^^^^^ missing `you_can_use_this_field`
- ///
- /// error: aborting due to previous error
- /// ```
- fn report_missing_fields(
- &self,
- adt_ty: Ty<'tcx>,
- span: Span,
- remaining_fields: FxHashMap<Ident, (usize, &ty::FieldDef)>,
- variant: &'tcx ty::VariantDef,
- ast_fields: &'tcx [hir::ExprField<'tcx>],
- substs: SubstsRef<'tcx>,
- ) {
- let len = remaining_fields.len();
-
- let mut displayable_field_names: Vec<&str> =
- remaining_fields.keys().map(|ident| ident.as_str()).collect();
- // sorting &str primitives here, sort_unstable is ok
- displayable_field_names.sort_unstable();
-
- let mut truncated_fields_error = String::new();
- let remaining_fields_names = match &displayable_field_names[..] {
- [field1] => format!("`{}`", field1),
- [field1, field2] => format!("`{field1}` and `{field2}`"),
- [field1, field2, field3] => format!("`{field1}`, `{field2}` and `{field3}`"),
- _ => {
- truncated_fields_error =
- format!(" and {} other field{}", len - 3, pluralize!(len - 3));
- displayable_field_names
- .iter()
- .take(3)
- .map(|n| format!("`{n}`"))
- .collect::<Vec<_>>()
- .join(", ")
- }
- };
-
- let mut err = struct_span_err!(
- self.tcx.sess,
- span,
- E0063,
- "missing field{} {}{} in initializer of `{}`",
- pluralize!(len),
- remaining_fields_names,
- truncated_fields_error,
- adt_ty
- );
- err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}"));
-
- // If the last field is a range literal, but it isn't supposed to be, then they probably
- // meant to use functional update syntax.
- //
- // I don't use 'is_range_literal' because only double-sided, half-open ranges count.
- if let Some((
- last,
- ExprKind::Struct(
- QPath::LangItem(LangItem::Range, ..),
- &[ref range_start, ref range_end],
- _,
- ),
- )) = ast_fields.last().map(|last| (last, &last.expr.kind)) &&
- let variant_field =
- variant.fields.iter().find(|field| field.ident(self.tcx) == last.ident) &&
- let range_def_id = self.tcx.lang_items().range_struct() &&
- variant_field
- .and_then(|field| field.ty(self.tcx, substs).ty_adt_def())
- .map(|adt| adt.did())
- != range_def_id
- {
- let instead = self
- .tcx
- .sess
- .source_map()
- .span_to_snippet(range_end.expr.span)
- .map(|s| format!(" from `{s}`"))
- .unwrap_or_default();
- err.span_suggestion(
- range_start.span.shrink_to_hi(),
- &format!("to set the remaining fields{instead}, separate the last named field with a comma"),
- ",",
- Applicability::MaybeIncorrect,
- );
- }
-
- err.emit();
- }
-
- /// Report an error for a struct field expression when there are invisible fields.
- ///
- /// ```text
- /// error: cannot construct `Foo` with struct literal syntax due to private fields
- /// --> src/main.rs:8:5
- /// |
- /// 8 | foo::Foo {};
- /// | ^^^^^^^^
- ///
- /// error: aborting due to previous error
- /// ```
- fn report_private_fields(
- &self,
- adt_ty: Ty<'tcx>,
- span: Span,
- private_fields: Vec<&ty::FieldDef>,
- used_fields: &'tcx [hir::ExprField<'tcx>],
- ) {
- let mut err = self.tcx.sess.struct_span_err(
- span,
- &format!(
- "cannot construct `{adt_ty}` with struct literal syntax due to private fields",
- ),
- );
- let (used_private_fields, remaining_private_fields): (
- Vec<(Symbol, Span, bool)>,
- Vec<(Symbol, Span, bool)>,
- ) = private_fields
- .iter()
- .map(|field| {
- match used_fields.iter().find(|used_field| field.name == used_field.ident.name) {
- Some(used_field) => (field.name, used_field.span, true),
- None => (field.name, self.tcx.def_span(field.did), false),
- }
- })
- .partition(|field| field.2);
- err.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field");
- if !remaining_private_fields.is_empty() {
- let remaining_private_fields_len = remaining_private_fields.len();
- let names = match &remaining_private_fields
- .iter()
- .map(|(name, _, _)| name)
- .collect::<Vec<_>>()[..]
- {
- _ if remaining_private_fields_len > 6 => String::new(),
- [name] => format!("`{name}` "),
- [names @ .., last] => {
- let names = names.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
- format!("{} and `{last}` ", names.join(", "))
- }
- [] => unreachable!(),
- };
- err.note(format!(
- "... and other private field{s} {names}that {were} not provided",
- s = pluralize!(remaining_private_fields_len),
- were = pluralize!("was", remaining_private_fields_len),
- ));
- }
- err.emit();
- }
-
- fn report_unknown_field(
- &self,
- ty: Ty<'tcx>,
- variant: &'tcx ty::VariantDef,
- field: &hir::ExprField<'_>,
- skip_fields: &[hir::ExprField<'_>],
- kind_name: &str,
- expr_span: Span,
- ) {
- if variant.is_recovered() {
- self.set_tainted_by_errors();
- return;
- }
- let mut err = self.type_error_struct_with_diag(
- field.ident.span,
- |actual| match ty.kind() {
- ty::Adt(adt, ..) if adt.is_enum() => struct_span_err!(
- self.tcx.sess,
- field.ident.span,
- E0559,
- "{} `{}::{}` has no field named `{}`",
- kind_name,
- actual,
- variant.name,
- field.ident
- ),
- _ => struct_span_err!(
- self.tcx.sess,
- field.ident.span,
- E0560,
- "{} `{}` has no field named `{}`",
- kind_name,
- actual,
- field.ident
- ),
- },
- ty,
- );
-
- let variant_ident_span = self.tcx.def_ident_span(variant.def_id).unwrap();
- match variant.ctor_kind {
- CtorKind::Fn => match ty.kind() {
- ty::Adt(adt, ..) if adt.is_enum() => {
- err.span_label(
- variant_ident_span,
- format!(
- "`{adt}::{variant}` defined here",
- adt = ty,
- variant = variant.name,
- ),
- );
- err.span_label(field.ident.span, "field does not exist");
- err.span_suggestion_verbose(
- expr_span,
- &format!(
- "`{adt}::{variant}` is a tuple {kind_name}, use the appropriate syntax",
- adt = ty,
- variant = variant.name,
- ),
- format!(
- "{adt}::{variant}(/* fields */)",
- adt = ty,
- variant = variant.name,
- ),
- Applicability::HasPlaceholders,
- );
- }
- _ => {
- err.span_label(variant_ident_span, format!("`{adt}` defined here", adt = ty));
- err.span_label(field.ident.span, "field does not exist");
- err.span_suggestion_verbose(
- expr_span,
- &format!(
- "`{adt}` is a tuple {kind_name}, use the appropriate syntax",
- adt = ty,
- kind_name = kind_name,
- ),
- format!("{adt}(/* fields */)", adt = ty),
- Applicability::HasPlaceholders,
- );
- }
- },
- _ => {
- // prevent all specified fields from being suggested
- let skip_fields = skip_fields.iter().map(|x| x.ident.name);
- if let Some(field_name) = self.suggest_field_name(
- variant,
- field.ident.name,
- skip_fields.collect(),
- expr_span,
- ) {
- err.span_suggestion(
- field.ident.span,
- "a field with a similar name exists",
- field_name,
- Applicability::MaybeIncorrect,
- );
- } else {
- match ty.kind() {
- ty::Adt(adt, ..) => {
- if adt.is_enum() {
- err.span_label(
- field.ident.span,
- format!("`{}::{}` does not have this field", ty, variant.name),
- );
- } else {
- err.span_label(
- field.ident.span,
- format!("`{ty}` does not have this field"),
- );
- }
- let available_field_names =
- self.available_field_names(variant, expr_span);
- if !available_field_names.is_empty() {
- err.note(&format!(
- "available fields are: {}",
- self.name_series_display(available_field_names)
- ));
- }
- }
- _ => bug!("non-ADT passed to report_unknown_field"),
- }
- };
- }
- }
- err.emit();
- }
-
- // Return a hint about the closest match in field names
- fn suggest_field_name(
- &self,
- variant: &'tcx ty::VariantDef,
- field: Symbol,
- skip: Vec<Symbol>,
- // The span where stability will be checked
- span: Span,
- ) -> Option<Symbol> {
- let names = variant
- .fields
- .iter()
- .filter_map(|field| {
- // ignore already set fields and private fields from non-local crates
- // and unstable fields.
- if skip.iter().any(|&x| x == field.name)
- || (!variant.def_id.is_local() && !field.vis.is_public())
- || matches!(
- self.tcx.eval_stability(field.did, None, span, None),
- stability::EvalResult::Deny { .. }
- )
- {
- None
- } else {
- Some(field.name)
- }
- })
- .collect::<Vec<Symbol>>();
-
- find_best_match_for_name(&names, field, None)
- }
-
- fn available_field_names(
- &self,
- variant: &'tcx ty::VariantDef,
- access_span: Span,
- ) -> Vec<Symbol> {
- variant
- .fields
- .iter()
- .filter(|field| {
- let def_scope = self
- .tcx
- .adjust_ident_and_get_scope(field.ident(self.tcx), variant.def_id, self.body_id)
- .1;
- field.vis.is_accessible_from(def_scope, self.tcx)
- && !matches!(
- self.tcx.eval_stability(field.did, None, access_span, None),
- stability::EvalResult::Deny { .. }
- )
- })
- .filter(|field| !self.tcx.is_doc_hidden(field.did))
- .map(|field| field.name)
- .collect()
- }
-
- fn name_series_display(&self, names: Vec<Symbol>) -> String {
- // dynamic limit, to never omit just one field
- let limit = if names.len() == 6 { 6 } else { 5 };
- let mut display =
- names.iter().take(limit).map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", ");
- if names.len() > limit {
- display = format!("{} ... and {} others", display, names.len() - limit);
- }
- display
- }
-
- // Check field access expressions
- fn check_field(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- base: &'tcx hir::Expr<'tcx>,
- field: Ident,
- ) -> Ty<'tcx> {
- debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
- let expr_t = self.check_expr(base);
- let expr_t = self.structurally_resolved_type(base.span, expr_t);
- let mut private_candidate = None;
- let mut autoderef = self.autoderef(expr.span, expr_t);
- while let Some((base_t, _)) = autoderef.next() {
- debug!("base_t: {:?}", base_t);
- match base_t.kind() {
- ty::Adt(base_def, substs) if !base_def.is_enum() => {
- debug!("struct named {:?}", base_t);
- let (ident, def_scope) =
- self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id);
- let fields = &base_def.non_enum_variant().fields;
- if let Some(index) = fields
- .iter()
- .position(|f| f.ident(self.tcx).normalize_to_macros_2_0() == ident)
- {
- let field = &fields[index];
- let field_ty = self.field_ty(expr.span, field, substs);
- // Save the index of all fields regardless of their visibility in case
- // of error recovery.
- self.write_field_index(expr.hir_id, index);
- let adjustments = self.adjust_steps(&autoderef);
- if field.vis.is_accessible_from(def_scope, self.tcx) {
- self.apply_adjustments(base, adjustments);
- self.register_predicates(autoderef.into_obligations());
-
- self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None);
- return field_ty;
- }
- private_candidate = Some((adjustments, base_def.did(), field_ty));
- }
- }
- ty::Tuple(tys) => {
- let fstr = field.as_str();
- if let Ok(index) = fstr.parse::<usize>() {
- if fstr == index.to_string() {
- if let Some(&field_ty) = tys.get(index) {
- let adjustments = self.adjust_steps(&autoderef);
- self.apply_adjustments(base, adjustments);
- self.register_predicates(autoderef.into_obligations());
-
- self.write_field_index(expr.hir_id, index);
- return field_ty;
- }
- }
- }
- }
- _ => {}
- }
- }
- self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
-
- if let Some((adjustments, did, field_ty)) = private_candidate {
- // (#90483) apply adjustments to avoid ExprUseVisitor from
- // creating erroneous projection.
- self.apply_adjustments(base, adjustments);
- self.ban_private_field_access(expr, expr_t, field, did);
- return field_ty;
- }
-
- if field.name == kw::Empty {
- } else if self.method_exists(field, expr_t, expr.hir_id, true) {
- self.ban_take_value_of_method(expr, expr_t, field);
- } else if !expr_t.is_primitive_ty() {
- self.ban_nonexisting_field(field, base, expr, expr_t);
- } else {
- let field_name = field.to_string();
- let mut err = type_error_struct!(
- self.tcx().sess,
- field.span,
- expr_t,
- E0610,
- "`{expr_t}` is a primitive type and therefore doesn't have fields",
- );
- let is_valid_suffix = |field: String| {
- if field == "f32" || field == "f64" {
- return true;
- }
- let mut chars = field.chars().peekable();
- match chars.peek() {
- Some('e') | Some('E') => {
- chars.next();
- if let Some(c) = chars.peek()
- && !c.is_numeric() && *c != '-' && *c != '+'
- {
- return false;
- }
- while let Some(c) = chars.peek() {
- if !c.is_numeric() {
- break;
- }
- chars.next();
- }
- }
- _ => (),
- }
- let suffix = chars.collect::<String>();
- suffix.is_empty() || suffix == "f32" || suffix == "f64"
- };
- if let ty::Infer(ty::IntVar(_)) = expr_t.kind()
- && let ExprKind::Lit(Spanned {
- node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed),
- ..
- }) = base.kind
- && !base.span.from_expansion()
- && is_valid_suffix(field_name)
- {
- err.span_suggestion_verbose(
- field.span.shrink_to_lo(),
- "If the number is meant to be a floating point number, consider adding a `0` after the period",
- '0',
- Applicability::MaybeIncorrect,
- );
- }
- err.emit();
- }
-
- self.tcx().ty_error()
- }
-
- fn check_call_constructor<G: EmissionGuarantee>(
- &self,
- err: &mut DiagnosticBuilder<'_, G>,
- base: &'tcx hir::Expr<'tcx>,
- def_id: DefId,
- ) {
- if let Some(local_id) = def_id.as_local() {
- let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id);
- let node = self.tcx.hir().get(hir_id);
-
- if let Some(fields) = node.tuple_fields() {
- let kind = match self.tcx.opt_def_kind(local_id) {
- Some(DefKind::Ctor(of, _)) => of,
- _ => return,
- };
-
- suggest_call_constructor(base.span, kind, fields.len(), err);
- }
- } else {
- // The logic here isn't smart but `associated_item_def_ids`
- // doesn't work nicely on local.
- if let DefKind::Ctor(of, _) = self.tcx.def_kind(def_id) {
- let parent_def_id = self.tcx.parent(def_id);
- let fields = self.tcx.associated_item_def_ids(parent_def_id);
- suggest_call_constructor(base.span, of, fields.len(), err);
- }
- }
- }
-
- fn suggest_await_on_field_access(
- &self,
- err: &mut Diagnostic,
- field_ident: Ident,
- base: &'tcx hir::Expr<'tcx>,
- ty: Ty<'tcx>,
- ) {
- let output_ty = match self.get_impl_future_output_ty(ty) {
- Some(output_ty) => self.resolve_vars_if_possible(output_ty),
- _ => return,
- };
- let mut add_label = true;
- if let ty::Adt(def, _) = output_ty.skip_binder().kind() {
- // no field access on enum type
- if !def.is_enum() {
- if def
- .non_enum_variant()
- .fields
- .iter()
- .any(|field| field.ident(self.tcx) == field_ident)
- {
- add_label = false;
- err.span_label(
- field_ident.span,
- "field not available in `impl Future`, but it is available in its `Output`",
- );
- err.span_suggestion_verbose(
- base.span.shrink_to_hi(),
- "consider `await`ing on the `Future` and access the field of its `Output`",
- ".await",
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
- if add_label {
- err.span_label(field_ident.span, &format!("field not found in `{ty}`"));
- }
- }
-
- fn ban_nonexisting_field(
- &self,
- field: Ident,
- base: &'tcx hir::Expr<'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- expr_t: Ty<'tcx>,
- ) {
- debug!(
- "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}",
- field, base, expr, expr_t
- );
- let mut err = self.no_such_field_err(field, expr_t, base.hir_id);
-
- match *expr_t.peel_refs().kind() {
- ty::Array(_, len) => {
- self.maybe_suggest_array_indexing(&mut err, expr, base, field, len);
- }
- ty::RawPtr(..) => {
- self.suggest_first_deref_field(&mut err, expr, base, field);
- }
- ty::Adt(def, _) if !def.is_enum() => {
- self.suggest_fields_on_recordish(&mut err, def, field, expr.span);
- }
- ty::Param(param_ty) => {
- self.point_at_param_definition(&mut err, param_ty);
- }
- ty::Opaque(_, _) => {
- self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs());
- }
- ty::FnDef(def_id, _) => {
- self.check_call_constructor(&mut err, base, def_id);
- }
- _ => {}
- }
-
- if field.name == kw::Await {
- // We know by construction that `<expr>.await` is either on Rust 2015
- // or results in `ExprKind::Await`. Suggest switching the edition to 2018.
- err.note("to `.await` a `Future`, switch to Rust 2018 or later");
- err.help_use_latest_edition();
- }
-
- err.emit();
- }
-
- fn ban_private_field_access(
- &self,
- expr: &hir::Expr<'_>,
- expr_t: Ty<'tcx>,
- field: Ident,
- base_did: DefId,
- ) {
- let struct_path = self.tcx().def_path_str(base_did);
- let kind_name = self.tcx().def_kind(base_did).descr(base_did);
- let mut err = struct_span_err!(
- self.tcx().sess,
- field.span,
- E0616,
- "field `{field}` of {kind_name} `{struct_path}` is private",
- );
- err.span_label(field.span, "private field");
- // Also check if an accessible method exists, which is often what is meant.
- if self.method_exists(field, expr_t, expr.hir_id, false) && !self.expr_in_place(expr.hir_id)
- {
- self.suggest_method_call(
- &mut err,
- &format!("a method `{field}` also exists, call it with parentheses"),
- field,
- expr_t,
- expr,
- None,
- );
- }
- err.emit();
- }
-
- fn ban_take_value_of_method(&self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, field: Ident) {
- let mut err = type_error_struct!(
- self.tcx().sess,
- field.span,
- expr_t,
- E0615,
- "attempted to take value of method `{field}` on type `{expr_t}`",
- );
- err.span_label(field.span, "method, not a field");
- let expr_is_call =
- if let hir::Node::Expr(hir::Expr { kind: ExprKind::Call(callee, _args), .. }) =
- self.tcx.hir().get(self.tcx.hir().get_parent_node(expr.hir_id))
- {
- expr.hir_id == callee.hir_id
- } else {
- false
- };
- let expr_snippet =
- self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap_or_default();
- let is_wrapped = expr_snippet.starts_with('(') && expr_snippet.ends_with(')');
- let after_open = expr.span.lo() + rustc_span::BytePos(1);
- let before_close = expr.span.hi() - rustc_span::BytePos(1);
-
- if expr_is_call && is_wrapped {
- err.multipart_suggestion(
- "remove wrapping parentheses to call the method",
- vec![
- (expr.span.with_hi(after_open), String::new()),
- (expr.span.with_lo(before_close), String::new()),
- ],
- Applicability::MachineApplicable,
- );
- } else if !self.expr_in_place(expr.hir_id) {
- // Suggest call parentheses inside the wrapping parentheses
- let span = if is_wrapped {
- expr.span.with_lo(after_open).with_hi(before_close)
- } else {
- expr.span
- };
- self.suggest_method_call(
- &mut err,
- "use parentheses to call the method",
- field,
- expr_t,
- expr,
- Some(span),
- );
- } else {
- let mut found = false;
-
- if let ty::RawPtr(ty_and_mut) = expr_t.kind()
- && let ty::Adt(adt_def, _) = ty_and_mut.ty.kind()
- {
- if adt_def.variants().len() == 1
- && adt_def
- .variants()
- .iter()
- .next()
- .unwrap()
- .fields
- .iter()
- .any(|f| f.ident(self.tcx) == field)
- {
- if let Some(dot_loc) = expr_snippet.rfind('.') {
- found = true;
- err.span_suggestion(
- expr.span.with_hi(expr.span.lo() + BytePos::from_usize(dot_loc)),
- "to access the field, dereference first",
- format!("(*{})", &expr_snippet[0..dot_loc]),
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
-
- if !found {
- err.help("methods are immutable and cannot be assigned to");
- }
- }
-
- err.emit();
- }
-
- fn point_at_param_definition(&self, err: &mut Diagnostic, param: ty::ParamTy) {
- let generics = self.tcx.generics_of(self.body_id.owner.to_def_id());
- let generic_param = generics.type_param(&param, self.tcx);
- if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind {
- return;
- }
- let param_def_id = generic_param.def_id;
- let param_hir_id = match param_def_id.as_local() {
- Some(x) => self.tcx.hir().local_def_id_to_hir_id(x),
- None => return,
- };
- let param_span = self.tcx.hir().span(param_hir_id);
- let param_name = self.tcx.hir().ty_param_name(param_def_id.expect_local());
-
- err.span_label(param_span, &format!("type parameter '{param_name}' declared here"));
- }
-
- fn suggest_fields_on_recordish(
- &self,
- err: &mut Diagnostic,
- def: ty::AdtDef<'tcx>,
- field: Ident,
- access_span: Span,
- ) {
- if let Some(suggested_field_name) =
- self.suggest_field_name(def.non_enum_variant(), field.name, vec![], access_span)
- {
- err.span_suggestion(
- field.span,
- "a field with a similar name exists",
- suggested_field_name,
- Applicability::MaybeIncorrect,
- );
- } else {
- err.span_label(field.span, "unknown field");
- let struct_variant_def = def.non_enum_variant();
- let field_names = self.available_field_names(struct_variant_def, access_span);
- if !field_names.is_empty() {
- err.note(&format!(
- "available fields are: {}",
- self.name_series_display(field_names),
- ));
- }
- }
- }
-
- fn maybe_suggest_array_indexing(
- &self,
- err: &mut Diagnostic,
- expr: &hir::Expr<'_>,
- base: &hir::Expr<'_>,
- field: Ident,
- len: ty::Const<'tcx>,
- ) {
- if let (Some(len), Ok(user_index)) =
- (len.try_eval_usize(self.tcx, self.param_env), field.as_str().parse::<u64>())
- && let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span)
- {
- let help = "instead of using tuple indexing, use array indexing";
- let suggestion = format!("{base}[{field}]");
- let applicability = if len < user_index {
- Applicability::MachineApplicable
- } else {
- Applicability::MaybeIncorrect
- };
- err.span_suggestion(expr.span, help, suggestion, applicability);
- }
- }
-
- fn suggest_first_deref_field(
- &self,
- err: &mut Diagnostic,
- expr: &hir::Expr<'_>,
- base: &hir::Expr<'_>,
- field: Ident,
- ) {
- if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) {
- let msg = format!("`{base}` is a raw pointer; try dereferencing it");
- let suggestion = format!("(*{base}).{field}");
- err.span_suggestion(expr.span, &msg, suggestion, Applicability::MaybeIncorrect);
- }
- }
-
- fn no_such_field_err(
- &self,
- field: Ident,
- expr_t: Ty<'tcx>,
- id: HirId,
- ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
- let span = field.span;
- debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t);
-
- let mut err = type_error_struct!(
- self.tcx().sess,
- field.span,
- expr_t,
- E0609,
- "no field `{field}` on type `{expr_t}`",
- );
-
- // try to add a suggestion in case the field is a nested field of a field of the Adt
- if let Some((fields, substs)) = self.get_field_candidates(span, expr_t) {
- for candidate_field in fields.iter() {
- if let Some(mut field_path) = self.check_for_nested_field_satisfying(
- span,
- &|candidate_field, _| candidate_field.ident(self.tcx()) == field,
- candidate_field,
- substs,
- vec![],
- self.tcx.parent_module(id).to_def_id(),
- ) {
- // field_path includes `field` that we're looking for, so pop it.
- field_path.pop();
-
- let field_path_str = field_path
- .iter()
- .map(|id| id.name.to_ident_string())
- .collect::<Vec<String>>()
- .join(".");
- debug!("field_path_str: {:?}", field_path_str);
-
- err.span_suggestion_verbose(
- field.span.shrink_to_lo(),
- "one of the expressions' fields has a field of the same name",
- format!("{field_path_str}."),
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
- err
- }
-
- pub(crate) fn get_field_candidates(
- &self,
- span: Span,
- base_t: Ty<'tcx>,
- ) -> Option<(&[ty::FieldDef], SubstsRef<'tcx>)> {
- debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_t);
-
- for (base_t, _) in self.autoderef(span, base_t) {
- match base_t.kind() {
- ty::Adt(base_def, substs) if !base_def.is_enum() => {
- let fields = &base_def.non_enum_variant().fields;
- // For compile-time reasons put a limit on number of fields we search
- if fields.len() > 100 {
- return None;
- }
- return Some((fields, substs));
- }
- _ => {}
- }
- }
- None
- }
-
- /// This method is called after we have encountered a missing field error to recursively
- /// search for the field
- pub(crate) fn check_for_nested_field_satisfying(
- &self,
- span: Span,
- matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool,
- candidate_field: &ty::FieldDef,
- subst: SubstsRef<'tcx>,
- mut field_path: Vec<Ident>,
- id: DefId,
- ) -> Option<Vec<Ident>> {
- debug!(
- "check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}",
- span, candidate_field, field_path
- );
-
- if field_path.len() > 3 {
- // For compile-time reasons and to avoid infinite recursion we only check for fields
- // up to a depth of three
- None
- } else {
- // recursively search fields of `candidate_field` if it's a ty::Adt
- field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0());
- let field_ty = candidate_field.ty(self.tcx, subst);
- if let Some((nested_fields, subst)) = self.get_field_candidates(span, field_ty) {
- for field in nested_fields.iter() {
- if field.vis.is_accessible_from(id, self.tcx) {
- if matches(candidate_field, field_ty) {
- return Some(field_path);
- } else if let Some(field_path) = self.check_for_nested_field_satisfying(
- span,
- matches,
- field,
- subst,
- field_path.clone(),
- id,
- ) {
- return Some(field_path);
- }
- }
- }
- }
- None
- }
- }
-
- fn check_expr_index(
- &self,
- base: &'tcx hir::Expr<'tcx>,
- idx: &'tcx hir::Expr<'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- ) -> Ty<'tcx> {
- let base_t = self.check_expr(&base);
- let idx_t = self.check_expr(&idx);
-
- if base_t.references_error() {
- base_t
- } else if idx_t.references_error() {
- idx_t
- } else {
- let base_t = self.structurally_resolved_type(base.span, base_t);
- match self.lookup_indexing(expr, base, base_t, idx, idx_t) {
- Some((index_ty, element_ty)) => {
- // two-phase not needed because index_ty is never mutable
- self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No);
- self.select_obligations_where_possible(false, |errors| {
- self.point_at_index_if_possible(errors, idx.span)
- });
- element_ty
- }
- None => {
- let mut err = type_error_struct!(
- self.tcx.sess,
- expr.span,
- base_t,
- E0608,
- "cannot index into a value of type `{base_t}`",
- );
- // Try to give some advice about indexing tuples.
- if let ty::Tuple(..) = base_t.kind() {
- let mut needs_note = true;
- // If the index is an integer, we can show the actual
- // fixed expression:
- if let ExprKind::Lit(ref lit) = idx.kind {
- if let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node {
- let snip = self.tcx.sess.source_map().span_to_snippet(base.span);
- if let Ok(snip) = snip {
- err.span_suggestion(
- expr.span,
- "to access tuple elements, use",
- format!("{snip}.{i}"),
- Applicability::MachineApplicable,
- );
- needs_note = false;
- }
- }
- }
- if needs_note {
- err.help(
- "to access tuple elements, use tuple indexing \
- syntax (e.g., `tuple.0`)",
- );
- }
- }
- err.emit();
- self.tcx.ty_error()
- }
- }
- }
- }
-
- fn point_at_index_if_possible(
- &self,
- errors: &mut Vec<traits::FulfillmentError<'tcx>>,
- span: Span,
- ) {
- for error in errors {
- match error.obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Trait(predicate)
- if self.tcx.is_diagnostic_item(sym::SliceIndex, predicate.trait_ref.def_id) => {
- }
- _ => continue,
- }
- error.obligation.cause.span = span;
- }
- }
-
- fn check_expr_yield(
- &self,
- value: &'tcx hir::Expr<'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- src: &'tcx hir::YieldSource,
- ) -> Ty<'tcx> {
- match self.resume_yield_tys {
- Some((resume_ty, yield_ty)) => {
- self.check_expr_coercable_to_type(&value, yield_ty, None);
-
- resume_ty
- }
- // Given that this `yield` expression was generated as a result of lowering a `.await`,
- // we know that the yield type must be `()`; however, the context won't contain this
- // information. Hence, we check the source of the yield expression here and check its
- // value's type against `()` (this check should always hold).
- None if src.is_await() => {
- self.check_expr_coercable_to_type(&value, self.tcx.mk_unit(), None);
- self.tcx.mk_unit()
- }
- _ => {
- self.tcx.sess.emit_err(YieldExprOutsideOfGenerator { span: expr.span });
- // Avoid expressions without types during writeback (#78653).
- self.check_expr(value);
- self.tcx.mk_unit()
- }
- }
- }
-
- fn check_expr_asm_operand(&self, expr: &'tcx hir::Expr<'tcx>, is_input: bool) {
- let needs = if is_input { Needs::None } else { Needs::MutPlace };
- let ty = self.check_expr_with_needs(expr, needs);
- self.require_type_is_sized(ty, expr.span, traits::InlineAsmSized);
-
- if !is_input && !expr.is_syntactic_place_expr() {
- let mut err = self.tcx.sess.struct_span_err(expr.span, "invalid asm output");
- err.span_label(expr.span, "cannot assign to this expression");
- err.emit();
- }
-
- // If this is an input value, we require its type to be fully resolved
- // at this point. This allows us to provide helpful coercions which help
- // pass the type candidate list in a later pass.
- //
- // We don't require output types to be resolved at this point, which
- // allows them to be inferred based on how they are used later in the
- // function.
- if is_input {
- let ty = self.structurally_resolved_type(expr.span, ty);
- match *ty.kind() {
- ty::FnDef(..) => {
- let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx));
- self.demand_coerce(expr, ty, fnptr_ty, None, AllowTwoPhase::No);
- }
- ty::Ref(_, base_ty, mutbl) => {
- let ptr_ty = self.tcx.mk_ptr(ty::TypeAndMut { ty: base_ty, mutbl });
- self.demand_coerce(expr, ty, ptr_ty, None, AllowTwoPhase::No);
- }
- _ => {}
- }
- }
- }
-
- fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> {
- for (op, _op_sp) in asm.operands {
- match op {
- hir::InlineAsmOperand::In { expr, .. } => {
- self.check_expr_asm_operand(expr, true);
- }
- hir::InlineAsmOperand::Out { expr: Some(expr), .. }
- | hir::InlineAsmOperand::InOut { expr, .. } => {
- self.check_expr_asm_operand(expr, false);
- }
- hir::InlineAsmOperand::Out { expr: None, .. } => {}
- hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
- self.check_expr_asm_operand(in_expr, true);
- if let Some(out_expr) = out_expr {
- self.check_expr_asm_operand(out_expr, false);
- }
- }
- // `AnonConst`s have their own body and is type-checked separately.
- // As they don't flow into the type system we don't need them to
- // be well-formed.
- hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } => {}
- hir::InlineAsmOperand::SymStatic { .. } => {}
- }
- }
- if asm.options.contains(ast::InlineAsmOptions::NORETURN) {
- self.tcx.types.never
- } else {
- self.tcx.mk_unit()
- }
- }
-}
-
-pub(super) fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> {
- Some(match ty.kind() {
- ty::Bool => "true",
- ty::Char => "'a'",
- ty::Int(_) | ty::Uint(_) => "42",
- ty::Float(_) => "3.14159",
- ty::Error(_) | ty::Never => return None,
- _ => "value",
- })
-}
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
deleted file mode 100644
index 660e7e4e3..000000000
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ /dev/null
@@ -1,1900 +0,0 @@
-use crate::astconv::AstConv;
-use crate::check::coercion::CoerceMany;
-use crate::check::fn_ctxt::arg_matrix::{
- ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx,
-};
-use crate::check::gather_locals::Declaration;
-use crate::check::intrinsicck::InlineAsmCtxt;
-use crate::check::method::MethodCallee;
-use crate::check::Expectation::*;
-use crate::check::TupleArgumentsFlag::*;
-use crate::check::{
- potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt,
- LocalTy, Needs, TupleArgumentsFlag,
-};
-use crate::structured_errors::StructuredDiagnostic;
-
-use rustc_ast as ast;
-use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan};
-use rustc_hir as hir;
-use rustc_hir::def::{CtorOf, DefKind, Res};
-use rustc_hir::def_id::DefId;
-use rustc_hir::{ExprKind, Node, QPath};
-use rustc_index::vec::IndexVec;
-use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt};
-use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_infer::infer::InferOk;
-use rustc_infer::infer::TypeTrace;
-use rustc_middle::ty::adjustment::AllowTwoPhase;
-use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty};
-use rustc_session::Session;
-use rustc_span::symbol::Ident;
-use rustc_span::{self, Span};
-use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
-
-use std::iter;
-use std::slice;
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- pub(in super::super) fn check_casts(&self) {
- let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
- debug!("FnCtxt::check_casts: {} deferred checks", deferred_cast_checks.len());
- for cast in deferred_cast_checks.drain(..) {
- cast.check(self);
- }
- }
-
- pub(in super::super) fn check_transmutes(&self) {
- let mut deferred_transmute_checks = self.deferred_transmute_checks.borrow_mut();
- debug!("FnCtxt::check_transmutes: {} deferred checks", deferred_transmute_checks.len());
- for (from, to, span) in deferred_transmute_checks.drain(..) {
- self.check_transmute(span, from, to);
- }
- }
-
- pub(in super::super) fn check_asms(&self) {
- let mut deferred_asm_checks = self.deferred_asm_checks.borrow_mut();
- debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len());
- for (asm, hir_id) in deferred_asm_checks.drain(..) {
- let enclosing_id = self.tcx.hir().enclosing_body_owner(hir_id);
- InlineAsmCtxt::new_in_fn(self)
- .check_asm(asm, self.tcx.hir().local_def_id_to_hir_id(enclosing_id));
- }
- }
-
- pub(in super::super) fn check_method_argument_types(
- &self,
- sp: Span,
- expr: &'tcx hir::Expr<'tcx>,
- method: Result<MethodCallee<'tcx>, ()>,
- args_no_rcvr: &'tcx [hir::Expr<'tcx>],
- tuple_arguments: TupleArgumentsFlag,
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- let has_error = match method {
- Ok(method) => method.substs.references_error() || method.sig.references_error(),
- Err(_) => true,
- };
- if has_error {
- let err_inputs = self.err_args(args_no_rcvr.len());
-
- let err_inputs = match tuple_arguments {
- DontTupleArguments => err_inputs,
- TupleArguments => vec![self.tcx.intern_tup(&err_inputs)],
- };
-
- self.check_argument_types(
- sp,
- expr,
- &err_inputs,
- None,
- args_no_rcvr,
- false,
- tuple_arguments,
- method.ok().map(|method| method.def_id),
- );
- return self.tcx.ty_error();
- }
-
- let method = method.unwrap();
- // HACK(eddyb) ignore self in the definition (see above).
- let expected_input_tys = self.expected_inputs_for_expected_output(
- sp,
- expected,
- method.sig.output(),
- &method.sig.inputs()[1..],
- );
- self.check_argument_types(
- sp,
- expr,
- &method.sig.inputs()[1..],
- expected_input_tys,
- args_no_rcvr,
- method.sig.c_variadic,
- tuple_arguments,
- Some(method.def_id),
- );
- method.sig.output()
- }
-
- /// Generic function that factors out common logic from function calls,
- /// method calls and overloaded operators.
- pub(in super::super) fn check_argument_types(
- &self,
- // Span enclosing the call site
- call_span: Span,
- // Expression of the call site
- call_expr: &'tcx hir::Expr<'tcx>,
- // Types (as defined in the *signature* of the target function)
- formal_input_tys: &[Ty<'tcx>],
- // More specific expected types, after unifying with caller output types
- expected_input_tys: Option<Vec<Ty<'tcx>>>,
- // The expressions for each provided argument
- provided_args: &'tcx [hir::Expr<'tcx>],
- // Whether the function is variadic, for example when imported from C
- c_variadic: bool,
- // Whether the arguments have been bundled in a tuple (ex: closures)
- tuple_arguments: TupleArgumentsFlag,
- // The DefId for the function being called, for better error messages
- fn_def_id: Option<DefId>,
- ) {
- let tcx = self.tcx;
-
- // Conceptually, we've got some number of expected inputs, and some number of provided aguments
- // and we can form a grid of whether each argument could satisfy a given input:
- // in1 | in2 | in3 | ...
- // arg1 ? | | |
- // arg2 | ? | |
- // arg3 | | ? |
- // ...
- // Initially, we just check the diagonal, because in the case of correct code
- // these are the only checks that matter
- // However, in the unhappy path, we'll fill in this whole grid to attempt to provide
- // better error messages about invalid method calls.
-
- // All the input types from the fn signature must outlive the call
- // so as to validate implied bounds.
- for (&fn_input_ty, arg_expr) in iter::zip(formal_input_tys, provided_args) {
- self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation);
- }
-
- let mut err_code = "E0061";
-
- // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
- let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments {
- let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]);
- match tuple_type.kind() {
- // We expected a tuple and got a tuple
- ty::Tuple(arg_types) => {
- // Argument length differs
- if arg_types.len() != provided_args.len() {
- err_code = "E0057";
- }
- let expected_input_tys = match expected_input_tys {
- Some(expected_input_tys) => match expected_input_tys.get(0) {
- Some(ty) => match ty.kind() {
- ty::Tuple(tys) => Some(tys.iter().collect()),
- _ => None,
- },
- None => None,
- },
- None => None,
- };
- (arg_types.iter().collect(), expected_input_tys)
- }
- _ => {
- // Otherwise, there's a mismatch, so clear out what we're expecting, and set
- // our input types to err_args so we don't blow up the error messages
- struct_span_err!(
- tcx.sess,
- call_span,
- E0059,
- "cannot use call notation; the first type parameter \
- for the function trait is neither a tuple nor unit"
- )
- .emit();
- (self.err_args(provided_args.len()), None)
- }
- }
- } else {
- (formal_input_tys.to_vec(), expected_input_tys)
- };
-
- // 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 {
- formal_input_tys.clone()
- };
-
- let minimum_input_count = expected_input_tys.len();
- let provided_arg_count = 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 = |idx| {
- let formal_input_ty: Ty<'tcx> = formal_input_tys[idx];
- let expected_input_ty: Ty<'tcx> = 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(self, expected_input_ty);
-
- let checked_ty = self.check_expr_with_expectation(provided_arg, expectation);
-
- // 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(self).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 =
- self.resolve_vars_with_obligations_and_mutate_fulfillment(coerced_ty, |errors| {
- self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr);
- self.point_at_arg_instead_of_call_if_possible(
- errors,
- call_expr,
- call_span,
- provided_args,
- &expected_input_tys,
- );
- });
-
- let coerce_error = self
- .try_coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None)
- .err();
-
- if coerce_error.is_some() {
- return Compatibility::Incompatible(coerce_error);
- }
-
- // 3. Check if the formal type is a supertype of the checked one
- // and register any such obligations for future type checks
- let supertype_error = self
- .at(&self.misc(provided_arg.span), self.param_env)
- .sup(formal_input_ty, coerced_ty);
- let subtyping_error = match supertype_error {
- Ok(InferOk { obligations, value: () }) => {
- self.register_predicates(obligations);
- None
- }
- Err(err) => Some(err),
- };
-
- // If neither check failed, the types are compatible
- match subtyping_error {
- None => Compatibility::Compatible,
- Some(_) => Compatibility::Incompatible(subtyping_error),
- }
- };
-
- // To start, we only care "along the diagonal", where we expect every
- // provided arg to be in the right spot
- let mut compatibility_diagonal =
- vec![Compatibility::Incompatible(None); provided_args.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 mut call_appears_satisfied = if c_variadic {
- provided_arg_count >= minimum_input_count
- } else {
- provided_arg_count == minimum_input_count
- };
-
- // 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] {
- // 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.select_obligations_where_possible(false, |errors| {
- self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr);
- self.point_at_arg_instead_of_call_if_possible(
- errors,
- call_expr,
- call_span,
- &provided_args,
- &expected_input_tys,
- );
- })
- }
-
- // 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() {
- // Warn only for the first loop (the "no closures" one).
- // Closure arguments themselves can't be diverging, but
- // a previous argument can, e.g., `foo(panic!(), || {})`.
- if !check_closures {
- self.warn_if_unreachable(arg.hir_id, arg.span, "expression");
- }
-
- // For C-variadic functions, we don't have a declared type for all of
- // the arguments hence we only do our usual type checking with
- // the arguments who's types we do know. However, we *can* check
- // for unreachable expressions (see above).
- // FIXME: unreachable warning current isn't emitted
- if idx >= minimum_input_count {
- continue;
- }
-
- let is_closure = matches!(arg.kind, ExprKind::Closure { .. });
- if is_closure != check_closures {
- continue;
- }
-
- let compatible = demand_compatible(idx);
- let is_compatible = matches!(compatible, Compatibility::Compatible);
- compatibility_diagonal[idx] = compatible;
-
- if !is_compatible {
- call_appears_satisfied = false;
- }
- }
- }
-
- if c_variadic && provided_arg_count < minimum_input_count {
- err_code = "E0060";
- }
-
- for arg in provided_args.iter().skip(minimum_input_count) {
- // Make sure we've checked this expr at least once.
- let arg_ty = self.check_expr(&arg);
-
- // If the function is c-style variadic, we skipped a bunch of arguments
- // so we need to check those, and write out the types
- // Ideally this would be folded into the above, for uniform style
- // but c-variadic is already a corner case
- if c_variadic {
- fn variadic_error<'tcx>(
- sess: &'tcx Session,
- span: Span,
- ty: Ty<'tcx>,
- cast_ty: &str,
- ) {
- use crate::structured_errors::MissingCastForVariadicArg;
-
- MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit();
- }
-
- // There are a few types which get autopromoted when passed via varargs
- // in C but we just error out instead and require explicit casts.
- let arg_ty = self.structurally_resolved_type(arg.span, arg_ty);
- match arg_ty.kind() {
- ty::Float(ty::FloatTy::F32) => {
- variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
- }
- ty::Int(ty::IntTy::I8 | ty::IntTy::I16) | ty::Bool => {
- variadic_error(tcx.sess, arg.span, arg_ty, "c_int");
- }
- ty::Uint(ty::UintTy::U8 | ty::UintTy::U16) => {
- variadic_error(tcx.sess, arg.span, arg_ty, "c_uint");
- }
- ty::FnDef(..) => {
- let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx));
- let ptr_ty = self.resolve_vars_if_possible(ptr_ty);
- variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string());
- }
- _ => {}
- }
- }
- }
-
- if !call_appears_satisfied {
- let compatibility_diagonal = IndexVec::from_raw(compatibility_diagonal);
- let provided_args = IndexVec::from_iter(provided_args.iter().take(if c_variadic {
- minimum_input_count
- } else {
- provided_arg_count
- }));
- debug_assert_eq!(
- formal_input_tys.len(),
- expected_input_tys.len(),
- "expected formal_input_tys to be the same size as expected_input_tys"
- );
- let formal_and_expected_inputs = IndexVec::from_iter(
- formal_input_tys
- .iter()
- .copied()
- .zip(expected_input_tys.iter().copied())
- .map(|vars| self.resolve_vars_if_possible(vars)),
- );
-
- self.report_arg_errors(
- compatibility_diagonal,
- formal_and_expected_inputs,
- provided_args,
- c_variadic,
- err_code,
- fn_def_id,
- call_span,
- call_expr,
- );
- }
- }
-
- fn report_arg_errors(
- &self,
- compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>,
- formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>,
- provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>,
- c_variadic: bool,
- err_code: &str,
- fn_def_id: Option<DefId>,
- call_span: Span,
- call_expr: &hir::Expr<'tcx>,
- ) {
- // Next, let's construct the error
- let (error_span, full_call_span, ctor_of) = match &call_expr.kind {
- hir::ExprKind::Call(
- hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. },
- _,
- ) => {
- if let Res::Def(DefKind::Ctor(of, _), _) =
- self.typeck_results.borrow().qpath_res(qpath, *hir_id)
- {
- (call_span, *span, Some(of))
- } else {
- (call_span, *span, None)
- }
- }
- hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None),
- hir::ExprKind::MethodCall(path_segment, _, span) => {
- let ident_span = path_segment.ident.span;
- let ident_span = if let Some(args) = path_segment.args {
- ident_span.with_hi(args.span_ext.hi())
- } else {
- ident_span
- };
- (
- *span, ident_span, None, // methods are never ctors
- )
- }
- k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k),
- };
- let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span);
- let call_name = match ctor_of {
- Some(CtorOf::Struct) => "struct",
- Some(CtorOf::Variant) => "enum variant",
- None => "function",
- };
-
- // Don't print if it has error types or is just plain `_`
- fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
- tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var())
- }
-
- self.set_tainted_by_errors();
- let tcx = self.tcx;
-
- // Get the argument span in the context of the call span so that
- // suggestions and labels are (more) correct when an arg is a
- // macro invocation.
- let normalize_span = |span: Span| -> Span {
- let normalized_span = span.find_ancestor_inside(error_span).unwrap_or(span);
- // Sometimes macros mess up the spans, so do not normalize the
- // arg span to equal the error span, because that's less useful
- // than pointing out the arg expr in the wrong context.
- if normalized_span.source_equal(error_span) { span } else { normalized_span }
- };
-
- // Precompute the provided types and spans, since that's all we typically need for below
- let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args
- .iter()
- .map(|expr| {
- let ty = self
- .typeck_results
- .borrow()
- .expr_ty_adjusted_opt(*expr)
- .unwrap_or_else(|| tcx.ty_error());
- (self.resolve_vars_if_possible(ty), normalize_span(expr.span))
- })
- .collect();
- let callee_expr = match &call_expr.peel_blocks().kind {
- hir::ExprKind::Call(callee, _) => Some(*callee),
- hir::ExprKind::MethodCall(_, callee, _) => {
- if let Some((DefKind::AssocFn, def_id)) =
- self.typeck_results.borrow().type_dependent_def(call_expr.hir_id)
- && let Some(assoc) = tcx.opt_associated_item(def_id)
- && assoc.fn_has_self_parameter
- {
- Some(&callee[0])
- } else {
- None
- }
- }
- _ => None,
- };
- let callee_ty = callee_expr
- .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr));
-
- // A "softer" version of the `demand_compatible`, which checks types without persisting them,
- // and treats error types differently
- // This will allow us to "probe" for other argument orders that would likely have been correct
- let check_compatible = |provided_idx: ProvidedIdx, expected_idx: ExpectedIdx| {
- if provided_idx.as_usize() == expected_idx.as_usize() {
- return compatibility_diagonal[provided_idx].clone();
- }
-
- let (formal_input_ty, expected_input_ty) = formal_and_expected_inputs[expected_idx];
- // If either is an error type, we defy the usual convention and consider them to *not* be
- // coercible. This prevents our error message heuristic from trying to pass errors into
- // every argument.
- if (formal_input_ty, expected_input_ty).references_error() {
- return Compatibility::Incompatible(None);
- }
-
- let (arg_ty, arg_span) = provided_arg_tys[provided_idx];
-
- let expectation = Expectation::rvalue_hint(self, expected_input_ty);
- let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty);
- let can_coerce = self.can_coerce(arg_ty, coerced_ty);
- if !can_coerce {
- return Compatibility::Incompatible(None);
- }
-
- // Using probe here, since we don't want this subtyping to affect inference.
- let subtyping_error = self.probe(|_| {
- self.at(&self.misc(arg_span), self.param_env).sup(formal_input_ty, coerced_ty).err()
- });
-
- // Same as above: if either the coerce type or the checked type is an error type,
- // consider them *not* compatible.
- let references_error = (coerced_ty, arg_ty).references_error();
- match (references_error, subtyping_error) {
- (false, None) => Compatibility::Compatible,
- (_, subtyping_error) => Compatibility::Incompatible(subtyping_error),
- }
- };
-
- // The algorithm here is inspired by levenshtein distance and longest common subsequence.
- // We'll try to detect 4 different types of mistakes:
- // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs
- // - An input is missing, which isn't satisfied by *any* of the other arguments
- // - Some number of arguments have been provided in the wrong order
- // - A type is straight up invalid
-
- // First, let's find the errors
- let (mut errors, matched_inputs) =
- ArgMatrix::new(provided_args.len(), formal_and_expected_inputs.len(), check_compatible)
- .find_errors();
-
- // First, check if we just need to wrap some arguments in a tuple.
- if let Some((mismatch_idx, terr)) =
- compatibility_diagonal.iter().enumerate().find_map(|(i, c)| {
- if let Compatibility::Incompatible(Some(terr)) = c { Some((i, terr)) } else { None }
- })
- {
- // Is the first bad expected argument a tuple?
- // Do we have as many extra provided arguments as the tuple's length?
- // If so, we might have just forgotten to wrap some args in a tuple.
- if let Some(ty::Tuple(tys)) =
- formal_and_expected_inputs.get(mismatch_idx.into()).map(|tys| tys.1.kind())
- // If the tuple is unit, we're not actually wrapping any arguments.
- && !tys.is_empty()
- && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len()
- {
- // Wrap up the N provided arguments starting at this position in a tuple.
- let provided_as_tuple = tcx.mk_tup(
- provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx).take(tys.len()),
- );
-
- let mut satisfied = true;
- // Check if the newly wrapped tuple + rest of the arguments are compatible.
- for ((_, expected_ty), provided_ty) in std::iter::zip(
- formal_and_expected_inputs.iter().skip(mismatch_idx),
- [provided_as_tuple].into_iter().chain(
- provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx + tys.len()),
- ),
- ) {
- if !self.can_coerce(provided_ty, *expected_ty) {
- satisfied = false;
- break;
- }
- }
-
- // If they're compatible, suggest wrapping in an arg, and we're done!
- // Take some care with spans, so we don't suggest wrapping a macro's
- // innards in parenthesis, for example.
- if satisfied
- && let Some((_, lo)) =
- provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx))
- && let Some((_, hi)) =
- provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx + tys.len() - 1))
- {
- let mut err;
- if tys.len() == 1 {
- // A tuple wrap suggestion actually occurs within,
- // so don't do anything special here.
- err = self.report_and_explain_type_error(
- TypeTrace::types(
- &self.misc(*lo),
- true,
- formal_and_expected_inputs[mismatch_idx.into()].1,
- provided_arg_tys[mismatch_idx.into()].0,
- ),
- terr,
- );
- err.span_label(
- full_call_span,
- format!("arguments to this {} are incorrect", call_name),
- );
- } else {
- err = tcx.sess.struct_span_err_with_code(
- full_call_span,
- &format!(
- "this {} takes {}{} but {} {} supplied",
- call_name,
- if c_variadic { "at least " } else { "" },
- potentially_plural_count(
- formal_and_expected_inputs.len(),
- "argument"
- ),
- potentially_plural_count(provided_args.len(), "argument"),
- pluralize!("was", provided_args.len())
- ),
- DiagnosticId::Error(err_code.to_owned()),
- );
- err.multipart_suggestion_verbose(
- "wrap these arguments in parentheses to construct a tuple",
- vec![
- (lo.shrink_to_lo(), "(".to_string()),
- (hi.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MachineApplicable,
- );
- };
- self.label_fn_like(&mut err, fn_def_id, callee_ty);
- err.emit();
- return;
- }
- }
- }
-
- // Okay, so here's where it gets complicated in regards to what errors
- // we emit and how.
- // There are 3 different "types" of errors we might encounter.
- // 1) Missing/extra/swapped arguments
- // 2) Valid but incorrect arguments
- // 3) Invalid arguments
- // - Currently I think this only comes up with `CyclicTy`
- //
- // We first need to go through, remove those from (3) and emit those
- // as their own error, particularly since they're error code and
- // message is special. From what I can tell, we *must* emit these
- // here (vs somewhere prior to this function) since the arguments
- // become invalid *because* of how they get used in the function.
- // It is what it is.
-
- if errors.is_empty() {
- if cfg!(debug_assertions) {
- span_bug!(error_span, "expected errors from argument matrix");
- } else {
- tcx.sess
- .struct_span_err(
- error_span,
- "argument type mismatch was detected, \
- but rustc had trouble determining where",
- )
- .note(
- "we would appreciate a bug report: \
- https://github.com/rust-lang/rust/issues/new",
- )
- .emit();
- }
- return;
- }
-
- errors.drain_filter(|error| {
- let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(error)) = error else { return false };
- let (provided_ty, provided_span) = provided_arg_tys[*provided_idx];
- let (expected_ty, _) = formal_and_expected_inputs[*expected_idx];
- let cause = &self.misc(provided_span);
- let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
- if let Some(e) = error {
- if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) {
- self.report_and_explain_type_error(trace, e).emit();
- return true;
- }
- }
- false
- });
-
- // We're done if we found errors, but we already emitted them.
- if errors.is_empty() {
- return;
- }
-
- // Okay, now that we've emitted the special errors separately, we
- // are only left missing/extra/swapped and mismatched arguments, both
- // can be collated pretty easily if needed.
-
- // Next special case: if there is only one "Incompatible" error, just emit that
- if let [
- Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))),
- ] = &errors[..]
- {
- let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx];
- let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx];
- let cause = &self.misc(provided_arg_span);
- let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
- let mut err = self.report_and_explain_type_error(trace, err);
- self.emit_coerce_suggestions(
- &mut err,
- &provided_args[*provided_idx],
- provided_ty,
- Expectation::rvalue_hint(self, expected_ty)
- .only_has_type(self)
- .unwrap_or(formal_ty),
- None,
- None,
- );
- err.span_label(
- full_call_span,
- format!("arguments to this {} are incorrect", call_name),
- );
- // Call out where the function is defined
- self.label_fn_like(&mut err, fn_def_id, callee_ty);
- err.emit();
- return;
- }
-
- let mut err = if formal_and_expected_inputs.len() == provided_args.len() {
- struct_span_err!(
- tcx.sess,
- full_call_span,
- E0308,
- "arguments to this {} are incorrect",
- call_name,
- )
- } else {
- tcx.sess.struct_span_err_with_code(
- full_call_span,
- &format!(
- "this {} takes {}{} but {} {} supplied",
- call_name,
- if c_variadic { "at least " } else { "" },
- potentially_plural_count(formal_and_expected_inputs.len(), "argument"),
- potentially_plural_count(provided_args.len(), "argument"),
- pluralize!("was", provided_args.len())
- ),
- DiagnosticId::Error(err_code.to_owned()),
- )
- };
-
- // As we encounter issues, keep track of what we want to provide for the suggestion
- let mut labels = vec![];
- // If there is a single error, we give a specific suggestion; otherwise, we change to
- // "did you mean" with the suggested function call
- enum SuggestionText {
- None,
- Provide(bool),
- Remove(bool),
- Swap,
- Reorder,
- DidYouMean,
- }
- let mut suggestion_text = SuggestionText::None;
-
- let mut errors = errors.into_iter().peekable();
- while let Some(error) = errors.next() {
- match error {
- Error::Invalid(provided_idx, expected_idx, compatibility) => {
- let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx];
- let (provided_ty, provided_span) = provided_arg_tys[provided_idx];
- if let Compatibility::Incompatible(error) = &compatibility {
- let cause = &self.misc(provided_span);
- let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
- if let Some(e) = error {
- self.note_type_err(
- &mut err,
- &trace.cause,
- None,
- Some(trace.values),
- e,
- false,
- true,
- );
- }
- }
-
- self.emit_coerce_suggestions(
- &mut err,
- &provided_args[provided_idx],
- provided_ty,
- Expectation::rvalue_hint(self, expected_ty)
- .only_has_type(self)
- .unwrap_or(formal_ty),
- None,
- None,
- );
- }
- Error::Extra(arg_idx) => {
- let (provided_ty, provided_span) = provided_arg_tys[arg_idx];
- let provided_ty_name = if !has_error_or_infer([provided_ty]) {
- // FIXME: not suggestable, use something else
- format!(" of type `{}`", provided_ty)
- } else {
- "".to_string()
- };
- labels
- .push((provided_span, format!("argument{} unexpected", provided_ty_name)));
- suggestion_text = match suggestion_text {
- SuggestionText::None => SuggestionText::Remove(false),
- SuggestionText::Remove(_) => SuggestionText::Remove(true),
- _ => SuggestionText::DidYouMean,
- };
- }
- Error::Missing(expected_idx) => {
- // If there are multiple missing arguments adjacent to each other,
- // then we can provide a single error.
-
- let mut missing_idxs = vec![expected_idx];
- while let Some(e) = errors.next_if(|e| {
- matches!(e, Error::Missing(next_expected_idx)
- if *next_expected_idx == *missing_idxs.last().unwrap() + 1)
- }) {
- match e {
- Error::Missing(expected_idx) => missing_idxs.push(expected_idx),
- _ => unreachable!(),
- }
- }
-
- // NOTE: Because we might be re-arranging arguments, might have extra
- // arguments, etc. it's hard to *really* know where we should provide
- // this error label, so as a heuristic, we point to the provided arg, or
- // to the call if the missing inputs pass the provided args.
- match &missing_idxs[..] {
- &[expected_idx] => {
- let (_, input_ty) = formal_and_expected_inputs[expected_idx];
- let span = if let Some((_, arg_span)) =
- provided_arg_tys.get(expected_idx.to_provided_idx())
- {
- *arg_span
- } else {
- args_span
- };
- let rendered = if !has_error_or_infer([input_ty]) {
- format!(" of type `{}`", input_ty)
- } else {
- "".to_string()
- };
- labels.push((span, format!("an argument{} is missing", rendered)));
- suggestion_text = match suggestion_text {
- SuggestionText::None => SuggestionText::Provide(false),
- SuggestionText::Provide(_) => SuggestionText::Provide(true),
- _ => SuggestionText::DidYouMean,
- };
- }
- &[first_idx, second_idx] => {
- let (_, first_expected_ty) = formal_and_expected_inputs[first_idx];
- let (_, second_expected_ty) = formal_and_expected_inputs[second_idx];
- let span = if let (Some((_, first_span)), Some((_, second_span))) = (
- provided_arg_tys.get(first_idx.to_provided_idx()),
- provided_arg_tys.get(second_idx.to_provided_idx()),
- ) {
- first_span.to(*second_span)
- } else {
- args_span
- };
- let rendered =
- if !has_error_or_infer([first_expected_ty, second_expected_ty]) {
- format!(
- " of type `{}` and `{}`",
- first_expected_ty, second_expected_ty
- )
- } else {
- "".to_string()
- };
- labels.push((span, format!("two arguments{} are missing", rendered)));
- suggestion_text = match suggestion_text {
- SuggestionText::None | SuggestionText::Provide(_) => {
- SuggestionText::Provide(true)
- }
- _ => SuggestionText::DidYouMean,
- };
- }
- &[first_idx, second_idx, third_idx] => {
- let (_, first_expected_ty) = formal_and_expected_inputs[first_idx];
- let (_, second_expected_ty) = formal_and_expected_inputs[second_idx];
- let (_, third_expected_ty) = formal_and_expected_inputs[third_idx];
- let span = if let (Some((_, first_span)), Some((_, third_span))) = (
- provided_arg_tys.get(first_idx.to_provided_idx()),
- provided_arg_tys.get(third_idx.to_provided_idx()),
- ) {
- first_span.to(*third_span)
- } else {
- args_span
- };
- let rendered = if !has_error_or_infer([
- first_expected_ty,
- second_expected_ty,
- third_expected_ty,
- ]) {
- format!(
- " of type `{}`, `{}`, and `{}`",
- first_expected_ty, second_expected_ty, third_expected_ty
- )
- } else {
- "".to_string()
- };
- labels.push((span, format!("three arguments{} are missing", rendered)));
- suggestion_text = match suggestion_text {
- SuggestionText::None | SuggestionText::Provide(_) => {
- SuggestionText::Provide(true)
- }
- _ => SuggestionText::DidYouMean,
- };
- }
- missing_idxs => {
- let first_idx = *missing_idxs.first().unwrap();
- let last_idx = *missing_idxs.last().unwrap();
- // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc.
- // It's hard to *really* know where we should provide this error label, so this is a
- // decent heuristic
- let span = if let (Some((_, first_span)), Some((_, last_span))) = (
- provided_arg_tys.get(first_idx.to_provided_idx()),
- provided_arg_tys.get(last_idx.to_provided_idx()),
- ) {
- first_span.to(*last_span)
- } else {
- args_span
- };
- labels.push((span, format!("multiple arguments are missing")));
- suggestion_text = match suggestion_text {
- SuggestionText::None | SuggestionText::Provide(_) => {
- SuggestionText::Provide(true)
- }
- _ => SuggestionText::DidYouMean,
- };
- }
- }
- }
- Error::Swap(
- first_provided_idx,
- second_provided_idx,
- first_expected_idx,
- second_expected_idx,
- ) => {
- let (first_provided_ty, first_span) = provided_arg_tys[first_provided_idx];
- let (_, first_expected_ty) = formal_and_expected_inputs[first_expected_idx];
- let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) {
- format!(", found `{}`", first_provided_ty)
- } else {
- String::new()
- };
- labels.push((
- first_span,
- format!("expected `{}`{}", first_expected_ty, first_provided_ty_name),
- ));
-
- let (second_provided_ty, second_span) = provided_arg_tys[second_provided_idx];
- let (_, second_expected_ty) = formal_and_expected_inputs[second_expected_idx];
- let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) {
- format!(", found `{}`", second_provided_ty)
- } else {
- String::new()
- };
- labels.push((
- second_span,
- format!("expected `{}`{}", second_expected_ty, second_provided_ty_name),
- ));
-
- suggestion_text = match suggestion_text {
- SuggestionText::None => SuggestionText::Swap,
- _ => SuggestionText::DidYouMean,
- };
- }
- Error::Permutation(args) => {
- for (dst_arg, dest_input) in args {
- let (_, expected_ty) = formal_and_expected_inputs[dst_arg];
- let (provided_ty, provided_span) = provided_arg_tys[dest_input];
- let provided_ty_name = if !has_error_or_infer([provided_ty]) {
- format!(", found `{}`", provided_ty)
- } else {
- String::new()
- };
- labels.push((
- provided_span,
- format!("expected `{}`{}", expected_ty, provided_ty_name),
- ));
- }
-
- suggestion_text = match suggestion_text {
- SuggestionText::None => SuggestionText::Reorder,
- _ => SuggestionText::DidYouMean,
- };
- }
- }
- }
-
- // If we have less than 5 things to say, it would be useful to call out exactly what's wrong
- if labels.len() <= 5 {
- for (span, label) in labels {
- err.span_label(span, label);
- }
- }
-
- // Call out where the function is defined
- self.label_fn_like(&mut err, fn_def_id, callee_ty);
-
- // And add a suggestion block for all of the parameters
- let suggestion_text = match suggestion_text {
- SuggestionText::None => None,
- SuggestionText::Provide(plural) => {
- Some(format!("provide the argument{}", if plural { "s" } else { "" }))
- }
- SuggestionText::Remove(plural) => {
- Some(format!("remove the extra argument{}", if plural { "s" } else { "" }))
- }
- SuggestionText::Swap => Some("swap these arguments".to_string()),
- SuggestionText::Reorder => Some("reorder these arguments".to_string()),
- SuggestionText::DidYouMean => Some("did you mean".to_string()),
- };
- if let Some(suggestion_text) = suggestion_text {
- let source_map = self.sess().source_map();
- let mut suggestion = format!(
- "{}(",
- source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| fn_def_id
- .map_or("".to_string(), |fn_def_id| tcx.item_name(fn_def_id).to_string()))
- );
- let mut needs_comma = false;
- for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() {
- if needs_comma {
- suggestion += ", ";
- } else {
- needs_comma = true;
- }
- let suggestion_text = if let Some(provided_idx) = provided_idx
- && let (_, provided_span) = provided_arg_tys[*provided_idx]
- && let Ok(arg_text) =
- source_map.span_to_snippet(provided_span)
- {
- arg_text
- } else {
- // Propose a placeholder of the correct type
- let (_, expected_ty) = formal_and_expected_inputs[expected_idx];
- if expected_ty.is_unit() {
- "()".to_string()
- } else if expected_ty.is_suggestable(tcx, false) {
- format!("/* {} */", expected_ty)
- } else {
- "/* value */".to_string()
- }
- };
- suggestion += &suggestion_text;
- }
- suggestion += ")";
- err.span_suggestion_verbose(
- error_span,
- &suggestion_text,
- suggestion,
- Applicability::HasPlaceholders,
- );
- }
-
- err.emit();
- }
-
- // AST fragment checking
- pub(in super::super) fn check_lit(
- &self,
- lit: &hir::Lit,
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- let tcx = self.tcx;
-
- match lit.node {
- ast::LitKind::Str(..) => tcx.mk_static_str(),
- ast::LitKind::ByteStr(ref v) => {
- tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64))
- }
- ast::LitKind::Byte(_) => tcx.types.u8,
- ast::LitKind::Char(_) => tcx.types.char,
- ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(ty::int_ty(t)),
- ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(ty::uint_ty(t)),
- ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => {
- let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
- ty::Int(_) | ty::Uint(_) => Some(ty),
- ty::Char => Some(tcx.types.u8),
- ty::RawPtr(..) => Some(tcx.types.usize),
- ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize),
- _ => None,
- });
- opt_ty.unwrap_or_else(|| self.next_int_var())
- }
- ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => {
- tcx.mk_mach_float(ty::float_ty(t))
- }
- ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => {
- let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
- ty::Float(_) => Some(ty),
- _ => None,
- });
- opt_ty.unwrap_or_else(|| self.next_float_var())
- }
- ast::LitKind::Bool(_) => tcx.types.bool,
- ast::LitKind::Err(_) => tcx.ty_error(),
- }
- }
-
- pub fn check_struct_path(
- &self,
- qpath: &QPath<'_>,
- hir_id: hir::HirId,
- ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> {
- let path_span = qpath.span();
- let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id);
- let variant = match def {
- Res::Err => {
- self.set_tainted_by_errors();
- return None;
- }
- Res::Def(DefKind::Variant, _) => match ty.kind() {
- ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did(), substs)),
- _ => bug!("unexpected type: {:?}", ty),
- },
- Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
- | Res::SelfTy { .. } => match ty.kind() {
- ty::Adt(adt, substs) if !adt.is_enum() => {
- Some((adt.non_enum_variant(), adt.did(), substs))
- }
- _ => None,
- },
- _ => bug!("unexpected definition: {:?}", def),
- };
-
- if let Some((variant, did, substs)) = variant {
- debug!("check_struct_path: did={:?} substs={:?}", did, substs);
- self.write_user_type_annotation_from_substs(hir_id, did, substs, None);
-
- // Check bounds on type arguments used in the path.
- self.add_required_obligations(path_span, did, substs);
-
- Some((variant, ty))
- } else {
- match ty.kind() {
- ty::Error(_) => {
- // E0071 might be caused by a spelling error, which will have
- // already caused an error message and probably a suggestion
- // elsewhere. Refrain from emitting more unhelpful errors here
- // (issue #88844).
- }
- _ => {
- struct_span_err!(
- self.tcx.sess,
- path_span,
- E0071,
- "expected struct, variant or union type, found {}",
- ty.sort_string(self.tcx)
- )
- .span_label(path_span, "not a struct")
- .emit();
- }
- }
- None
- }
- }
-
- pub fn check_decl_initializer(
- &self,
- hir_id: hir::HirId,
- pat: &'tcx hir::Pat<'tcx>,
- init: &'tcx hir::Expr<'tcx>,
- ) -> Ty<'tcx> {
- // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed
- // for #42640 (default match binding modes).
- //
- // See #44848.
- let ref_bindings = pat.contains_explicit_ref_binding();
-
- let local_ty = self.local_ty(init.span, hir_id).revealed_ty;
- if let Some(m) = ref_bindings {
- // Somewhat subtle: if we have a `ref` binding in the pattern,
- // we want to avoid introducing coercions for the RHS. This is
- // both because it helps preserve sanity and, in the case of
- // ref mut, for soundness (issue #23116). In particular, in
- // the latter case, we need to be clear that the type of the
- // referent for the reference that results is *equal to* the
- // type of the place it is referencing, and not some
- // supertype thereof.
- let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
- self.demand_eqtype(init.span, local_ty, init_ty);
- init_ty
- } else {
- self.check_expr_coercable_to_type(init, local_ty, None)
- }
- }
-
- pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) {
- // Determine and write the type which we'll check the pattern against.
- let decl_ty = self.local_ty(decl.span, decl.hir_id).decl_ty;
- self.write_ty(decl.hir_id, decl_ty);
-
- // Type check the initializer.
- if let Some(ref init) = decl.init {
- let init_ty = self.check_decl_initializer(decl.hir_id, decl.pat, &init);
- self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, init_ty);
- }
-
- // Does the expected pattern type originate from an expression and what is the span?
- let (origin_expr, ty_span) = match (decl.ty, decl.init) {
- (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
- (_, Some(init)) => {
- (true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span)))
- } // No explicit type; so use the scrutinee.
- _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
- };
-
- // Type check the pattern. Override if necessary to avoid knock-on errors.
- self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr);
- let pat_ty = self.node_ty(decl.pat.hir_id);
- self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, pat_ty);
-
- if let Some(blk) = decl.els {
- let previous_diverges = self.diverges.get();
- let else_ty = self.check_block_with_expected(blk, NoExpectation);
- let cause = self.cause(blk.span, ObligationCauseCode::LetElse);
- if let Some(mut err) =
- self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty)
- {
- err.emit();
- }
- self.diverges.set(previous_diverges);
- }
- }
-
- /// Type check a `let` statement.
- pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
- self.check_decl(local.into());
- }
-
- pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>, is_last: bool) {
- // Don't do all the complex logic below for `DeclItem`.
- match stmt.kind {
- hir::StmtKind::Item(..) => return,
- hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {}
- }
-
- self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement");
-
- // Hide the outer diverging and `has_errors` flags.
- let old_diverges = self.diverges.replace(Diverges::Maybe);
- let old_has_errors = self.has_errors.replace(false);
-
- match stmt.kind {
- hir::StmtKind::Local(l) => {
- self.check_decl_local(l);
- }
- // Ignore for now.
- hir::StmtKind::Item(_) => {}
- hir::StmtKind::Expr(ref expr) => {
- // Check with expected type of `()`.
- self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| {
- if expr.can_have_side_effects() {
- self.suggest_semicolon_at_end(expr.span, err);
- }
- });
- }
- hir::StmtKind::Semi(ref expr) => {
- // All of this is equivalent to calling `check_expr`, but it is inlined out here
- // in order to capture the fact that this `match` is the last statement in its
- // function. This is done for better suggestions to remove the `;`.
- let expectation = match expr.kind {
- hir::ExprKind::Match(..) if is_last => IsLast(stmt.span),
- _ => NoExpectation,
- };
- self.check_expr_with_expectation(expr, expectation);
- }
- }
-
- // Combine the diverging and `has_error` flags.
- self.diverges.set(self.diverges.get() | old_diverges);
- self.has_errors.set(self.has_errors.get() | old_has_errors);
- }
-
- pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) {
- let unit = self.tcx.mk_unit();
- let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
-
- // if the block produces a `!` value, that can always be
- // (effectively) coerced to unit.
- if !ty.is_never() {
- self.demand_suptype(blk.span, unit, ty);
- }
- }
-
- pub(in super::super) fn check_block_with_expected(
- &self,
- blk: &'tcx hir::Block<'tcx>,
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- let prev = self.ps.replace(self.ps.get().recurse(blk));
-
- // In some cases, blocks have just one exit, but other blocks
- // can be targeted by multiple breaks. This can happen both
- // with labeled blocks as well as when we desugar
- // a `try { ... }` expression.
- //
- // Example 1:
- //
- // 'a: { if true { break 'a Err(()); } Ok(()) }
- //
- // Here we would wind up with two coercions, one from
- // `Err(())` and the other from the tail expression
- // `Ok(())`. If the tail expression is omitted, that's a
- // "forced unit" -- unless the block diverges, in which
- // case we can ignore the tail expression (e.g., `'a: {
- // break 'a 22; }` would not force the type of the block
- // to be `()`).
- let tail_expr = blk.expr.as_ref();
- let coerce_to_ty = expected.coercion_target_type(self, blk.span);
- let coerce = if blk.targeted_by_break {
- CoerceMany::new(coerce_to_ty)
- } else {
- let tail_expr: &[&hir::Expr<'_>] = match tail_expr {
- Some(e) => slice::from_ref(e),
- None => &[],
- };
- CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr)
- };
-
- let prev_diverges = self.diverges.get();
- let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false };
-
- let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || {
- for (pos, s) in blk.stmts.iter().enumerate() {
- self.check_stmt(s, blk.stmts.len() - 1 == pos);
- }
-
- // check the tail expression **without** holding the
- // `enclosing_breakables` lock below.
- let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
-
- let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
- let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
- let coerce = ctxt.coerce.as_mut().unwrap();
- if let Some(tail_expr_ty) = tail_expr_ty {
- let tail_expr = tail_expr.unwrap();
- let span = self.get_expr_coercion_span(tail_expr);
- let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
- let ty_for_diagnostic = coerce.merged_ty();
- // We use coerce_inner here because we want to augment the error
- // suggesting to wrap the block in square brackets if it might've
- // been mistaken array syntax
- coerce.coerce_inner(
- self,
- &cause,
- Some(tail_expr),
- tail_expr_ty,
- Some(&mut |diag: &mut Diagnostic| {
- self.suggest_block_to_brackets(diag, blk, tail_expr_ty, ty_for_diagnostic);
- }),
- false,
- );
- } else {
- // Subtle: if there is no explicit tail expression,
- // that is typically equivalent to a tail expression
- // of `()` -- except if the block diverges. In that
- // case, there is no value supplied from the tail
- // expression (assuming there are no other breaks,
- // this implies that the type of the block will be
- // `!`).
- //
- // #41425 -- label the implicit `()` as being the
- // "found type" here, rather than the "expected type".
- if !self.diverges.get().is_always() {
- // #50009 -- Do not point at the entire fn block span, point at the return type
- // span, as it is the cause of the requirement, and
- // `consider_hint_about_removing_semicolon` will point at the last expression
- // if it were a relevant part of the error. This improves usability in editors
- // that highlight errors inline.
- let mut sp = blk.span;
- let mut fn_span = None;
- if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) {
- let ret_sp = decl.output.span();
- if let Some(block_sp) = self.parent_item_span(blk.hir_id) {
- // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
- // output would otherwise be incorrect and even misleading. Make sure
- // the span we're aiming at correspond to a `fn` body.
- if block_sp == blk.span {
- sp = ret_sp;
- fn_span = Some(ident.span);
- }
- }
- }
- coerce.coerce_forced_unit(
- self,
- &self.misc(sp),
- &mut |err| {
- if let Some(expected_ty) = expected.only_has_type(self) {
- if !self.consider_removing_semicolon(blk, expected_ty, err) {
- self.consider_returning_binding(blk, expected_ty, err);
- }
- if expected_ty == self.tcx.types.bool {
- // If this is caused by a missing `let` in a `while let`,
- // silence this redundant error, as we already emit E0070.
-
- // Our block must be a `assign desugar local; assignment`
- if let Some(hir::Node::Block(hir::Block {
- stmts:
- [
- hir::Stmt {
- kind:
- hir::StmtKind::Local(hir::Local {
- source:
- hir::LocalSource::AssignDesugar(_),
- ..
- }),
- ..
- },
- hir::Stmt {
- kind:
- hir::StmtKind::Expr(hir::Expr {
- kind: hir::ExprKind::Assign(..),
- ..
- }),
- ..
- },
- ],
- ..
- })) = self.tcx.hir().find(blk.hir_id)
- {
- self.comes_from_while_condition(blk.hir_id, |_| {
- err.downgrade_to_delayed_bug();
- })
- }
- }
- }
- if let Some(fn_span) = fn_span {
- err.span_label(
- fn_span,
- "implicitly returns `()` as its body has no tail or `return` \
- expression",
- );
- }
- },
- false,
- );
- }
- }
- });
-
- if ctxt.may_break {
- // If we can break from the block, then the block's exit is always reachable
- // (... as long as the entry is reachable) - regardless of the tail of the block.
- self.diverges.set(prev_diverges);
- }
-
- let mut ty = ctxt.coerce.unwrap().complete(self);
-
- if self.has_errors.get() || ty.references_error() {
- ty = self.tcx.ty_error()
- }
-
- self.write_ty(blk.hir_id, ty);
-
- self.ps.set(prev);
- ty
- }
-
- fn parent_item_span(&self, id: hir::HirId) -> Option<Span> {
- let node = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(id));
- match node {
- Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })
- | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => {
- let body = self.tcx.hir().body(body_id);
- if let ExprKind::Block(block, _) = &body.value.kind {
- return Some(block.span);
- }
- }
- _ => {}
- }
- None
- }
-
- /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise.
- fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
- let parent = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(blk_id));
- self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident))
- }
-
- /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail
- /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors
- /// when given code like the following:
- /// ```text
- /// if false { return 0i32; } else { 1u32 }
- /// // ^^^^ point at this instead of the whole `if` expression
- /// ```
- fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span {
- let check_in_progress = |elem: &hir::Expr<'_>| {
- self.typeck_results.borrow().node_type_opt(elem.hir_id).filter(|ty| !ty.is_never()).map(
- |_| match elem.kind {
- // Point at the tail expression when possible.
- hir::ExprKind::Block(block, _) => block.expr.map_or(block.span, |e| e.span),
- _ => elem.span,
- },
- )
- };
-
- if let hir::ExprKind::If(_, _, Some(el)) = expr.kind {
- if let Some(rslt) = check_in_progress(el) {
- return rslt;
- }
- }
-
- if let hir::ExprKind::Match(_, arms, _) = expr.kind {
- let mut iter = arms.iter().filter_map(|arm| check_in_progress(arm.body));
- if let Some(span) = iter.next() {
- if iter.next().is_none() {
- return span;
- }
- }
- }
-
- expr.span
- }
-
- fn overwrite_local_ty_if_err(
- &self,
- hir_id: hir::HirId,
- pat: &'tcx hir::Pat<'tcx>,
- decl_ty: Ty<'tcx>,
- ty: Ty<'tcx>,
- ) {
- if ty.references_error() {
- // Override the types everywhere with `err()` to avoid knock on errors.
- self.write_ty(hir_id, ty);
- self.write_ty(pat.hir_id, ty);
- let local_ty = LocalTy { decl_ty, revealed_ty: ty };
- self.locals.borrow_mut().insert(hir_id, local_ty);
- self.locals.borrow_mut().insert(pat.hir_id, local_ty);
- }
- }
-
- // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary.
- // The newly resolved definition is written into `type_dependent_defs`.
- fn finish_resolving_struct_path(
- &self,
- qpath: &QPath<'_>,
- path_span: Span,
- hir_id: hir::HirId,
- ) -> (Res, Ty<'tcx>) {
- match *qpath {
- QPath::Resolved(ref maybe_qself, ref path) => {
- let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself));
- let ty = <dyn AstConv<'_>>::res_to_ty(self, self_ty, path, true);
- (path.res, ty)
- }
- QPath::TypeRelative(ref qself, ref segment) => {
- let ty = self.to_ty(qself);
-
- let result = <dyn AstConv<'_>>::associated_path_to_ty(
- self, hir_id, path_span, ty, qself, segment, true,
- );
- let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error());
- let result = result.map(|(_, kind, def_id)| (kind, def_id));
-
- // Write back the new resolution.
- self.write_resolution(hir_id, result);
-
- (result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty)
- }
- QPath::LangItem(lang_item, span, id) => {
- self.resolve_lang_item_path(lang_item, span, hir_id, id)
- }
- }
- }
-
- /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk
- /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s
- /// reference a type argument. The reason to walk also the checked type is that the coerced type
- /// can be not easily comparable with predicate type (because of coercion). If the types match
- /// for either checked or coerced type, and there's only *one* argument that does, we point at
- /// the corresponding argument's expression span instead of the `fn` call path span.
- fn point_at_arg_instead_of_call_if_possible(
- &self,
- errors: &mut Vec<traits::FulfillmentError<'tcx>>,
- expr: &'tcx hir::Expr<'tcx>,
- call_sp: Span,
- args: &'tcx [hir::Expr<'tcx>],
- expected_tys: &[Ty<'tcx>],
- ) {
- // We *do not* do this for desugared call spans to keep good diagnostics when involving
- // the `?` operator.
- if call_sp.desugaring_kind().is_some() {
- return;
- }
-
- 'outer: for error in errors {
- // Only if the cause is somewhere inside the expression we want try to point at arg.
- // Otherwise, it means that the cause is somewhere else and we should not change
- // anything because we can break the correct span.
- if !call_sp.contains(error.obligation.cause.span) {
- continue;
- }
-
- // Peel derived obligation, because it's the type that originally
- // started this inference chain that matters, not the one we wound
- // up with at the end.
- fn unpeel_to_top<'a, 'tcx>(
- mut code: &'a ObligationCauseCode<'tcx>,
- ) -> &'a ObligationCauseCode<'tcx> {
- let mut result_code = code;
- loop {
- let parent = match code {
- ObligationCauseCode::ImplDerivedObligation(c) => &c.derived.parent_code,
- ObligationCauseCode::BuiltinDerivedObligation(c)
- | ObligationCauseCode::DerivedObligation(c) => &c.parent_code,
- _ => break result_code,
- };
- (result_code, code) = (code, parent);
- }
- }
- let self_: ty::subst::GenericArg<'_> =
- match unpeel_to_top(error.obligation.cause.code()) {
- ObligationCauseCode::BuiltinDerivedObligation(code)
- | ObligationCauseCode::DerivedObligation(code) => {
- code.parent_trait_pred.self_ty().skip_binder().into()
- }
- ObligationCauseCode::ImplDerivedObligation(code) => {
- code.derived.parent_trait_pred.self_ty().skip_binder().into()
- }
- _ if let ty::PredicateKind::Trait(predicate) =
- error.obligation.predicate.kind().skip_binder() =>
- {
- predicate.self_ty().into()
- }
- _ => continue,
- };
- let self_ = self.resolve_vars_if_possible(self_);
- let ty_matches_self = |ty: Ty<'tcx>| ty.walk().any(|arg| arg == self_);
-
- let typeck_results = self.typeck_results.borrow();
-
- for (idx, arg) in args.iter().enumerate() {
- // Don't adjust the span if we already have a more precise span
- // within one of the args.
- if arg.span.contains(error.obligation.cause.span) {
- let references_arg =
- typeck_results.expr_ty_opt(arg).map_or(false, &ty_matches_self)
- || expected_tys.get(idx).copied().map_or(false, &ty_matches_self);
- if references_arg && !arg.span.from_expansion() {
- error.obligation.cause.map_code(|parent_code| {
- ObligationCauseCode::FunctionArgumentObligation {
- arg_hir_id: args[idx].hir_id,
- call_hir_id: expr.hir_id,
- parent_code,
- }
- })
- }
- continue 'outer;
- }
- }
-
- // Collect the argument position for all arguments that could have caused this
- // `FulfillmentError`.
- let mut referenced_in: Vec<_> = std::iter::zip(expected_tys, args)
- .enumerate()
- .flat_map(|(idx, (expected_ty, arg))| {
- if let Some(arg_ty) = typeck_results.expr_ty_opt(arg) {
- vec![(idx, arg_ty), (idx, *expected_ty)]
- } else {
- vec![]
- }
- })
- .filter_map(|(i, ty)| {
- let ty = self.resolve_vars_if_possible(ty);
- // We walk the argument type because the argument's type could have
- // been `Option<T>`, but the `FulfillmentError` references `T`.
- if ty_matches_self(ty) { Some(i) } else { None }
- })
- .collect();
-
- // Both checked and coerced types could have matched, thus we need to remove
- // duplicates.
-
- // We sort primitive type usize here and can use unstable sort
- referenced_in.sort_unstable();
- referenced_in.dedup();
-
- if let &[idx] = &referenced_in[..] {
- // Do not point at the inside of a macro.
- // That would often result in poor error messages.
- if args[idx].span.from_expansion() {
- continue;
- }
- // We make sure that only *one* argument matches the obligation failure
- // and we assign the obligation's span to its expression's.
- error.obligation.cause.span = args[idx].span;
- error.obligation.cause.map_code(|parent_code| {
- ObligationCauseCode::FunctionArgumentObligation {
- arg_hir_id: args[idx].hir_id,
- call_hir_id: expr.hir_id,
- parent_code,
- }
- });
- } else if error.obligation.cause.span == call_sp {
- // Make function calls point at the callee, not the whole thing.
- if let hir::ExprKind::Call(callee, _) = expr.kind {
- error.obligation.cause.span = callee.span;
- }
- }
- }
- }
-
- /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the
- /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s
- /// were caused by them. If they were, we point at the corresponding type argument's span
- /// instead of the `fn` call path span.
- fn point_at_type_arg_instead_of_call_if_possible(
- &self,
- errors: &mut Vec<traits::FulfillmentError<'tcx>>,
- call_expr: &'tcx hir::Expr<'tcx>,
- ) {
- if let hir::ExprKind::Call(path, _) = &call_expr.kind {
- if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &path.kind {
- for error in errors {
- if let ty::PredicateKind::Trait(predicate) =
- error.obligation.predicate.kind().skip_binder()
- {
- // If any of the type arguments in this path segment caused the
- // `FulfillmentError`, point at its span (#61860).
- for arg in path
- .segments
- .iter()
- .filter_map(|seg| seg.args.as_ref())
- .flat_map(|a| a.args.iter())
- {
- if let hir::GenericArg::Type(hir_ty) = &arg
- && let Some(ty) =
- self.typeck_results.borrow().node_type_opt(hir_ty.hir_id)
- && self.resolve_vars_if_possible(ty) == predicate.self_ty()
- {
- error.obligation.cause.span = hir_ty.span;
- break;
- }
- }
- }
- }
- }
- }
- }
-
- fn label_fn_like(
- &self,
- err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>,
- callable_def_id: Option<DefId>,
- callee_ty: Option<Ty<'tcx>>,
- ) {
- let Some(mut def_id) = callable_def_id else {
- return;
- };
-
- if let Some(assoc_item) = self.tcx.opt_associated_item(def_id)
- // Possibly points at either impl or trait item, so try to get it
- // to point to trait item, then get the parent.
- // This parent might be an impl in the case of an inherent function,
- // but the next check will fail.
- && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id)
- && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id)
- // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce"
- && let Some(call_kind) = ty::ClosureKind::from_def_id(self.tcx, maybe_trait_def_id)
- && let Some(callee_ty) = callee_ty
- {
- let callee_ty = callee_ty.peel_refs();
- match *callee_ty.kind() {
- ty::Param(param) => {
- let param =
- self.tcx.generics_of(self.body_id.owner).type_param(&param, self.tcx);
- if param.kind.is_synthetic() {
- // if it's `impl Fn() -> ..` then just fall down to the def-id based logic
- def_id = param.def_id;
- } else {
- // Otherwise, find the predicate that makes this generic callable,
- // and point at that.
- let instantiated = self
- .tcx
- .explicit_predicates_of(self.body_id.owner)
- .instantiate_identity(self.tcx);
- // FIXME(compiler-errors): This could be problematic if something has two
- // fn-like predicates with different args, but callable types really never
- // do that, so it's OK.
- for (predicate, span) in
- std::iter::zip(instantiated.predicates, instantiated.spans)
- {
- if let ty::PredicateKind::Trait(pred) = predicate.kind().skip_binder()
- && pred.self_ty().peel_refs() == callee_ty
- && ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some()
- {
- err.span_note(span, "callable defined here");
- return;
- }
- }
- }
- }
- ty::Opaque(new_def_id, _)
- | ty::Closure(new_def_id, _)
- | ty::FnDef(new_def_id, _) => {
- def_id = new_def_id;
- }
- _ => {
- // Look for a user-provided impl of a `Fn` trait, and point to it.
- let new_def_id = self.probe(|_| {
- let trait_ref = ty::TraitRef::new(
- call_kind.to_def_id(self.tcx),
- self.tcx.mk_substs([
- ty::GenericArg::from(callee_ty),
- self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::MiscVariable,
- span: rustc_span::DUMMY_SP,
- })
- .into(),
- ].into_iter()),
- );
- let obligation = traits::Obligation::new(
- traits::ObligationCause::dummy(),
- self.param_env,
- ty::Binder::dummy(ty::TraitPredicate {
- trait_ref,
- constness: ty::BoundConstness::NotConst,
- polarity: ty::ImplPolarity::Positive,
- }),
- );
- match SelectionContext::new(&self).select(&obligation) {
- Ok(Some(traits::ImplSource::UserDefined(impl_source))) => {
- Some(impl_source.impl_def_id)
- }
- _ => None
- }
- });
- if let Some(new_def_id) = new_def_id {
- def_id = new_def_id;
- } else {
- return;
- }
- }
- }
- }
-
- if let Some(def_span) = self.tcx.def_ident_span(def_id) && !def_span.is_dummy() {
- let mut spans: MultiSpan = def_span.into();
-
- let params = self
- .tcx
- .hir()
- .get_if_local(def_id)
- .and_then(|node| node.body_id())
- .into_iter()
- .flat_map(|id| self.tcx.hir().body(id).params);
-
- for param in params {
- spans.push_span_label(param.span, "");
- }
-
- let def_kind = self.tcx.def_kind(def_id);
- err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
- } else {
- let def_kind = self.tcx.def_kind(def_id);
- err.span_note(
- self.tcx.def_span(def_id),
- &format!("{} defined here", def_kind.descr(def_id)),
- );
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs
deleted file mode 100644
index 05bcc710e..000000000
--- a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs
+++ /dev/null
@@ -1,296 +0,0 @@
-mod _impl;
-mod arg_matrix;
-mod checks;
-mod suggestions;
-
-pub use _impl::*;
-pub use suggestions::*;
-
-use crate::astconv::AstConv;
-use crate::check::coercion::DynamicCoerceMany;
-use crate::check::{Diverges, EnclosingBreakables, Inherited, UnsafetyState};
-
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_infer::infer;
-use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
-use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{self, Const, Ty, TyCtxt};
-use rustc_session::Session;
-use rustc_span::symbol::Ident;
-use rustc_span::{self, Span};
-use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
-
-use std::cell::{Cell, RefCell};
-use std::ops::Deref;
-
-pub struct FnCtxt<'a, 'tcx> {
- pub(super) body_id: hir::HirId,
-
- /// The parameter environment used for proving trait obligations
- /// in this function. This can change when we descend into
- /// closures (as they bring new things into scope), hence it is
- /// not part of `Inherited` (as of the time of this writing,
- /// closures do not yet change the environment, but they will
- /// eventually).
- pub(super) param_env: ty::ParamEnv<'tcx>,
-
- /// Number of errors that had been reported when we started
- /// checking this function. On exit, if we find that *more* errors
- /// have been reported, we will skip regionck and other work that
- /// expects the types within the function to be consistent.
- // FIXME(matthewjasper) This should not exist, and it's not correct
- // if type checking is run in parallel.
- err_count_on_creation: usize,
-
- /// If `Some`, this stores coercion information for returned
- /// expressions. If `None`, this is in a context where return is
- /// inappropriate, such as a const expression.
- ///
- /// This is a `RefCell<DynamicCoerceMany>`, which means that we
- /// can track all the return expressions and then use them to
- /// compute a useful coercion from the set, similar to a match
- /// expression or other branching context. You can use methods
- /// like `expected_ty` to access the declared return type (if
- /// any).
- pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,
-
- pub(super) ret_type_span: Option<Span>,
-
- /// Used exclusively to reduce cost of advanced evaluation used for
- /// more helpful diagnostics.
- pub(super) in_tail_expr: bool,
-
- /// First span of a return site that we find. Used in error messages.
- pub(super) ret_coercion_span: Cell<Option<Span>>,
-
- pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>,
-
- pub(super) ps: Cell<UnsafetyState>,
-
- /// Whether the last checked node generates a divergence (e.g.,
- /// `return` will set this to `Always`). In general, when entering
- /// an expression or other node in the tree, the initial value
- /// indicates whether prior parts of the containing expression may
- /// have diverged. It is then typically set to `Maybe` (and the
- /// old value remembered) for processing the subparts of the
- /// current expression. As each subpart is processed, they may set
- /// the flag to `Always`, etc. Finally, at the end, we take the
- /// result and "union" it with the original value, so that when we
- /// return the flag indicates if any subpart of the parent
- /// expression (up to and including this part) has diverged. So,
- /// if you read it after evaluating a subexpression `X`, the value
- /// you get indicates whether any subexpression that was
- /// evaluating up to and including `X` diverged.
- ///
- /// We currently use this flag only for diagnostic purposes:
- ///
- /// - To warn about unreachable code: if, after processing a
- /// sub-expression but before we have applied the effects of the
- /// current node, we see that the flag is set to `Always`, we
- /// can issue a warning. This corresponds to something like
- /// `foo(return)`; we warn on the `foo()` expression. (We then
- /// update the flag to `WarnedAlways` to suppress duplicate
- /// reports.) Similarly, if we traverse to a fresh statement (or
- /// tail expression) from an `Always` setting, we will issue a
- /// warning. This corresponds to something like `{return;
- /// foo();}` or `{return; 22}`, where we would warn on the
- /// `foo()` or `22`.
- ///
- /// An expression represents dead code if, after checking it,
- /// the diverges flag is set to something other than `Maybe`.
- pub(super) diverges: Cell<Diverges>,
-
- /// Whether any child nodes have any type errors.
- pub(super) has_errors: Cell<bool>,
-
- pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
-
- pub(super) inh: &'a Inherited<'a, 'tcx>,
-
- /// True if the function or closure's return type is known before
- /// entering the function/closure, i.e. if the return type is
- /// either given explicitly or inferred from, say, an `Fn*` trait
- /// bound. Used for diagnostic purposes only.
- pub(super) return_type_pre_known: bool,
-
- /// True if the return type has an Opaque type
- pub(super) return_type_has_opaque: bool,
-}
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- pub fn new(
- inh: &'a Inherited<'a, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
- ) -> FnCtxt<'a, 'tcx> {
- FnCtxt {
- body_id,
- param_env,
- err_count_on_creation: inh.tcx.sess.err_count(),
- ret_coercion: None,
- ret_type_span: None,
- in_tail_expr: false,
- ret_coercion_span: Cell::new(None),
- resume_yield_tys: None,
- ps: Cell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)),
- diverges: Cell::new(Diverges::Maybe),
- has_errors: Cell::new(false),
- enclosing_breakables: RefCell::new(EnclosingBreakables {
- stack: Vec::new(),
- by_id: Default::default(),
- }),
- inh,
- return_type_pre_known: true,
- return_type_has_opaque: false,
- }
- }
-
- pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> {
- ObligationCause::new(span, self.body_id, code)
- }
-
- pub fn misc(&self, span: Span) -> ObligationCause<'tcx> {
- self.cause(span, ObligationCauseCode::MiscObligation)
- }
-
- pub fn sess(&self) -> &Session {
- &self.tcx.sess
- }
-
- pub fn errors_reported_since_creation(&self) -> bool {
- self.tcx.sess.err_count() > self.err_count_on_creation
- }
-}
-
-impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
- type Target = Inherited<'a, 'tcx>;
- fn deref(&self) -> &Self::Target {
- &self.inh
- }
-}
-
-impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
- fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
- self.tcx
- }
-
- fn item_def_id(&self) -> Option<DefId> {
- None
- }
-
- fn get_type_parameter_bounds(
- &self,
- _: Span,
- def_id: DefId,
- _: Ident,
- ) -> ty::GenericPredicates<'tcx> {
- let tcx = self.tcx;
- let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local());
- let generics = tcx.generics_of(item_def_id);
- let index = generics.param_def_id_to_index[&def_id];
- ty::GenericPredicates {
- parent: None,
- predicates: tcx.arena.alloc_from_iter(
- self.param_env.caller_bounds().iter().filter_map(|predicate| {
- match predicate.kind().skip_binder() {
- ty::PredicateKind::Trait(data) if data.self_ty().is_param(index) => {
- // HACK(eddyb) should get the original `Span`.
- let span = tcx.def_span(def_id);
- Some((predicate, span))
- }
- _ => None,
- }
- }),
- ),
- }
- }
-
- fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> {
- let v = match def {
- Some(def) => infer::EarlyBoundRegion(span, def.name),
- None => infer::MiscVariable(span),
- };
- Some(self.next_region_var(v))
- }
-
- fn allow_ty_infer(&self) -> bool {
- true
- }
-
- fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
- if let Some(param) = param {
- if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() {
- return ty;
- }
- unreachable!()
- } else {
- self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::TypeInference,
- span,
- })
- }
- }
-
- fn ct_infer(
- &self,
- ty: Ty<'tcx>,
- param: Option<&ty::GenericParamDef>,
- span: Span,
- ) -> Const<'tcx> {
- if let Some(param) = param {
- if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() {
- return ct;
- }
- unreachable!()
- } else {
- self.next_const_var(
- ty,
- ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span },
- )
- }
- }
-
- fn projected_ty_from_poly_trait_ref(
- &self,
- span: Span,
- item_def_id: DefId,
- item_segment: &hir::PathSegment<'_>,
- poly_trait_ref: ty::PolyTraitRef<'tcx>,
- ) -> Ty<'tcx> {
- let trait_ref = self.replace_bound_vars_with_fresh_vars(
- span,
- infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id),
- poly_trait_ref,
- );
-
- let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item(
- self,
- self.tcx,
- span,
- item_def_id,
- item_segment,
- trait_ref.substs,
- );
-
- self.tcx().mk_projection(item_def_id, item_substs)
- }
-
- fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
- if ty.has_escaping_bound_vars() {
- ty // FIXME: normalization and escaping regions
- } else {
- self.normalize_associated_types_in(span, ty)
- }
- }
-
- fn set_tainted_by_errors(&self) {
- self.infcx.set_tainted_by_errors()
- }
-
- fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) {
- self.write_ty(hir_id, ty)
- }
-}
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
deleted file mode 100644
index 57771e096..000000000
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ /dev/null
@@ -1,912 +0,0 @@
-use super::FnCtxt;
-use crate::astconv::AstConv;
-use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
-
-use rustc_ast::util::parser::ExprPrecedence;
-use rustc_errors::{Applicability, Diagnostic, MultiSpan};
-use rustc_hir as hir;
-use rustc_hir::def::{CtorOf, DefKind};
-use rustc_hir::lang_items::LangItem;
-use rustc_hir::{
- Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
-};
-use rustc_infer::infer::{self, TyCtxtInferExt};
-use rustc_infer::traits::{self, StatementAsExpression};
-use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty};
-use rustc_span::symbol::sym;
-use rustc_span::Span;
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) {
- err.span_suggestion_short(
- span.shrink_to_hi(),
- "consider using a semicolon here",
- ";",
- Applicability::MachineApplicable,
- );
- }
-
- /// On implicit return expressions with mismatched types, provides the following suggestions:
- ///
- /// - Points out the method's return type as the reason for the expected type.
- /// - Possible missing semicolon.
- /// - Possible missing return type if the return type is the default, and not `fn main()`.
- pub fn suggest_mismatched_types_on_tail(
- &self,
- err: &mut Diagnostic,
- expr: &'tcx hir::Expr<'tcx>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- blk_id: hir::HirId,
- ) -> bool {
- let expr = expr.peel_drop_temps();
- self.suggest_missing_semicolon(err, expr, expected, false);
- let mut pointing_at_return_type = false;
- if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
- let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap();
- pointing_at_return_type = self.suggest_missing_return_type(
- err,
- &fn_decl,
- expected,
- found,
- can_suggest,
- fn_id,
- );
- self.suggest_missing_break_or_return_expr(
- err, expr, &fn_decl, expected, found, blk_id, fn_id,
- );
- }
- pointing_at_return_type
- }
-
- /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
- /// the ctor would successfully solve the type mismatch and if so, suggest it:
- /// ```compile_fail,E0308
- /// fn foo(x: usize) -> usize { x }
- /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
- /// ```
- fn suggest_fn_call(
- &self,
- err: &mut Diagnostic,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) -> bool {
- let (def_id, output, inputs) = match *found.kind() {
- ty::FnDef(def_id, _) => {
- let fn_sig = found.fn_sig(self.tcx);
- (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len())
- }
- ty::Closure(def_id, substs) => {
- let fn_sig = substs.as_closure().sig();
- (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)
- }
- ty::Opaque(def_id, substs) => {
- let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
- if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
- && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- Some((
- pred.kind().rebind(proj.term.ty().unwrap()),
- args.len(),
- ))
- } else {
- None
- }
- });
- if let Some((output, inputs)) = sig {
- (def_id, output, inputs)
- } else {
- return false;
- }
- }
- _ => return false,
- };
-
- let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
- let output = self.normalize_associated_types_in(expr.span, output);
- if !output.is_ty_var() && self.can_coerce(output, expected) {
- let (sugg_call, mut applicability) = match inputs {
- 0 => ("".to_string(), Applicability::MachineApplicable),
- 1..=4 => (
- (0..inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
- Applicability::MachineApplicable,
- ),
- _ => ("...".to_string(), Applicability::HasPlaceholders),
- };
-
- let msg = match self.tcx.def_kind(def_id) {
- DefKind::Fn => "call this function",
- DefKind::Closure | DefKind::OpaqueTy => "call this closure",
- DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct",
- DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant",
- _ => "call this function",
- };
-
- let sugg = match expr.kind {
- hir::ExprKind::Call(..)
- | hir::ExprKind::Path(..)
- | hir::ExprKind::Index(..)
- | hir::ExprKind::Lit(..) => {
- vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))]
- }
- hir::ExprKind::Closure { .. } => {
- // Might be `{ expr } || { bool }`
- applicability = Applicability::MaybeIncorrect;
- vec![
- (expr.span.shrink_to_lo(), "(".to_string()),
- (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
- ]
- }
- _ => {
- vec![
- (expr.span.shrink_to_lo(), "(".to_string()),
- (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
- ]
- }
- };
-
- err.multipart_suggestion_verbose(
- format!("use parentheses to {msg}"),
- sugg,
- applicability,
- );
-
- return true;
- }
- false
- }
-
- pub fn suggest_deref_ref_or_into(
- &self,
- err: &mut Diagnostic,
- expr: &hir::Expr<'tcx>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
- ) {
- let expr = expr.peel_blocks();
- if let Some((sp, msg, suggestion, applicability, verbose)) =
- self.check_ref(expr, found, expected)
- {
- if verbose {
- err.span_suggestion_verbose(sp, &msg, suggestion, applicability);
- } else {
- err.span_suggestion(sp, &msg, suggestion, applicability);
- }
- } else if let (ty::FnDef(def_id, ..), true) =
- (&found.kind(), self.suggest_fn_call(err, expr, expected, found))
- {
- if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
- err.span_label(sp, format!("{found} defined here"));
- }
- } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
- let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
- if !methods.is_empty() {
- let mut suggestions = methods.iter()
- .filter_map(|conversion_method| {
- let receiver_method_ident = expr.method_ident();
- if let Some(method_ident) = receiver_method_ident
- && method_ident.name == conversion_method.name
- {
- return None // do not suggest code that is already there (#53348)
- }
-
- let method_call_list = [sym::to_vec, sym::to_string];
- let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
- && receiver_method.ident.name == sym::clone
- && method_call_list.contains(&conversion_method.name)
- // If receiver is `.clone()` and found type has one of those methods,
- // we guess that the user wants to convert from a slice type (`&[]` or `&str`)
- // to an owned type (`Vec` or `String`). These conversions clone internally,
- // so we remove the user's `clone` call.
- {
- vec![(
- receiver_method.ident.span,
- conversion_method.name.to_string()
- )]
- } else if expr.precedence().order()
- < ExprPrecedence::MethodCall.order()
- {
- vec![
- (expr.span.shrink_to_lo(), "(".to_string()),
- (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
- ]
- } else {
- vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
- };
- let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr);
- if let Some(name) = struct_pat_shorthand_field {
- sugg.insert(
- 0,
- (expr.span.shrink_to_lo(), format!("{}: ", name)),
- );
- }
- Some(sugg)
- })
- .peekable();
- if suggestions.peek().is_some() {
- err.multipart_suggestions(
- "try using a conversion method",
- suggestions,
- Applicability::MaybeIncorrect,
- );
- }
- } else if let ty::Adt(found_adt, found_substs) = found.kind()
- && self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
- && let ty::Adt(expected_adt, expected_substs) = expected.kind()
- && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
- && let ty::Ref(_, inner_ty, _) = expected_substs.type_at(0).kind()
- && inner_ty.is_str()
- {
- let ty = found_substs.type_at(0);
- let mut peeled = ty;
- let mut ref_cnt = 0;
- while let ty::Ref(_, inner, _) = peeled.kind() {
- peeled = *inner;
- ref_cnt += 1;
- }
- if let ty::Adt(adt, _) = peeled.kind()
- && self.tcx.is_diagnostic_item(sym::String, adt.did())
- {
- err.span_suggestion_verbose(
- expr.span.shrink_to_hi(),
- "try converting the passed type into a `&str`",
- format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)),
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
- }
-
- /// When encountering the expected boxed value allocated in the stack, suggest allocating it
- /// in the heap by calling `Box::new()`.
- pub(in super::super) fn suggest_boxing_when_appropriate(
- &self,
- err: &mut Diagnostic,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) {
- if self.tcx.hir().is_inside_const_context(expr.hir_id) {
- // Do not suggest `Box::new` in const context.
- return;
- }
- if !expected.is_box() || found.is_box() {
- return;
- }
- let boxed_found = self.tcx.mk_box(found);
- if self.can_coerce(boxed_found, expected) {
- err.multipart_suggestion(
- "store this in the heap by calling `Box::new`",
- vec![
- (expr.span.shrink_to_lo(), "Box::new(".to_string()),
- (expr.span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MachineApplicable,
- );
- err.note(
- "for more on the distinction between the stack and the heap, read \
- https://doc.rust-lang.org/book/ch15-01-box.html, \
- https://doc.rust-lang.org/rust-by-example/std/box.html, and \
- https://doc.rust-lang.org/std/boxed/index.html",
- );
- }
- }
-
- /// When encountering a closure that captures variables, where a FnPtr is expected,
- /// suggest a non-capturing closure
- pub(in super::super) fn suggest_no_capture_closure(
- &self,
- err: &mut Diagnostic,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) {
- if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
- if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
- // Report upto four upvars being captured to reduce the amount error messages
- // reported back to the user.
- let spans_and_labels = upvars
- .iter()
- .take(4)
- .map(|(var_hir_id, upvar)| {
- let var_name = self.tcx.hir().name(*var_hir_id).to_string();
- let msg = format!("`{}` captured here", var_name);
- (upvar.span, msg)
- })
- .collect::<Vec<_>>();
-
- let mut multi_span: MultiSpan =
- spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
- for (sp, label) in spans_and_labels {
- multi_span.push_span_label(sp, label);
- }
- err.span_note(
- multi_span,
- "closures can only be coerced to `fn` types if they do not capture any variables"
- );
- }
- }
- }
-
- /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
- #[instrument(skip(self, err))]
- pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
- &self,
- err: &mut Diagnostic,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) -> bool {
- // Handle #68197.
-
- if self.tcx.hir().is_inside_const_context(expr.hir_id) {
- // Do not suggest `Box::new` in const context.
- return false;
- }
- let pin_did = self.tcx.lang_items().pin_type();
- // This guards the `unwrap` and `mk_box` below.
- if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() {
- return false;
- }
- let box_found = self.tcx.mk_box(found);
- let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap();
- let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap();
- match expected.kind() {
- ty::Adt(def, _) if Some(def.did()) == pin_did => {
- if self.can_coerce(pin_box_found, expected) {
- debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
- match found.kind() {
- ty::Adt(def, _) if def.is_box() => {
- err.help("use `Box::pin`");
- }
- _ => {
- err.multipart_suggestion(
- "you need to pin and box this expression",
- vec![
- (expr.span.shrink_to_lo(), "Box::pin(".to_string()),
- (expr.span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- }
- }
- true
- } else if self.can_coerce(pin_found, expected) {
- match found.kind() {
- ty::Adt(def, _) if def.is_box() => {
- err.help("use `Box::pin`");
- true
- }
- _ => false,
- }
- } else {
- false
- }
- }
- ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => {
- // Check if the parent expression is a call to Pin::new. If it
- // is and we were expecting a Box, ergo Pin<Box<expected>>, we
- // can suggest Box::pin.
- let parent = self.tcx.hir().get_parent_node(expr.hir_id);
- let Some(Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. })) = self.tcx.hir().find(parent) else {
- return false;
- };
- match fn_name.kind {
- ExprKind::Path(QPath::TypeRelative(
- hir::Ty {
- kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })),
- ..
- },
- method,
- )) if recv_ty.opt_def_id() == pin_did && method.ident.name == sym::new => {
- err.span_suggestion(
- fn_name.span,
- "use `Box::pin` to pin and box this expression",
- "Box::pin",
- Applicability::MachineApplicable,
- );
- true
- }
- _ => false,
- }
- }
- _ => false,
- }
- }
-
- /// A common error is to forget to add a semicolon at the end of a block, e.g.,
- ///
- /// ```compile_fail,E0308
- /// # fn bar_that_returns_u32() -> u32 { 4 }
- /// fn foo() {
- /// bar_that_returns_u32()
- /// }
- /// ```
- ///
- /// This routine checks if the return expression in a block would make sense on its own as a
- /// statement and the return type has been left as default or has been specified as `()`. If so,
- /// it suggests adding a semicolon.
- ///
- /// If the expression is the expression of a closure without block (`|| expr`), a
- /// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`.
- pub fn suggest_missing_semicolon(
- &self,
- err: &mut Diagnostic,
- expression: &'tcx hir::Expr<'tcx>,
- expected: Ty<'tcx>,
- needs_block: bool,
- ) {
- if expected.is_unit() {
- // `BlockTailExpression` only relevant if the tail expr would be
- // useful on its own.
- match expression.kind {
- ExprKind::Call(..)
- | ExprKind::MethodCall(..)
- | ExprKind::Loop(..)
- | ExprKind::If(..)
- | ExprKind::Match(..)
- | ExprKind::Block(..)
- if expression.can_have_side_effects()
- // If the expression is from an external macro, then do not suggest
- // adding a semicolon, because there's nowhere to put it.
- // See issue #81943.
- && !in_external_macro(self.tcx.sess, expression.span) =>
- {
- if needs_block {
- err.multipart_suggestion(
- "consider using a semicolon here",
- vec![
- (expression.span.shrink_to_lo(), "{ ".to_owned()),
- (expression.span.shrink_to_hi(), "; }".to_owned()),
- ],
- Applicability::MachineApplicable,
- );
- } else {
- err.span_suggestion(
- expression.span.shrink_to_hi(),
- "consider using a semicolon here",
- ";",
- Applicability::MachineApplicable,
- );
- }
- }
- _ => (),
- }
- }
- }
-
- /// A possible error is to forget to add a return type that is needed:
- ///
- /// ```compile_fail,E0308
- /// # fn bar_that_returns_u32() -> u32 { 4 }
- /// fn foo() {
- /// bar_that_returns_u32()
- /// }
- /// ```
- ///
- /// This routine checks if the return type is left as default, the method is not part of an
- /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
- /// type.
- pub(in super::super) fn suggest_missing_return_type(
- &self,
- err: &mut Diagnostic,
- fn_decl: &hir::FnDecl<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- can_suggest: bool,
- fn_id: hir::HirId,
- ) -> bool {
- let found =
- self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
- // Only suggest changing the return type for methods that
- // haven't set a return type at all (and aren't `fn main()` or an impl).
- match (
- &fn_decl.output,
- found.is_suggestable(self.tcx, false),
- can_suggest,
- expected.is_unit(),
- ) {
- (&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
- err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found });
- true
- }
- (&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
- // FIXME: if `found` could be `impl Iterator` or `impl Fn*`, we should suggest
- // that.
- err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span });
- true
- }
- (&hir::FnRetTy::DefaultReturn(span), _, false, true) => {
- // `fn main()` must return `()`, do not suggest changing return type
- err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span });
- true
- }
- // expectation was caused by something else, not the default return
- (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false,
- (&hir::FnRetTy::Return(ref ty), _, _, _) => {
- // Only point to return type if the expected type is the return type, as if they
- // are not, the expectation must have been caused by something else.
- debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
- let span = ty.span;
- let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
- debug!("suggest_missing_return_type: return type {:?}", ty);
- debug!("suggest_missing_return_type: expected type {:?}", ty);
- let bound_vars = self.tcx.late_bound_vars(fn_id);
- let ty = Binder::bind_with_vars(ty, bound_vars);
- let ty = self.normalize_associated_types_in(span, ty);
- let ty = self.tcx.erase_late_bound_regions(ty);
- if self.can_coerce(expected, ty) {
- err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
- self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
- return true;
- }
- false
- }
- }
- }
-
- /// check whether the return type is a generic type with a trait bound
- /// only suggest this if the generic param is not present in the arguments
- /// if this is true, hint them towards changing the return type to `impl Trait`
- /// ```compile_fail,E0308
- /// fn cant_name_it<T: Fn() -> u32>() -> T {
- /// || 3
- /// }
- /// ```
- fn try_suggest_return_impl_trait(
- &self,
- err: &mut Diagnostic,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- fn_id: hir::HirId,
- ) {
- // Only apply the suggestion if:
- // - the return type is a generic parameter
- // - the generic param is not used as a fn param
- // - the generic param has at least one bound
- // - the generic param doesn't appear in any other bounds where it's not the Self type
- // Suggest:
- // - Changing the return type to be `impl <all bounds>`
-
- debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found);
-
- let ty::Param(expected_ty_as_param) = expected.kind() else { return };
-
- let fn_node = self.tcx.hir().find(fn_id);
-
- let Some(hir::Node::Item(hir::Item {
- kind:
- hir::ItemKind::Fn(
- hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. },
- hir::Generics { params, predicates, .. },
- _body_id,
- ),
- ..
- })) = fn_node else { return };
-
- if params.get(expected_ty_as_param.index as usize).is_none() {
- return;
- };
-
- // get all where BoundPredicates here, because they are used in to cases below
- let where_predicates = predicates
- .iter()
- .filter_map(|p| match p {
- WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
- bounds,
- bounded_ty,
- ..
- }) => {
- // FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below)
- let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, bounded_ty);
- Some((ty, bounds))
- }
- _ => None,
- })
- .map(|(ty, bounds)| match ty.kind() {
- ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)),
- // check whether there is any predicate that contains our `T`, like `Option<T>: Send`
- _ => match ty.contains(expected) {
- true => Err(()),
- false => Ok(None),
- },
- })
- .collect::<Result<Vec<_>, _>>();
-
- let Ok(where_predicates) = where_predicates else { return };
-
- // now get all predicates in the same types as the where bounds, so we can chain them
- let predicates_from_where =
- where_predicates.iter().flatten().flat_map(|bounds| bounds.iter());
-
- // extract all bounds from the source code using their spans
- let all_matching_bounds_strs = predicates_from_where
- .filter_map(|bound| match bound {
- GenericBound::Trait(_, _) => {
- self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
- }
- _ => None,
- })
- .collect::<Vec<String>>();
-
- if all_matching_bounds_strs.len() == 0 {
- return;
- }
-
- let all_bounds_str = all_matching_bounds_strs.join(" + ");
-
- let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| {
- let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, param);
- matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param)
- });
-
- if ty_param_used_in_fn_params {
- return;
- }
-
- err.span_suggestion(
- fn_return.span(),
- "consider using an impl return type",
- format!("impl {}", all_bounds_str),
- Applicability::MaybeIncorrect,
- );
- }
-
- pub(in super::super) fn suggest_missing_break_or_return_expr(
- &self,
- err: &mut Diagnostic,
- expr: &'tcx hir::Expr<'tcx>,
- fn_decl: &hir::FnDecl<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- id: hir::HirId,
- fn_id: hir::HirId,
- ) {
- if !expected.is_unit() {
- return;
- }
- let found = self.resolve_vars_with_obligations(found);
-
- let in_loop = self.is_loop(id)
- || self.tcx.hir().parent_iter(id).any(|(parent_id, _)| self.is_loop(parent_id));
-
- let in_local_statement = self.is_local_statement(id)
- || self
- .tcx
- .hir()
- .parent_iter(id)
- .any(|(parent_id, _)| self.is_local_statement(parent_id));
-
- if in_loop && in_local_statement {
- err.multipart_suggestion(
- "you might have meant to break the loop with this value",
- vec![
- (expr.span.shrink_to_lo(), "break ".to_string()),
- (expr.span.shrink_to_hi(), ";".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- return;
- }
-
- if let hir::FnRetTy::Return(ty) = fn_decl.output {
- let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
- let bound_vars = self.tcx.late_bound_vars(fn_id);
- let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars));
- let ty = self.normalize_associated_types_in(expr.span, ty);
- let ty = match self.tcx.asyncness(fn_id.owner) {
- hir::IsAsync::Async => self
- .tcx
- .infer_ctxt()
- .enter(|infcx| {
- infcx.get_impl_future_output_ty(ty).unwrap_or_else(|| {
- span_bug!(
- fn_decl.output.span(),
- "failed to get output type of async function"
- )
- })
- })
- .skip_binder(),
- hir::IsAsync::NotAsync => ty,
- };
- if self.can_coerce(found, ty) {
- err.multipart_suggestion(
- "you might have meant to return this value",
- vec![
- (expr.span.shrink_to_lo(), "return ".to_string()),
- (expr.span.shrink_to_hi(), ";".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
-
- pub(in super::super) fn suggest_missing_parentheses(
- &self,
- err: &mut Diagnostic,
- expr: &hir::Expr<'_>,
- ) {
- let sp = self.tcx.sess.source_map().start_point(expr.span);
- if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
- // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
- self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp);
- }
- }
-
- /// Given an expression type mismatch, peel any `&` expressions until we get to
- /// a block expression, and then suggest replacing the braces with square braces
- /// if it was possibly mistaken array syntax.
- pub(crate) fn suggest_block_to_brackets_peeling_refs(
- &self,
- diag: &mut Diagnostic,
- mut expr: &hir::Expr<'_>,
- mut expr_ty: Ty<'tcx>,
- mut expected_ty: Ty<'tcx>,
- ) {
- loop {
- match (&expr.kind, expr_ty.kind(), expected_ty.kind()) {
- (
- hir::ExprKind::AddrOf(_, _, inner_expr),
- ty::Ref(_, inner_expr_ty, _),
- ty::Ref(_, inner_expected_ty, _),
- ) => {
- expr = *inner_expr;
- expr_ty = *inner_expr_ty;
- expected_ty = *inner_expected_ty;
- }
- (hir::ExprKind::Block(blk, _), _, _) => {
- self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty);
- break;
- }
- _ => break,
- }
- }
- }
-
- /// Suggest wrapping the block in square brackets instead of curly braces
- /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
- pub(crate) fn suggest_block_to_brackets(
- &self,
- diag: &mut Diagnostic,
- blk: &hir::Block<'_>,
- blk_ty: Ty<'tcx>,
- expected_ty: Ty<'tcx>,
- ) {
- if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() {
- if self.can_coerce(blk_ty, *elem_ty)
- && blk.stmts.is_empty()
- && blk.rules == hir::BlockCheckMode::DefaultBlock
- {
- let source_map = self.tcx.sess.source_map();
- if let Ok(snippet) = source_map.span_to_snippet(blk.span) {
- if snippet.starts_with('{') && snippet.ends_with('}') {
- diag.multipart_suggestion_verbose(
- "to create an array, use square brackets instead of curly braces",
- vec![
- (
- blk.span
- .shrink_to_lo()
- .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)),
- "[".to_string(),
- ),
- (
- blk.span
- .shrink_to_hi()
- .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)),
- "]".to_string(),
- ),
- ],
- Applicability::MachineApplicable,
- );
- }
- }
- }
- }
- }
-
- fn is_loop(&self, id: hir::HirId) -> bool {
- let node = self.tcx.hir().get(id);
- matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
- }
-
- fn is_local_statement(&self, id: hir::HirId) -> bool {
- let node = self.tcx.hir().get(id);
- matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. }))
- }
-
- /// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`,
- /// which is a side-effect of autoref.
- pub(crate) fn note_type_is_not_clone(
- &self,
- diag: &mut Diagnostic,
- expected_ty: Ty<'tcx>,
- found_ty: Ty<'tcx>,
- expr: &hir::Expr<'_>,
- ) {
- let hir::ExprKind::MethodCall(segment, &[ref callee_expr], _) = expr.kind else { return; };
- let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; };
- let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
- let results = self.typeck_results.borrow();
- // First, look for a `Clone::clone` call
- if segment.ident.name == sym::clone
- && results.type_dependent_def_id(expr.hir_id).map_or(
- false,
- |did| {
- let assoc_item = self.tcx.associated_item(did);
- assoc_item.container == ty::AssocItemContainer::TraitContainer
- && assoc_item.container_id(self.tcx) == clone_trait_did
- },
- )
- // If that clone call hasn't already dereferenced the self type (i.e. don't give this
- // diagnostic in cases where we have `(&&T).clone()` and we expect `T`).
- && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
- // Check that we're in fact trying to clone into the expected type
- && self.can_coerce(*pointee_ty, expected_ty)
- // And the expected type doesn't implement `Clone`
- && !self.predicate_must_hold_considering_regions(&traits::Obligation {
- cause: traits::ObligationCause::dummy(),
- param_env: self.param_env,
- recursion_depth: 0,
- predicate: ty::Binder::dummy(ty::TraitRef {
- def_id: clone_trait_did,
- substs: self.tcx.mk_substs([expected_ty.into()].iter()),
- })
- .without_const()
- .to_predicate(self.tcx),
- })
- {
- diag.span_note(
- callee_expr.span,
- &format!(
- "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
- ),
- );
- }
- }
-
- /// A common error is to add an extra semicolon:
- ///
- /// ```compile_fail,E0308
- /// fn foo() -> usize {
- /// 22;
- /// }
- /// ```
- ///
- /// This routine checks if the final statement in a block is an
- /// expression with an explicit semicolon whose type is compatible
- /// with `expected_ty`. If so, it suggests removing the semicolon.
- pub(crate) fn consider_removing_semicolon(
- &self,
- blk: &'tcx hir::Block<'tcx>,
- expected_ty: Ty<'tcx>,
- err: &mut Diagnostic,
- ) -> bool {
- if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
- if let StatementAsExpression::NeedsBoxing = boxed {
- err.span_suggestion_verbose(
- span_semi,
- "consider removing this semicolon and boxing the expression",
- "",
- Applicability::HasPlaceholders,
- );
- } else {
- err.span_suggestion_short(
- span_semi,
- "remove this semicolon",
- "",
- Applicability::MachineApplicable,
- );
- }
- true
- } else {
- false
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs
deleted file mode 100644
index d4f800149..000000000
--- a/compiler/rustc_typeck/src/check/generator_interior.rs
+++ /dev/null
@@ -1,632 +0,0 @@
-//! This calculates the types which has storage which lives across a suspension point in a
-//! generator from the perspective of typeck. The actual types used at runtime
-//! is calculated in `rustc_mir_transform::generator` and may be a subset of the
-//! types computed here.
-
-use self::drop_ranges::DropRanges;
-use super::FnCtxt;
-use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
-use rustc_errors::pluralize;
-use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, DefKind, Res};
-use rustc_hir::def_id::DefId;
-use rustc_hir::hir_id::HirIdSet;
-use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind};
-use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData};
-use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt, TypeVisitable};
-use rustc_span::symbol::sym;
-use rustc_span::Span;
-use tracing::debug;
-
-mod drop_ranges;
-
-struct InteriorVisitor<'a, 'tcx> {
- fcx: &'a FnCtxt<'a, 'tcx>,
- region_scope_tree: &'a region::ScopeTree,
- types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>,
- rvalue_scopes: &'a RvalueScopes,
- expr_count: usize,
- kind: hir::GeneratorKind,
- prev_unresolved_span: Option<Span>,
- linted_values: HirIdSet,
- drop_ranges: DropRanges,
-}
-
-impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
- fn record(
- &mut self,
- ty: Ty<'tcx>,
- hir_id: HirId,
- scope: Option<region::Scope>,
- expr: Option<&'tcx Expr<'tcx>>,
- source_span: Span,
- ) {
- use rustc_span::DUMMY_SP;
-
- let ty = self.fcx.resolve_vars_if_possible(ty);
-
- debug!(
- "attempting to record type ty={:?}; hir_id={:?}; scope={:?}; expr={:?}; source_span={:?}; expr_count={:?}",
- ty, hir_id, scope, expr, source_span, self.expr_count,
- );
-
- let live_across_yield = scope
- .map(|s| {
- self.region_scope_tree.yield_in_scope(s).and_then(|yield_data| {
- // If we are recording an expression that is the last yield
- // in the scope, or that has a postorder CFG index larger
- // than the one of all of the yields, then its value can't
- // be storage-live (and therefore live) at any of the yields.
- //
- // See the mega-comment at `yield_in_scope` for a proof.
-
- yield_data
- .iter()
- .find(|yield_data| {
- debug!(
- "comparing counts yield: {} self: {}, source_span = {:?}",
- yield_data.expr_and_pat_count, self.expr_count, source_span
- );
-
- if self.fcx.sess().opts.unstable_opts.drop_tracking
- && self
- .drop_ranges
- .is_dropped_at(hir_id, yield_data.expr_and_pat_count)
- {
- debug!("value is dropped at yield point; not recording");
- return false;
- }
-
- // If it is a borrowing happening in the guard,
- // it needs to be recorded regardless because they
- // do live across this yield point.
- yield_data.expr_and_pat_count >= self.expr_count
- })
- .cloned()
- })
- })
- .unwrap_or_else(|| {
- Some(YieldData { span: DUMMY_SP, expr_and_pat_count: 0, source: self.kind.into() })
- });
-
- if let Some(yield_data) = live_across_yield {
- debug!(
- "type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}",
- expr, scope, ty, self.expr_count, yield_data.span
- );
-
- if let Some((unresolved_type, unresolved_type_span)) =
- self.fcx.unresolved_type_vars(&ty)
- {
- // If unresolved type isn't a ty_var then unresolved_type_span is None
- let span = self
- .prev_unresolved_span
- .unwrap_or_else(|| unresolved_type_span.unwrap_or(source_span));
-
- // If we encounter an int/float variable, then inference fallback didn't
- // finish due to some other error. Don't emit spurious additional errors.
- if let ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) =
- unresolved_type.kind()
- {
- self.fcx
- .tcx
- .sess
- .delay_span_bug(span, &format!("Encountered var {:?}", unresolved_type));
- } else {
- let note = format!(
- "the type is part of the {} because of this {}",
- self.kind, yield_data.source
- );
-
- self.fcx
- .need_type_info_err_in_generator(self.kind, span, unresolved_type)
- .span_note(yield_data.span, &*note)
- .emit();
- }
- } else {
- // Insert the type into the ordered set.
- let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree));
-
- if !self.linted_values.contains(&hir_id) {
- check_must_not_suspend_ty(
- self.fcx,
- ty,
- hir_id,
- SuspendCheckData {
- expr,
- source_span,
- yield_span: yield_data.span,
- plural_len: 1,
- ..Default::default()
- },
- );
- self.linted_values.insert(hir_id);
- }
-
- self.types.insert(ty::GeneratorInteriorTypeCause {
- span: source_span,
- ty,
- scope_span,
- yield_span: yield_data.span,
- expr: expr.map(|e| e.hir_id),
- });
- }
- } else {
- debug!(
- "no type in expr = {:?}, count = {:?}, span = {:?}",
- expr,
- self.expr_count,
- expr.map(|e| e.span)
- );
- if let Some((unresolved_type, unresolved_type_span)) =
- self.fcx.unresolved_type_vars(&ty)
- {
- debug!(
- "remained unresolved_type = {:?}, unresolved_type_span: {:?}",
- unresolved_type, unresolved_type_span
- );
- self.prev_unresolved_span = unresolved_type_span;
- }
- }
- }
-}
-
-pub fn resolve_interior<'a, 'tcx>(
- fcx: &'a FnCtxt<'a, 'tcx>,
- def_id: DefId,
- body_id: hir::BodyId,
- interior: Ty<'tcx>,
- kind: hir::GeneratorKind,
-) {
- let body = fcx.tcx.hir().body(body_id);
- let typeck_results = fcx.inh.typeck_results.borrow();
- let mut visitor = InteriorVisitor {
- fcx,
- types: FxIndexSet::default(),
- region_scope_tree: fcx.tcx.region_scope_tree(def_id),
- rvalue_scopes: &typeck_results.rvalue_scopes,
- expr_count: 0,
- kind,
- prev_unresolved_span: None,
- linted_values: <_>::default(),
- drop_ranges: drop_ranges::compute_drop_ranges(fcx, def_id, body),
- };
- intravisit::walk_body(&mut visitor, body);
-
- // Check that we visited the same amount of expressions as the RegionResolutionVisitor
- let region_expr_count = fcx.tcx.region_scope_tree(def_id).body_expr_count(body_id).unwrap();
- assert_eq!(region_expr_count, visitor.expr_count);
-
- // The types are already kept in insertion order.
- let types = visitor.types;
-
- // The types in the generator interior contain lifetimes local to the generator itself,
- // which should not be exposed outside of the generator. Therefore, we replace these
- // lifetimes with existentially-bound lifetimes, which reflect the exact value of the
- // lifetimes not being known by users.
- //
- // These lifetimes are used in auto trait impl checking (for example,
- // if a Sync generator contains an &'α T, we need to check whether &'α T: Sync),
- // so knowledge of the exact relationships between them isn't particularly important.
-
- debug!("types in generator {:?}, span = {:?}", types, body.value.span);
-
- let mut counter = 0;
- let mut captured_tys = FxHashSet::default();
- let type_causes: Vec<_> = types
- .into_iter()
- .filter_map(|mut cause| {
- // Erase regions and canonicalize late-bound regions to deduplicate as many types as we
- // can.
- let erased = fcx.tcx.erase_regions(cause.ty);
- if captured_tys.insert(erased) {
- // Replace all regions inside the generator interior with late bound regions.
- // Note that each region slot in the types gets a new fresh late bound region,
- // which means that none of the regions inside relate to any other, even if
- // typeck had previously found constraints that would cause them to be related.
- let folded = fcx.tcx.fold_regions(erased, |_, current_depth| {
- let br = ty::BoundRegion {
- var: ty::BoundVar::from_u32(counter),
- kind: ty::BrAnon(counter),
- };
- let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, br));
- counter += 1;
- r
- });
-
- cause.ty = folded;
- Some(cause)
- } else {
- None
- }
- })
- .collect();
-
- // Extract type components to build the witness type.
- let type_list = fcx.tcx.mk_type_list(type_causes.iter().map(|cause| cause.ty));
- let bound_vars = fcx.tcx.mk_bound_variable_kinds(
- (0..counter).map(|i| ty::BoundVariableKind::Region(ty::BrAnon(i))),
- );
- let witness =
- fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone()));
-
- drop(typeck_results);
- // Store the generator types and spans into the typeck results for this generator.
- fcx.inh.typeck_results.borrow_mut().generator_interior_types =
- ty::Binder::bind_with_vars(type_causes, bound_vars);
-
- debug!(
- "types in generator after region replacement {:?}, span = {:?}",
- witness, body.value.span
- );
-
- // Unify the type variable inside the generator with the new witness
- match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(interior, witness) {
- Ok(ok) => fcx.register_infer_ok_obligations(ok),
- _ => bug!(),
- }
-}
-
-// This visitor has to have the same visit_expr calls as RegionResolutionVisitor in
-// librustc_middle/middle/region.rs since `expr_count` is compared against the results
-// there.
-impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
- fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) {
- let Arm { guard, pat, body, .. } = arm;
- self.visit_pat(pat);
- if let Some(ref g) = guard {
- {
- // If there is a guard, we need to count all variables bound in the pattern as
- // borrowed for the entire guard body, regardless of whether they are accessed.
- // We do this by walking the pattern bindings and recording `&T` for any `x: T`
- // that is bound.
-
- struct ArmPatCollector<'a, 'b, 'tcx> {
- interior_visitor: &'a mut InteriorVisitor<'b, 'tcx>,
- scope: Scope,
- }
-
- impl<'a, 'b, 'tcx> Visitor<'tcx> for ArmPatCollector<'a, 'b, 'tcx> {
- fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
- intravisit::walk_pat(self, pat);
- if let PatKind::Binding(_, id, ident, ..) = pat.kind {
- let ty =
- self.interior_visitor.fcx.typeck_results.borrow().node_type(id);
- let tcx = self.interior_visitor.fcx.tcx;
- let ty = tcx.mk_ref(
- // Use `ReErased` as `resolve_interior` is going to replace all the
- // regions anyway.
- tcx.mk_region(ty::ReErased),
- ty::TypeAndMut { ty, mutbl: hir::Mutability::Not },
- );
- self.interior_visitor.record(
- ty,
- id,
- Some(self.scope),
- None,
- ident.span,
- );
- }
- }
- }
-
- ArmPatCollector {
- interior_visitor: self,
- scope: Scope { id: g.body().hir_id.local_id, data: ScopeData::Node },
- }
- .visit_pat(pat);
- }
-
- match g {
- Guard::If(ref e) => {
- self.visit_expr(e);
- }
- Guard::IfLet(ref l) => {
- self.visit_let_expr(l);
- }
- }
- }
- self.visit_expr(body);
- }
-
- fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
- intravisit::walk_pat(self, pat);
-
- self.expr_count += 1;
-
- if let PatKind::Binding(..) = pat.kind {
- let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id).unwrap();
- let ty = self.fcx.typeck_results.borrow().pat_ty(pat);
- self.record(ty, pat.hir_id, Some(scope), None, pat.span);
- }
- }
-
- fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
- match &expr.kind {
- ExprKind::Call(callee, args) => match &callee.kind {
- ExprKind::Path(qpath) => {
- let res = self.fcx.typeck_results.borrow().qpath_res(qpath, callee.hir_id);
- match res {
- // Direct calls never need to keep the callee `ty::FnDef`
- // ZST in a temporary, so skip its type, just in case it
- // can significantly complicate the generator type.
- Res::Def(
- DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn),
- _,
- ) => {
- // NOTE(eddyb) this assumes a path expression has
- // no nested expressions to keep track of.
- self.expr_count += 1;
-
- // Record the rest of the call expression normally.
- for arg in *args {
- self.visit_expr(arg);
- }
- }
- _ => intravisit::walk_expr(self, expr),
- }
- }
- _ => intravisit::walk_expr(self, expr),
- },
- _ => intravisit::walk_expr(self, expr),
- }
-
- self.expr_count += 1;
-
- debug!("is_borrowed_temporary: {:?}", self.drop_ranges.is_borrowed_temporary(expr));
-
- let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr);
- let may_need_drop = |ty: Ty<'tcx>| {
- // Avoid ICEs in needs_drop.
- let ty = self.fcx.resolve_vars_if_possible(ty);
- let ty = self.fcx.tcx.erase_regions(ty);
- if ty.needs_infer() {
- return true;
- }
- ty.needs_drop(self.fcx.tcx, self.fcx.param_env)
- };
-
- // Typically, the value produced by an expression is consumed by its parent in some way,
- // so we only have to check if the parent contains a yield (note that the parent may, for
- // example, store the value into a local variable, but then we already consider local
- // variables to be live across their scope).
- //
- // However, in the case of temporary values, we are going to store the value into a
- // temporary on the stack that is live for the current temporary scope and then return a
- // reference to it. That value may be live across the entire temporary scope.
- //
- // There's another subtlety: if the type has an observable drop, it must be dropped after
- // the yield, even if it's not borrowed or referenced after the yield. Ideally this would
- // *only* happen for types with observable drop, not all types which wrap them, but that
- // doesn't match the behavior of MIR borrowck and causes ICEs. See the FIXME comment in
- // src/test/ui/generator/drop-tracking-parent-expression.rs.
- let scope = if self.drop_ranges.is_borrowed_temporary(expr)
- || ty.map_or(true, |ty| {
- let needs_drop = may_need_drop(ty);
- debug!(?needs_drop, ?ty);
- needs_drop
- }) {
- self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id)
- } else {
- debug!("parent_node: {:?}", self.fcx.tcx.hir().find_parent_node(expr.hir_id));
- match self.fcx.tcx.hir().find_parent_node(expr.hir_id) {
- Some(parent) => Some(Scope { id: parent.local_id, data: ScopeData::Node }),
- None => {
- self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id)
- }
- }
- };
-
- // If there are adjustments, then record the final type --
- // this is the actual value that is being produced.
- if let Some(adjusted_ty) = ty {
- self.record(adjusted_ty, expr.hir_id, scope, Some(expr), expr.span);
- }
-
- // Also record the unadjusted type (which is the only type if
- // there are no adjustments). The reason for this is that the
- // unadjusted value is sometimes a "temporary" that would wind
- // up in a MIR temporary.
- //
- // As an example, consider an expression like `vec![].push(x)`.
- // Here, the `vec![]` would wind up MIR stored into a
- // temporary variable `t` which we can borrow to invoke
- // `<Vec<_>>::push(&mut t, x)`.
- //
- // Note that an expression can have many adjustments, and we
- // are just ignoring those intermediate types. This is because
- // those intermediate values are always linearly "consumed" by
- // the other adjustments, and hence would never be directly
- // captured in the MIR.
- //
- // (Note that this partly relies on the fact that the `Deref`
- // traits always return references, which means their content
- // can be reborrowed without needing to spill to a temporary.
- // If this were not the case, then we could conceivably have
- // to create intermediate temporaries.)
- //
- // The type table might not have information for this expression
- // if it is in a malformed scope. (#66387)
- if let Some(ty) = self.fcx.typeck_results.borrow().expr_ty_opt(expr) {
- self.record(ty, expr.hir_id, scope, Some(expr), expr.span);
- } else {
- self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node");
- }
- }
-}
-
-#[derive(Default)]
-pub struct SuspendCheckData<'a, 'tcx> {
- expr: Option<&'tcx Expr<'tcx>>,
- source_span: Span,
- yield_span: Span,
- descr_pre: &'a str,
- descr_post: &'a str,
- plural_len: usize,
-}
-
-// Returns whether it emitted a diagnostic or not
-// Note that this fn and the proceeding one are based on the code
-// for creating must_use diagnostics
-//
-// Note that this technique was chosen over things like a `Suspend` marker trait
-// as it is simpler and has precedent in the compiler
-pub fn check_must_not_suspend_ty<'tcx>(
- fcx: &FnCtxt<'_, 'tcx>,
- ty: Ty<'tcx>,
- hir_id: HirId,
- data: SuspendCheckData<'_, 'tcx>,
-) -> bool {
- if ty.is_unit()
- // FIXME: should this check `is_ty_uninhabited_from`. This query is not available in this stage
- // of typeck (before ReVar and RePlaceholder are removed), but may remove noise, like in
- // `must_use`
- // || fcx.tcx.is_ty_uninhabited_from(fcx.tcx.parent_module(hir_id).to_def_id(), ty, fcx.param_env)
- {
- return false;
- }
-
- let plural_suffix = pluralize!(data.plural_len);
-
- match *ty.kind() {
- ty::Adt(..) if ty.is_box() => {
- let boxed_ty = ty.boxed_ty();
- let descr_pre = &format!("{}boxed ", data.descr_pre);
- check_must_not_suspend_ty(fcx, boxed_ty, hir_id, SuspendCheckData { descr_pre, ..data })
- }
- ty::Adt(def, _) => check_must_not_suspend_def(fcx.tcx, def.did(), hir_id, data),
- // FIXME: support adding the attribute to TAITs
- ty::Opaque(def, _) => {
- let mut has_emitted = false;
- for &(predicate, _) in fcx.tcx.explicit_item_bounds(def) {
- // We only look at the `DefId`, so it is safe to skip the binder here.
- if let ty::PredicateKind::Trait(ref poly_trait_predicate) =
- predicate.kind().skip_binder()
- {
- let def_id = poly_trait_predicate.trait_ref.def_id;
- let descr_pre = &format!("{}implementer{} of ", data.descr_pre, plural_suffix);
- if check_must_not_suspend_def(
- fcx.tcx,
- def_id,
- hir_id,
- SuspendCheckData { descr_pre, ..data },
- ) {
- has_emitted = true;
- break;
- }
- }
- }
- has_emitted
- }
- ty::Dynamic(binder, _) => {
- let mut has_emitted = false;
- for predicate in binder.iter() {
- if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
- let def_id = trait_ref.def_id;
- let descr_post = &format!(" trait object{}{}", plural_suffix, data.descr_post);
- if check_must_not_suspend_def(
- fcx.tcx,
- def_id,
- hir_id,
- SuspendCheckData { descr_post, ..data },
- ) {
- has_emitted = true;
- break;
- }
- }
- }
- has_emitted
- }
- ty::Tuple(fields) => {
- let mut has_emitted = false;
- let comps = match data.expr.map(|e| &e.kind) {
- Some(hir::ExprKind::Tup(comps)) => {
- debug_assert_eq!(comps.len(), fields.len());
- Some(comps)
- }
- _ => None,
- };
- for (i, ty) in fields.iter().enumerate() {
- let descr_post = &format!(" in tuple element {i}");
- let span = comps.and_then(|c| c.get(i)).map(|e| e.span).unwrap_or(data.source_span);
- if check_must_not_suspend_ty(
- fcx,
- ty,
- hir_id,
- SuspendCheckData {
- descr_post,
- expr: comps.and_then(|comps| comps.get(i)),
- source_span: span,
- ..data
- },
- ) {
- has_emitted = true;
- }
- }
- has_emitted
- }
- ty::Array(ty, len) => {
- let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix);
- check_must_not_suspend_ty(
- fcx,
- ty,
- hir_id,
- SuspendCheckData {
- descr_pre,
- plural_len: len.try_eval_usize(fcx.tcx, fcx.param_env).unwrap_or(0) as usize
- + 1,
- ..data
- },
- )
- }
- _ => false,
- }
-}
-
-fn check_must_not_suspend_def(
- tcx: TyCtxt<'_>,
- def_id: DefId,
- hir_id: HirId,
- data: SuspendCheckData<'_, '_>,
-) -> bool {
- if let Some(attr) = tcx.get_attr(def_id, sym::must_not_suspend) {
- tcx.struct_span_lint_hir(
- rustc_session::lint::builtin::MUST_NOT_SUSPEND,
- hir_id,
- data.source_span,
- |lint| {
- let msg = format!(
- "{}`{}`{} held across a suspend point, but should not be",
- data.descr_pre,
- tcx.def_path_str(def_id),
- data.descr_post,
- );
- let mut err = lint.build(&msg);
-
- // add span pointing to the offending yield/await
- err.span_label(data.yield_span, "the value is held across this suspend point");
-
- // Add optional reason note
- if let Some(note) = attr.value_str() {
- // FIXME(guswynn): consider formatting this better
- err.span_note(data.source_span, note.as_str());
- }
-
- // Add some quick suggestions on what to do
- // FIXME: can `drop` work as a suggestion here as well?
- err.span_help(
- data.source_span,
- "consider using a block (`{ ... }`) \
- to shrink the value's scope, ending before the suspend point",
- );
-
- err.emit();
- },
- );
-
- true
- } else {
- false
- }
-}
diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs
deleted file mode 100644
index 518cd7342..000000000
--- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs
+++ /dev/null
@@ -1,309 +0,0 @@
-//! Drop range analysis finds the portions of the tree where a value is guaranteed to be dropped
-//! (i.e. moved, uninitialized, etc.). This is used to exclude the types of those values from the
-//! generator type. See `InteriorVisitor::record` for where the results of this analysis are used.
-//!
-//! There are three phases to this analysis:
-//! 1. Use `ExprUseVisitor` to identify the interesting values that are consumed and borrowed.
-//! 2. Use `DropRangeVisitor` to find where the interesting values are dropped or reinitialized,
-//! and also build a control flow graph.
-//! 3. Use `DropRanges::propagate_to_fixpoint` to flow the dropped/reinitialized information through
-//! the CFG and find the exact points where we know a value is definitely dropped.
-//!
-//! The end result is a data structure that maps the post-order index of each node in the HIR tree
-//! to a set of values that are known to be dropped at that location.
-
-use self::cfg_build::build_control_flow_graph;
-use self::record_consumed_borrow::find_consumed_and_borrowed;
-use crate::check::FnCtxt;
-use hir::def_id::DefId;
-use hir::{Body, HirId, HirIdMap, Node};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir as hir;
-use rustc_index::bit_set::BitSet;
-use rustc_index::vec::IndexVec;
-use rustc_middle::hir::map::Map;
-use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId};
-use rustc_middle::ty;
-use std::collections::BTreeMap;
-use std::fmt::Debug;
-
-mod cfg_build;
-mod cfg_propagate;
-mod cfg_visualize;
-mod record_consumed_borrow;
-
-pub fn compute_drop_ranges<'a, 'tcx>(
- fcx: &'a FnCtxt<'a, 'tcx>,
- def_id: DefId,
- body: &'tcx Body<'tcx>,
-) -> DropRanges {
- if fcx.sess().opts.unstable_opts.drop_tracking {
- let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body);
-
- let typeck_results = &fcx.typeck_results.borrow();
- let num_exprs = fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0);
- let (mut drop_ranges, borrowed_temporaries) = build_control_flow_graph(
- fcx.tcx.hir(),
- fcx.tcx,
- typeck_results,
- consumed_borrowed_places,
- body,
- num_exprs,
- );
-
- drop_ranges.propagate_to_fixpoint();
-
- debug!("borrowed_temporaries = {borrowed_temporaries:?}");
- DropRanges {
- tracked_value_map: drop_ranges.tracked_value_map,
- nodes: drop_ranges.nodes,
- borrowed_temporaries: Some(borrowed_temporaries),
- }
- } else {
- // If drop range tracking is not enabled, skip all the analysis and produce an
- // empty set of DropRanges.
- DropRanges {
- tracked_value_map: FxHashMap::default(),
- nodes: IndexVec::new(),
- borrowed_temporaries: None,
- }
- }
-}
-
-/// Applies `f` to consumable node in the HIR subtree pointed to by `place`.
-///
-/// This includes the place itself, and if the place is a reference to a local
-/// variable then `f` is also called on the HIR node for that variable as well.
-///
-/// For example, if `place` points to `foo()`, then `f` is called once for the
-/// result of `foo`. On the other hand, if `place` points to `x` then `f` will
-/// be called both on the `ExprKind::Path` node that represents the expression
-/// as well as the HirId of the local `x` itself.
-fn for_each_consumable<'tcx>(hir: Map<'tcx>, place: TrackedValue, mut f: impl FnMut(TrackedValue)) {
- f(place);
- let node = hir.find(place.hir_id());
- if let Some(Node::Expr(expr)) = node {
- match expr.kind {
- hir::ExprKind::Path(hir::QPath::Resolved(
- _,
- hir::Path { res: hir::def::Res::Local(hir_id), .. },
- )) => {
- f(TrackedValue::Variable(*hir_id));
- }
- _ => (),
- }
- }
-}
-
-rustc_index::newtype_index! {
- pub struct PostOrderId {
- DEBUG_FORMAT = "id({})",
- }
-}
-
-rustc_index::newtype_index! {
- pub struct TrackedValueIndex {
- DEBUG_FORMAT = "hidx({})",
- }
-}
-
-/// Identifies a value whose drop state we need to track.
-#[derive(PartialEq, Eq, Hash, Clone, Copy)]
-enum TrackedValue {
- /// Represents a named variable, such as a let binding, parameter, or upvar.
- ///
- /// The HirId points to the variable's definition site.
- Variable(HirId),
- /// A value produced as a result of an expression.
- ///
- /// The HirId points to the expression that returns this value.
- Temporary(HirId),
-}
-
-impl Debug for TrackedValue {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- ty::tls::with_opt(|opt_tcx| {
- if let Some(tcx) = opt_tcx {
- write!(f, "{}", tcx.hir().node_to_string(self.hir_id()))
- } else {
- match self {
- Self::Variable(hir_id) => write!(f, "Variable({:?})", hir_id),
- Self::Temporary(hir_id) => write!(f, "Temporary({:?})", hir_id),
- }
- }
- })
- }
-}
-
-impl TrackedValue {
- fn hir_id(&self) -> HirId {
- match self {
- TrackedValue::Variable(hir_id) | TrackedValue::Temporary(hir_id) => *hir_id,
- }
- }
-
- fn from_place_with_projections_allowed(place_with_id: &PlaceWithHirId<'_>) -> Self {
- match place_with_id.place.base {
- PlaceBase::Rvalue | PlaceBase::StaticItem => {
- TrackedValue::Temporary(place_with_id.hir_id)
- }
- PlaceBase::Local(hir_id)
- | PlaceBase::Upvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id }, .. }) => {
- TrackedValue::Variable(hir_id)
- }
- }
- }
-}
-
-/// Represents a reason why we might not be able to convert a HirId or Place
-/// into a tracked value.
-#[derive(Debug)]
-enum TrackedValueConversionError {
- /// Place projects are not currently supported.
- ///
- /// The reasoning around these is kind of subtle, so we choose to be more
- /// conservative around these for now. There is no reason in theory we
- /// cannot support these, we just have not implemented it yet.
- PlaceProjectionsNotSupported,
-}
-
-impl TryFrom<&PlaceWithHirId<'_>> for TrackedValue {
- type Error = TrackedValueConversionError;
-
- fn try_from(place_with_id: &PlaceWithHirId<'_>) -> Result<Self, Self::Error> {
- if !place_with_id.place.projections.is_empty() {
- debug!(
- "TrackedValue from PlaceWithHirId: {:?} has projections, which are not supported.",
- place_with_id
- );
- return Err(TrackedValueConversionError::PlaceProjectionsNotSupported);
- }
-
- Ok(TrackedValue::from_place_with_projections_allowed(place_with_id))
- }
-}
-
-pub struct DropRanges {
- tracked_value_map: FxHashMap<TrackedValue, TrackedValueIndex>,
- nodes: IndexVec<PostOrderId, NodeInfo>,
- borrowed_temporaries: Option<FxHashSet<HirId>>,
-}
-
-impl DropRanges {
- pub fn is_dropped_at(&self, hir_id: HirId, location: usize) -> bool {
- self.tracked_value_map
- .get(&TrackedValue::Temporary(hir_id))
- .or(self.tracked_value_map.get(&TrackedValue::Variable(hir_id)))
- .cloned()
- .map_or(false, |tracked_value_id| {
- self.expect_node(location.into()).drop_state.contains(tracked_value_id)
- })
- }
-
- pub fn is_borrowed_temporary(&self, expr: &hir::Expr<'_>) -> bool {
- if let Some(b) = &self.borrowed_temporaries { b.contains(&expr.hir_id) } else { true }
- }
-
- /// Returns a reference to the NodeInfo for a node, panicking if it does not exist
- fn expect_node(&self, id: PostOrderId) -> &NodeInfo {
- &self.nodes[id]
- }
-}
-
-/// Tracks information needed to compute drop ranges.
-struct DropRangesBuilder {
- /// The core of DropRangesBuilder is a set of nodes, which each represent
- /// one expression. We primarily refer to them by their index in a
- /// post-order traversal of the HIR tree, since this is what
- /// generator_interior uses to talk about yield positions.
- ///
- /// This IndexVec keeps the relevant details for each node. See the
- /// NodeInfo struct for more details, but this information includes things
- /// such as the set of control-flow successors, which variables are dropped
- /// or reinitialized, and whether each variable has been inferred to be
- /// known-dropped or potentially reinitialized at each point.
- nodes: IndexVec<PostOrderId, NodeInfo>,
- /// We refer to values whose drop state we are tracking by the HirId of
- /// where they are defined. Within a NodeInfo, however, we store the
- /// drop-state in a bit vector indexed by a HirIdIndex
- /// (see NodeInfo::drop_state). The hir_id_map field stores the mapping
- /// from HirIds to the HirIdIndex that is used to represent that value in
- /// bitvector.
- tracked_value_map: FxHashMap<TrackedValue, TrackedValueIndex>,
-
- /// When building the control flow graph, we don't always know the
- /// post-order index of the target node at the point we encounter it.
- /// For example, this happens with break and continue. In those cases,
- /// we store a pair of the PostOrderId of the source and the HirId
- /// of the target. Once we have gathered all of these edges, we make a
- /// pass over the set of deferred edges (see process_deferred_edges in
- /// cfg_build.rs), look up the PostOrderId for the target (since now the
- /// post-order index for all nodes is known), and add missing control flow
- /// edges.
- deferred_edges: Vec<(PostOrderId, HirId)>,
- /// This maps HirIds of expressions to their post-order index. It is
- /// used in process_deferred_edges to correctly add back-edges.
- post_order_map: HirIdMap<PostOrderId>,
-}
-
-impl Debug for DropRangesBuilder {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("DropRanges")
- .field("hir_id_map", &self.tracked_value_map)
- .field("post_order_maps", &self.post_order_map)
- .field("nodes", &self.nodes.iter_enumerated().collect::<BTreeMap<_, _>>())
- .finish()
- }
-}
-
-/// DropRanges keeps track of what values are definitely dropped at each point in the code.
-///
-/// Values of interest are defined by the hir_id of their place. Locations in code are identified
-/// by their index in the post-order traversal. At its core, DropRanges maps
-/// (hir_id, post_order_id) -> bool, where a true value indicates that the value is definitely
-/// dropped at the point of the node identified by post_order_id.
-impl DropRangesBuilder {
- /// Returns the number of values (hir_ids) that are tracked
- fn num_values(&self) -> usize {
- self.tracked_value_map.len()
- }
-
- fn node_mut(&mut self, id: PostOrderId) -> &mut NodeInfo {
- let size = self.num_values();
- self.nodes.ensure_contains_elem(id, || NodeInfo::new(size));
- &mut self.nodes[id]
- }
-
- fn add_control_edge(&mut self, from: PostOrderId, to: PostOrderId) {
- trace!("adding control edge from {:?} to {:?}", from, to);
- self.node_mut(from).successors.push(to);
- }
-}
-
-#[derive(Debug)]
-struct NodeInfo {
- /// IDs of nodes that can follow this one in the control flow
- ///
- /// If the vec is empty, then control proceeds to the next node.
- successors: Vec<PostOrderId>,
-
- /// List of hir_ids that are dropped by this node.
- drops: Vec<TrackedValueIndex>,
-
- /// List of hir_ids that are reinitialized by this node.
- reinits: Vec<TrackedValueIndex>,
-
- /// Set of values that are definitely dropped at this point.
- drop_state: BitSet<TrackedValueIndex>,
-}
-
-impl NodeInfo {
- fn new(num_values: usize) -> Self {
- Self {
- successors: vec![],
- drops: vec![],
- reinits: vec![],
- drop_state: BitSet::new_filled(num_values),
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_typeck/src/check/inherited.rs
deleted file mode 100644
index cd152eb97..000000000
--- a/compiler/rustc_typeck/src/check/inherited.rs
+++ /dev/null
@@ -1,183 +0,0 @@
-use super::callee::DeferredCallResolution;
-
-use rustc_data_structures::fx::FxHashSet;
-use rustc_hir as hir;
-use rustc_hir::def_id::LocalDefId;
-use rustc_hir::HirIdMap;
-use rustc_infer::infer;
-use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
-use rustc_middle::ty::fold::TypeFoldable;
-use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::def_id::LocalDefIdMap;
-use rustc_span::{self, Span};
-use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::{self, ObligationCause, TraitEngine, TraitEngineExt};
-
-use std::cell::RefCell;
-use std::ops::Deref;
-
-/// Closures defined within the function. For example:
-/// ```ignore (illustrative)
-/// fn foo() {
-/// bar(move|| { ... })
-/// }
-/// ```
-/// Here, the function `foo()` and the closure passed to
-/// `bar()` will each have their own `FnCtxt`, but they will
-/// share the inherited fields.
-pub struct Inherited<'a, 'tcx> {
- pub(super) infcx: InferCtxt<'a, 'tcx>,
-
- pub(super) typeck_results: &'a RefCell<ty::TypeckResults<'tcx>>,
-
- pub(super) locals: RefCell<HirIdMap<super::LocalTy<'tcx>>>,
-
- pub(super) fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx>>>,
-
- // Some additional `Sized` obligations badly affect type inference.
- // These obligations are added in a later stage of typeck.
- pub(super) deferred_sized_obligations:
- RefCell<Vec<(Ty<'tcx>, Span, traits::ObligationCauseCode<'tcx>)>>,
-
- // When we process a call like `c()` where `c` is a closure type,
- // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or
- // `FnOnce` closure. In that case, we defer full resolution of the
- // call until upvar inference can kick in and make the
- // decision. We keep these deferred resolutions grouped by the
- // def-id of the closure, so that once we decide, we can easily go
- // back and process them.
- pub(super) deferred_call_resolutions: RefCell<LocalDefIdMap<Vec<DeferredCallResolution<'tcx>>>>,
-
- pub(super) deferred_cast_checks: RefCell<Vec<super::cast::CastCheck<'tcx>>>,
-
- pub(super) deferred_transmute_checks: RefCell<Vec<(Ty<'tcx>, Ty<'tcx>, Span)>>,
-
- pub(super) deferred_asm_checks: RefCell<Vec<(&'tcx hir::InlineAsm<'tcx>, hir::HirId)>>,
-
- pub(super) deferred_generator_interiors:
- RefCell<Vec<(hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>,
-
- pub(super) body_id: Option<hir::BodyId>,
-
- /// Whenever we introduce an adjustment from `!` into a type variable,
- /// we record that type variable here. This is later used to inform
- /// fallback. See the `fallback` module for details.
- pub(super) diverging_type_vars: RefCell<FxHashSet<Ty<'tcx>>>,
-}
-
-impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> {
- type Target = InferCtxt<'a, 'tcx>;
- fn deref(&self) -> &Self::Target {
- &self.infcx
- }
-}
-
-/// A temporary returned by `Inherited::build(...)`. This is necessary
-/// for multiple `InferCtxt` to share the same `in_progress_typeck_results`
-/// without using `Rc` or something similar.
-pub struct InheritedBuilder<'tcx> {
- infcx: infer::InferCtxtBuilder<'tcx>,
- def_id: LocalDefId,
-}
-
-impl<'tcx> Inherited<'_, 'tcx> {
- pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> {
- let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner;
-
- InheritedBuilder {
- infcx: tcx
- .infer_ctxt()
- .ignoring_regions()
- .with_fresh_in_progress_typeck_results(hir_owner),
- def_id,
- }
- }
-}
-
-impl<'tcx> InheritedBuilder<'tcx> {
- pub fn enter<F, R>(&mut self, f: F) -> R
- where
- F: for<'a> FnOnce(Inherited<'a, 'tcx>) -> R,
- {
- let def_id = self.def_id;
- self.infcx.enter(|infcx| f(Inherited::new(infcx, def_id)))
- }
-}
-
-impl<'a, 'tcx> Inherited<'a, 'tcx> {
- fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self {
- let tcx = infcx.tcx;
- let body_id = tcx.hir().maybe_body_owned_by(def_id);
- let typeck_results =
- infcx.in_progress_typeck_results.expect("building `FnCtxt` without typeck results");
-
- Inherited {
- typeck_results,
- infcx,
- fulfillment_cx: RefCell::new(<dyn TraitEngine<'_>>::new(tcx)),
- locals: RefCell::new(Default::default()),
- deferred_sized_obligations: RefCell::new(Vec::new()),
- deferred_call_resolutions: RefCell::new(Default::default()),
- deferred_cast_checks: RefCell::new(Vec::new()),
- deferred_transmute_checks: RefCell::new(Vec::new()),
- deferred_asm_checks: RefCell::new(Vec::new()),
- deferred_generator_interiors: RefCell::new(Vec::new()),
- diverging_type_vars: RefCell::new(Default::default()),
- body_id,
- }
- }
-
- #[instrument(level = "debug", skip(self))]
- pub(super) fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) {
- if obligation.has_escaping_bound_vars() {
- span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation);
- }
- self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation);
- }
-
- pub(super) fn register_predicates<I>(&self, obligations: I)
- where
- I: IntoIterator<Item = traits::PredicateObligation<'tcx>>,
- {
- for obligation in obligations {
- self.register_predicate(obligation);
- }
- }
-
- pub(super) fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T {
- self.register_predicates(infer_ok.obligations);
- infer_ok.value
- }
-
- pub(super) fn normalize_associated_types_in<T>(
- &self,
- span: Span,
- body_id: hir::HirId,
- param_env: ty::ParamEnv<'tcx>,
- value: T,
- ) -> T
- where
- T: TypeFoldable<'tcx>,
- {
- self.normalize_associated_types_in_with_cause(
- ObligationCause::misc(span, body_id),
- param_env,
- value,
- )
- }
-
- pub(super) fn normalize_associated_types_in_with_cause<T>(
- &self,
- cause: ObligationCause<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- value: T,
- ) -> T
- where
- T: TypeFoldable<'tcx>,
- {
- let ok = self.partially_normalize_associated_types_in(cause, param_env, value);
- debug!(?ok);
- self.register_infer_ok_obligations(ok)
- }
-}
diff --git a/compiler/rustc_typeck/src/check/intrinsicck.rs b/compiler/rustc_typeck/src/check/intrinsicck.rs
deleted file mode 100644
index df94abbaf..000000000
--- a/compiler/rustc_typeck/src/check/intrinsicck.rs
+++ /dev/null
@@ -1,530 +0,0 @@
-use rustc_ast::InlineAsmTemplatePiece;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::struct_span_err;
-use rustc_hir as hir;
-use rustc_index::vec::Idx;
-use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
-use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitable, UintTy};
-use rustc_session::lint;
-use rustc_span::{Span, Symbol, DUMMY_SP};
-use rustc_target::abi::{Pointer, VariantIdx};
-use rustc_target::asm::{InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType};
-use rustc_trait_selection::infer::InferCtxtExt;
-
-use super::FnCtxt;
-
-/// If the type is `Option<T>`, it will return `T`, otherwise
-/// the type itself. Works on most `Option`-like types.
-fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
- let ty::Adt(def, substs) = *ty.kind() else { return ty };
-
- if def.variants().len() == 2 && !def.repr().c() && def.repr().int.is_none() {
- let data_idx;
-
- let one = VariantIdx::new(1);
- let zero = VariantIdx::new(0);
-
- if def.variant(zero).fields.is_empty() {
- data_idx = one;
- } else if def.variant(one).fields.is_empty() {
- data_idx = zero;
- } else {
- return ty;
- }
-
- if def.variant(data_idx).fields.len() == 1 {
- return def.variant(data_idx).fields[0].ty(tcx, substs);
- }
- }
-
- ty
-}
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- pub fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) {
- let convert = |ty: Ty<'tcx>| {
- let ty = self.resolve_vars_if_possible(ty);
- let ty = self.tcx.normalize_erasing_regions(self.param_env, ty);
- (SizeSkeleton::compute(ty, self.tcx, self.param_env), ty)
- };
- let (sk_from, from) = convert(from);
- let (sk_to, to) = convert(to);
-
- // Check for same size using the skeletons.
- if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
- if sk_from.same_size(sk_to) {
- return;
- }
-
- // Special-case transmuting from `typeof(function)` and
- // `Option<typeof(function)>` to present a clearer error.
- let from = unpack_option_like(self.tcx, from);
- if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) && size_to == Pointer.size(&self.tcx) {
- struct_span_err!(self.tcx.sess, span, E0591, "can't transmute zero-sized type")
- .note(&format!("source type: {from}"))
- .note(&format!("target type: {to}"))
- .help("cast with `as` to a pointer instead")
- .emit();
- return;
- }
- }
-
- // Try to display a sensible error with as much information as possible.
- let skeleton_string = |ty: Ty<'tcx>, sk| match sk {
- Ok(SizeSkeleton::Known(size)) => format!("{} bits", size.bits()),
- Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
- Err(LayoutError::Unknown(bad)) => {
- if bad == ty {
- "this type does not have a fixed size".to_owned()
- } else {
- format!("size can vary because of {bad}")
- }
- }
- Err(err) => err.to_string(),
- };
-
- let mut err = struct_span_err!(
- self.tcx.sess,
- span,
- E0512,
- "cannot transmute between types of different sizes, \
- or dependently-sized types"
- );
- if from == to {
- err.note(&format!("`{from}` does not have a fixed size"));
- } else {
- err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)))
- .note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
- }
- err.emit();
- }
-
- // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()`
- fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
- // Type still may have region variables, but `Sized` does not depend
- // on those, so just erase them before querying.
- if self.tcx.erase_regions(ty).is_sized(self.tcx.at(DUMMY_SP), self.param_env) {
- return true;
- }
- if let ty::Foreign(..) = ty.kind() {
- return true;
- }
- false
- }
-}
-
-pub struct InlineAsmCtxt<'a, 'tcx> {
- tcx: TyCtxt<'tcx>,
- fcx: Option<&'a FnCtxt<'a, 'tcx>>,
-}
-
-impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
- pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self {
- InlineAsmCtxt { tcx, fcx: None }
- }
-
- pub fn new_in_fn(fcx: &'a FnCtxt<'a, 'tcx>) -> Self {
- InlineAsmCtxt { tcx: fcx.tcx, fcx: Some(fcx) }
- }
-
- fn check_asm_operand_type(
- &self,
- idx: usize,
- reg: InlineAsmRegOrRegClass,
- expr: &hir::Expr<'tcx>,
- template: &[InlineAsmTemplatePiece],
- is_input: bool,
- tied_input: Option<(&hir::Expr<'tcx>, Option<InlineAsmType>)>,
- target_features: &FxHashSet<Symbol>,
- ) -> Option<InlineAsmType> {
- let fcx = self.fcx.unwrap_or_else(|| span_bug!(expr.span, "asm operand for global asm"));
- // Check the type against the allowed types for inline asm.
- let ty = fcx.typeck_results.borrow().expr_ty_adjusted(expr);
- let ty = fcx.resolve_vars_if_possible(ty);
- let asm_ty_isize = match self.tcx.sess.target.pointer_width {
- 16 => InlineAsmType::I16,
- 32 => InlineAsmType::I32,
- 64 => InlineAsmType::I64,
- _ => unreachable!(),
- };
-
- // Expect types to be fully resolved, no const or type variables.
- if ty.has_infer_types_or_consts() {
- assert!(fcx.is_tainted_by_errors());
- return None;
- }
-
- let asm_ty = match *ty.kind() {
- // `!` is allowed for input but not for output (issue #87802)
- ty::Never if is_input => return None,
- ty::Error(_) => return None,
- ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8),
- ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16),
- ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32),
- ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64),
- ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128),
- ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize),
- ty::Float(FloatTy::F32) => Some(InlineAsmType::F32),
- ty::Float(FloatTy::F64) => Some(InlineAsmType::F64),
- ty::FnPtr(_) => Some(asm_ty_isize),
- ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if fcx.is_thin_ptr_ty(ty) => {
- Some(asm_ty_isize)
- }
- ty::Adt(adt, substs) if adt.repr().simd() => {
- let fields = &adt.non_enum_variant().fields;
- let elem_ty = fields[0].ty(self.tcx, substs);
- match elem_ty.kind() {
- ty::Never | ty::Error(_) => return None,
- ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => {
- Some(InlineAsmType::VecI8(fields.len() as u64))
- }
- ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => {
- Some(InlineAsmType::VecI16(fields.len() as u64))
- }
- ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => {
- Some(InlineAsmType::VecI32(fields.len() as u64))
- }
- ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => {
- Some(InlineAsmType::VecI64(fields.len() as u64))
- }
- ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => {
- Some(InlineAsmType::VecI128(fields.len() as u64))
- }
- ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => {
- Some(match self.tcx.sess.target.pointer_width {
- 16 => InlineAsmType::VecI16(fields.len() as u64),
- 32 => InlineAsmType::VecI32(fields.len() as u64),
- 64 => InlineAsmType::VecI64(fields.len() as u64),
- _ => unreachable!(),
- })
- }
- ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(fields.len() as u64)),
- ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(fields.len() as u64)),
- _ => None,
- }
- }
- ty::Infer(_) => unreachable!(),
- _ => None,
- };
- let Some(asm_ty) = asm_ty else {
- let msg = &format!("cannot use value of type `{ty}` for inline assembly");
- let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
- err.note(
- "only integers, floats, SIMD vectors, pointers and function pointers \
- can be used as arguments for inline assembly",
- );
- err.emit();
- return None;
- };
-
- // Check that the type implements Copy. The only case where this can
- // possibly fail is for SIMD types which don't #[derive(Copy)].
- if !fcx.infcx.type_is_copy_modulo_regions(fcx.param_env, ty, DUMMY_SP) {
- let msg = "arguments for inline assembly must be copyable";
- let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
- err.note(&format!("`{ty}` does not implement the Copy trait"));
- err.emit();
- }
-
- // Ideally we wouldn't need to do this, but LLVM's register allocator
- // really doesn't like it when tied operands have different types.
- //
- // This is purely an LLVM limitation, but we have to live with it since
- // there is no way to hide this with implicit conversions.
- //
- // For the purposes of this check we only look at the `InlineAsmType`,
- // which means that pointers and integers are treated as identical (modulo
- // size).
- if let Some((in_expr, Some(in_asm_ty))) = tied_input {
- if in_asm_ty != asm_ty {
- let msg = "incompatible types for asm inout argument";
- let mut err = self.tcx.sess.struct_span_err(vec![in_expr.span, expr.span], msg);
-
- let in_expr_ty = fcx.typeck_results.borrow().expr_ty_adjusted(in_expr);
- let in_expr_ty = fcx.resolve_vars_if_possible(in_expr_ty);
- err.span_label(in_expr.span, &format!("type `{in_expr_ty}`"));
- err.span_label(expr.span, &format!("type `{ty}`"));
- err.note(
- "asm inout arguments must have the same type, \
- unless they are both pointers or integers of the same size",
- );
- err.emit();
- }
-
- // All of the later checks have already been done on the input, so
- // let's not emit errors and warnings twice.
- return Some(asm_ty);
- }
-
- // Check the type against the list of types supported by the selected
- // register class.
- let asm_arch = self.tcx.sess.asm_arch.unwrap();
- let reg_class = reg.reg_class();
- let supported_tys = reg_class.supported_types(asm_arch);
- let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else {
- let msg = &format!("type `{ty}` cannot be used with this register class");
- let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
- let supported_tys: Vec<_> =
- supported_tys.iter().map(|(t, _)| t.to_string()).collect();
- err.note(&format!(
- "register class `{}` supports these types: {}",
- reg_class.name(),
- supported_tys.join(", "),
- ));
- if let Some(suggest) = reg_class.suggest_class(asm_arch, asm_ty) {
- err.help(&format!(
- "consider using the `{}` register class instead",
- suggest.name()
- ));
- }
- err.emit();
- return Some(asm_ty);
- };
-
- // Check whether the selected type requires a target feature. Note that
- // this is different from the feature check we did earlier. While the
- // previous check checked that this register class is usable at all
- // with the currently enabled features, some types may only be usable
- // with a register class when a certain feature is enabled. We check
- // this here since it depends on the results of typeck.
- //
- // Also note that this check isn't run when the operand type is never
- // (!). In that case we still need the earlier check to verify that the
- // register class is usable at all.
- if let Some(feature) = feature {
- if !target_features.contains(&feature) {
- let msg = &format!("`{}` target feature is not enabled", feature);
- let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
- err.note(&format!(
- "this is required to use type `{}` with register class `{}`",
- ty,
- reg_class.name(),
- ));
- err.emit();
- return Some(asm_ty);
- }
- }
-
- // Check whether a modifier is suggested for using this type.
- if let Some((suggested_modifier, suggested_result)) =
- reg_class.suggest_modifier(asm_arch, asm_ty)
- {
- // Search for any use of this operand without a modifier and emit
- // the suggestion for them.
- let mut spans = vec![];
- for piece in template {
- if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece
- {
- if operand_idx == idx && modifier.is_none() {
- spans.push(span);
- }
- }
- }
- if !spans.is_empty() {
- let (default_modifier, default_result) =
- reg_class.default_modifier(asm_arch).unwrap();
- self.tcx.struct_span_lint_hir(
- lint::builtin::ASM_SUB_REGISTER,
- expr.hir_id,
- spans,
- |lint| {
- let msg = "formatting may not be suitable for sub-register argument";
- let mut err = lint.build(msg);
- err.span_label(expr.span, "for this argument");
- err.help(&format!(
- "use the `{suggested_modifier}` modifier to have the register formatted as `{suggested_result}`",
- ));
- err.help(&format!(
- "or use the `{default_modifier}` modifier to keep the default formatting of `{default_result}`",
- ));
- err.emit();
- },
- );
- }
- }
-
- Some(asm_ty)
- }
-
- pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>, enclosing_id: hir::HirId) {
- let hir = self.tcx.hir();
- let enclosing_def_id = hir.local_def_id(enclosing_id).to_def_id();
- let target_features = self.tcx.asm_target_features(enclosing_def_id);
- let Some(asm_arch) = self.tcx.sess.asm_arch else {
- self.tcx.sess.delay_span_bug(DUMMY_SP, "target architecture does not support asm");
- return;
- };
- for (idx, (op, op_sp)) in asm.operands.iter().enumerate() {
- // Validate register classes against currently enabled target
- // features. We check that at least one type is available for
- // the enabled features.
- //
- // We ignore target feature requirements for clobbers: if the
- // feature is disabled then the compiler doesn't care what we
- // do with the registers.
- //
- // Note that this is only possible for explicit register
- // operands, which cannot be used in the asm string.
- if let Some(reg) = op.reg() {
- // Some explicit registers cannot be used depending on the
- // target. Reject those here.
- if let InlineAsmRegOrRegClass::Reg(reg) = reg {
- if let InlineAsmReg::Err = reg {
- // `validate` will panic on `Err`, as an error must
- // already have been reported.
- continue;
- }
- if let Err(msg) = reg.validate(
- asm_arch,
- self.tcx.sess.relocation_model(),
- &target_features,
- &self.tcx.sess.target,
- op.is_clobber(),
- ) {
- let msg = format!("cannot use register `{}`: {}", reg.name(), msg);
- self.tcx.sess.struct_span_err(*op_sp, &msg).emit();
- continue;
- }
- }
-
- if !op.is_clobber() {
- let mut missing_required_features = vec![];
- let reg_class = reg.reg_class();
- if let InlineAsmRegClass::Err = reg_class {
- continue;
- }
- for &(_, feature) in reg_class.supported_types(asm_arch) {
- match feature {
- Some(feature) => {
- if target_features.contains(&feature) {
- missing_required_features.clear();
- break;
- } else {
- missing_required_features.push(feature);
- }
- }
- None => {
- missing_required_features.clear();
- break;
- }
- }
- }
-
- // We are sorting primitive strs here and can use unstable sort here
- missing_required_features.sort_unstable();
- missing_required_features.dedup();
- match &missing_required_features[..] {
- [] => {}
- [feature] => {
- let msg = format!(
- "register class `{}` requires the `{}` target feature",
- reg_class.name(),
- feature
- );
- self.tcx.sess.struct_span_err(*op_sp, &msg).emit();
- // register isn't enabled, don't do more checks
- continue;
- }
- features => {
- let msg = format!(
- "register class `{}` requires at least one of the following target features: {}",
- reg_class.name(),
- features
- .iter()
- .map(|f| f.as_str())
- .intersperse(", ")
- .collect::<String>(),
- );
- self.tcx.sess.struct_span_err(*op_sp, &msg).emit();
- // register isn't enabled, don't do more checks
- continue;
- }
- }
- }
- }
-
- match *op {
- hir::InlineAsmOperand::In { reg, ref expr } => {
- self.check_asm_operand_type(
- idx,
- reg,
- expr,
- asm.template,
- true,
- None,
- &target_features,
- );
- }
- hir::InlineAsmOperand::Out { reg, late: _, ref expr } => {
- if let Some(expr) = expr {
- self.check_asm_operand_type(
- idx,
- reg,
- expr,
- asm.template,
- false,
- None,
- &target_features,
- );
- }
- }
- hir::InlineAsmOperand::InOut { reg, late: _, ref expr } => {
- self.check_asm_operand_type(
- idx,
- reg,
- expr,
- asm.template,
- false,
- None,
- &target_features,
- );
- }
- hir::InlineAsmOperand::SplitInOut { reg, late: _, ref in_expr, ref out_expr } => {
- let in_ty = self.check_asm_operand_type(
- idx,
- reg,
- in_expr,
- asm.template,
- true,
- None,
- &target_features,
- );
- if let Some(out_expr) = out_expr {
- self.check_asm_operand_type(
- idx,
- reg,
- out_expr,
- asm.template,
- false,
- Some((in_expr, in_ty)),
- &target_features,
- );
- }
- }
- // No special checking is needed for these:
- // - Typeck has checked that Const operands are integers.
- // - AST lowering guarantees that SymStatic points to a static.
- hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymStatic { .. } => {}
- // Check that sym actually points to a function. Later passes
- // depend on this.
- hir::InlineAsmOperand::SymFn { anon_const } => {
- let ty = self.tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id);
- match ty.kind() {
- ty::Never | ty::Error(_) => {}
- ty::FnDef(..) => {}
- _ => {
- let mut err =
- self.tcx.sess.struct_span_err(*op_sp, "invalid `sym` operand");
- err.span_label(
- self.tcx.hir().span(anon_const.body.hir_id),
- &format!("is {} `{}`", ty.kind().article(), ty),
- );
- err.help("`sym` operands must refer to either a function or a static");
- err.emit();
- }
- };
- }
- }
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs
deleted file mode 100644
index 0e678c41f..000000000
--- a/compiler/rustc_typeck/src/check/method/mod.rs
+++ /dev/null
@@ -1,658 +0,0 @@
-//! Method lookup: the secret sauce of Rust. See the [rustc dev guide] for more information.
-//!
-//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html
-
-mod confirm;
-mod prelude2021;
-pub mod probe;
-mod suggest;
-
-pub use self::suggest::SelfSource;
-pub use self::MethodError::*;
-
-use crate::check::{Expectation, FnCtxt};
-use crate::ObligationCause;
-use rustc_data_structures::sync::Lrc;
-use rustc_errors::{Applicability, Diagnostic};
-use rustc_hir as hir;
-use rustc_hir::def::{CtorOf, DefKind, Namespace};
-use rustc_hir::def_id::DefId;
-use rustc_infer::infer::{self, InferOk};
-use rustc_middle::ty::subst::Subst;
-use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
-use rustc_middle::ty::{
- self, AssocKind, DefIdTree, GenericParamDefKind, ProjectionPredicate, ProjectionTy, Term,
- ToPredicate, Ty, TypeVisitable,
-};
-use rustc_span::symbol::Ident;
-use rustc_span::Span;
-use rustc_trait_selection::traits;
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
-
-use self::probe::{IsSuggestion, ProbeScope};
-
-pub fn provide(providers: &mut ty::query::Providers) {
- probe::provide(providers);
-}
-
-#[derive(Clone, Copy, Debug)]
-pub struct MethodCallee<'tcx> {
- /// Impl method ID, for inherent methods, or trait method ID, otherwise.
- pub def_id: DefId,
- pub substs: SubstsRef<'tcx>,
-
- /// Instantiated method signature, i.e., it has been
- /// substituted, normalized, and has had late-bound
- /// lifetimes replaced with inference variables.
- pub sig: ty::FnSig<'tcx>,
-}
-
-#[derive(Debug)]
-pub enum MethodError<'tcx> {
- // Did not find an applicable method, but we did find various near-misses that may work.
- NoMatch(NoMatchData<'tcx>),
-
- // Multiple methods might apply.
- Ambiguity(Vec<CandidateSource>),
-
- // Found an applicable method, but it is not visible. The third argument contains a list of
- // not-in-scope traits which may work.
- PrivateMatch(DefKind, DefId, Vec<DefId>),
-
- // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have
- // forgotten to import a trait.
- IllegalSizedBound(Vec<DefId>, bool, Span),
-
- // Found a match, but the return type is wrong
- BadReturnType,
-}
-
-// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which
-// could lead to matches if satisfied, and a list of not-in-scope traits which may work.
-#[derive(Debug)]
-pub struct NoMatchData<'tcx> {
- pub static_candidates: Vec<CandidateSource>,
- pub unsatisfied_predicates:
- Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
- pub out_of_scope_traits: Vec<DefId>,
- pub lev_candidate: Option<ty::AssocItem>,
- pub mode: probe::Mode,
-}
-
-// A pared down enum describing just the places from which a method
-// candidate can arise. Used for error reporting only.
-#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
-pub enum CandidateSource {
- Impl(DefId),
- Trait(DefId /* trait id */),
-}
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- /// Determines whether the type `self_ty` supports a method name `method_name` or not.
- #[instrument(level = "debug", skip(self))]
- pub fn method_exists(
- &self,
- method_name: Ident,
- self_ty: Ty<'tcx>,
- call_expr_id: hir::HirId,
- allow_private: bool,
- ) -> bool {
- let mode = probe::Mode::MethodCall;
- match self.probe_for_name(
- method_name.span,
- mode,
- method_name,
- IsSuggestion(false),
- self_ty,
- call_expr_id,
- ProbeScope::TraitsInScope,
- ) {
- Ok(..) => true,
- Err(NoMatch(..)) => false,
- Err(Ambiguity(..)) => true,
- Err(PrivateMatch(..)) => allow_private,
- Err(IllegalSizedBound(..)) => true,
- Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"),
- }
- }
-
- /// Adds a suggestion to call the given method to the provided diagnostic.
- #[instrument(level = "debug", skip(self, err, call_expr))]
- pub(crate) fn suggest_method_call(
- &self,
- err: &mut Diagnostic,
- msg: &str,
- method_name: Ident,
- self_ty: Ty<'tcx>,
- call_expr: &hir::Expr<'_>,
- span: Option<Span>,
- ) {
- let params = self
- .probe_for_name(
- method_name.span,
- probe::Mode::MethodCall,
- method_name,
- IsSuggestion(false),
- self_ty,
- call_expr.hir_id,
- ProbeScope::TraitsInScope,
- )
- .map(|pick| {
- let sig = self.tcx.fn_sig(pick.item.def_id);
- sig.inputs().skip_binder().len().saturating_sub(1)
- })
- .unwrap_or(0);
-
- // Account for `foo.bar<T>`;
- let sugg_span = span.unwrap_or(call_expr.span).shrink_to_hi();
- let (suggestion, applicability) = (
- format!("({})", (0..params).map(|_| "_").collect::<Vec<_>>().join(", ")),
- if params > 0 { Applicability::HasPlaceholders } else { Applicability::MaybeIncorrect },
- );
-
- err.span_suggestion_verbose(sugg_span, msg, suggestion, applicability);
- }
-
- /// Performs method lookup. If lookup is successful, it will return the callee
- /// and store an appropriate adjustment for the self-expr. In some cases it may
- /// report an error (e.g., invoking the `drop` method).
- ///
- /// # Arguments
- ///
- /// Given a method call like `foo.bar::<T1,...Tn>(a, b + 1, ...)`:
- ///
- /// * `self`: the surrounding `FnCtxt` (!)
- /// * `self_ty`: the (unadjusted) type of the self expression (`foo`)
- /// * `segment`: the name and generic arguments of the method (`bar::<T1, ...Tn>`)
- /// * `span`: the span for the method call
- /// * `call_expr`: the complete method call: (`foo.bar::<T1,...Tn>(...)`)
- /// * `self_expr`: the self expression (`foo`)
- /// * `args`: the expressions of the arguments (`a, b + 1, ...`)
- #[instrument(level = "debug", skip(self, call_expr, self_expr))]
- pub fn lookup_method(
- &self,
- self_ty: Ty<'tcx>,
- segment: &hir::PathSegment<'_>,
- span: Span,
- call_expr: &'tcx hir::Expr<'tcx>,
- self_expr: &'tcx hir::Expr<'tcx>,
- args: &'tcx [hir::Expr<'tcx>],
- ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
- debug!(
- "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
- segment.ident, self_ty, call_expr, self_expr
- );
-
- let pick =
- self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
-
- self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args);
-
- for import_id in &pick.import_ids {
- debug!("used_trait_import: {:?}", import_id);
- Lrc::get_mut(&mut self.typeck_results.borrow_mut().used_trait_imports)
- .unwrap()
- .insert(*import_id);
- }
-
- self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span, None);
-
- let result =
- self.confirm_method(span, self_expr, call_expr, self_ty, pick.clone(), segment);
- debug!("result = {:?}", result);
-
- if let Some(span) = result.illegal_sized_bound {
- let mut needs_mut = false;
- if let ty::Ref(region, t_type, mutability) = self_ty.kind() {
- let trait_type = self
- .tcx
- .mk_ref(*region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() });
- // We probe again to see if there might be a borrow mutability discrepancy.
- match self.lookup_probe(
- span,
- segment.ident,
- trait_type,
- call_expr,
- ProbeScope::TraitsInScope,
- ) {
- Ok(ref new_pick) if *new_pick != pick => {
- needs_mut = true;
- }
- _ => {}
- }
- }
-
- // We probe again, taking all traits into account (not only those in scope).
- let mut candidates = match self.lookup_probe(
- span,
- segment.ident,
- self_ty,
- call_expr,
- ProbeScope::AllTraits,
- ) {
- // If we find a different result the caller probably forgot to import a trait.
- Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container_id(self.tcx)],
- Err(Ambiguity(ref sources)) => sources
- .iter()
- .filter_map(|source| {
- match *source {
- // Note: this cannot come from an inherent impl,
- // because the first probing succeeded.
- CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
- CandidateSource::Trait(_) => None,
- }
- })
- .collect(),
- _ => Vec::new(),
- };
- candidates.retain(|candidate| *candidate != self.tcx.parent(result.callee.def_id));
-
- return Err(IllegalSizedBound(candidates, needs_mut, span));
- }
-
- Ok(result.callee)
- }
-
- #[instrument(level = "debug", skip(self, call_expr))]
- pub fn lookup_probe(
- &self,
- span: Span,
- method_name: Ident,
- self_ty: Ty<'tcx>,
- call_expr: &'tcx hir::Expr<'tcx>,
- scope: ProbeScope,
- ) -> probe::PickResult<'tcx> {
- let mode = probe::Mode::MethodCall;
- let self_ty = self.resolve_vars_if_possible(self_ty);
- self.probe_for_name(
- span,
- mode,
- method_name,
- IsSuggestion(false),
- self_ty,
- call_expr.hir_id,
- scope,
- )
- }
-
- pub(super) fn obligation_for_method(
- &self,
- span: Span,
- trait_def_id: DefId,
- self_ty: Ty<'tcx>,
- opt_input_types: Option<&[Ty<'tcx>]>,
- ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
- {
- // Construct a trait-reference `self_ty : Trait<input_tys>`
- let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
- match param.kind {
- GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {}
- GenericParamDefKind::Type { .. } => {
- if param.index == 0 {
- return self_ty.into();
- } else if let Some(input_types) = opt_input_types {
- return input_types[param.index as usize - 1].into();
- }
- }
- }
- self.var_for_def(span, param)
- });
-
- let trait_ref = ty::TraitRef::new(trait_def_id, substs);
-
- // Construct an obligation
- let poly_trait_ref = ty::Binder::dummy(trait_ref);
- (
- traits::Obligation::misc(
- span,
- self.body_id,
- self.param_env,
- poly_trait_ref.without_const().to_predicate(self.tcx),
- ),
- substs,
- )
- }
-
- pub(super) fn obligation_for_op_method(
- &self,
- span: Span,
- trait_def_id: DefId,
- self_ty: Ty<'tcx>,
- opt_input_type: Option<Ty<'tcx>>,
- opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
- expected: Expectation<'tcx>,
- ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
- {
- // Construct a trait-reference `self_ty : Trait<input_tys>`
- let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
- match param.kind {
- GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {}
- GenericParamDefKind::Type { .. } => {
- if param.index == 0 {
- return self_ty.into();
- } else if let Some(input_type) = opt_input_type {
- return input_type.into();
- }
- }
- }
- self.var_for_def(span, param)
- });
-
- let trait_ref = ty::TraitRef::new(trait_def_id, substs);
-
- // Construct an obligation
- let poly_trait_ref = ty::Binder::dummy(trait_ref);
- let opt_output_ty =
- expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty));
- let opt_output_assoc_item = self.tcx.associated_items(trait_def_id).find_by_name_and_kind(
- self.tcx,
- Ident::from_str("Output"),
- AssocKind::Type,
- trait_def_id,
- );
- let output_pred =
- opt_output_ty.zip(opt_output_assoc_item).map(|(output_ty, output_assoc_item)| {
- ty::Binder::dummy(ty::PredicateKind::Projection(ProjectionPredicate {
- projection_ty: ProjectionTy { substs, item_def_id: output_assoc_item.def_id },
- term: Term::Ty(output_ty),
- }))
- .to_predicate(self.tcx)
- });
-
- (
- traits::Obligation::new(
- traits::ObligationCause::new(
- span,
- self.body_id,
- traits::BinOp {
- rhs_span: opt_input_expr.map(|expr| expr.span),
- is_lit: opt_input_expr
- .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
- output_pred,
- },
- ),
- self.param_env,
- poly_trait_ref.without_const().to_predicate(self.tcx),
- ),
- substs,
- )
- }
-
- /// `lookup_method_in_trait` is used for overloaded operators.
- /// It does a very narrow slice of what the normal probe/confirm path does.
- /// In particular, it doesn't really do any probing: it simply constructs
- /// an obligation for a particular trait with the given self type and checks
- /// whether that trait is implemented.
- #[instrument(level = "debug", skip(self, span, opt_input_types))]
- pub(super) fn lookup_method_in_trait(
- &self,
- span: Span,
- m_name: Ident,
- trait_def_id: DefId,
- self_ty: Ty<'tcx>,
- opt_input_types: Option<&[Ty<'tcx>]>,
- ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
- debug!(
- "lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})",
- self_ty, m_name, trait_def_id, opt_input_types
- );
-
- let (obligation, substs) =
- self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types);
- self.construct_obligation_for_trait(
- span,
- m_name,
- trait_def_id,
- obligation,
- substs,
- None,
- false,
- )
- }
-
- pub(super) fn lookup_op_method_in_trait(
- &self,
- span: Span,
- m_name: Ident,
- trait_def_id: DefId,
- self_ty: Ty<'tcx>,
- opt_input_type: Option<Ty<'tcx>>,
- opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
- expected: Expectation<'tcx>,
- ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
- let (obligation, substs) = self.obligation_for_op_method(
- span,
- trait_def_id,
- self_ty,
- opt_input_type,
- opt_input_expr,
- expected,
- );
- self.construct_obligation_for_trait(
- span,
- m_name,
- trait_def_id,
- obligation,
- substs,
- opt_input_expr,
- true,
- )
- }
-
- // FIXME(#18741): it seems likely that we can consolidate some of this
- // code with the other method-lookup code. In particular, the second half
- // of this method is basically the same as confirmation.
- fn construct_obligation_for_trait(
- &self,
- span: Span,
- m_name: Ident,
- trait_def_id: DefId,
- obligation: traits::PredicateObligation<'tcx>,
- substs: &'tcx ty::List<ty::subst::GenericArg<'tcx>>,
- opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
- is_op: bool,
- ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
- debug!(?obligation);
-
- // Now we want to know if this can be matched
- if !self.predicate_may_hold(&obligation) {
- debug!("--> Cannot match obligation");
- // Cannot be matched, no such method resolution is possible.
- return None;
- }
-
- // Trait must have a method named `m_name` and it should not have
- // type parameters or early-bound regions.
- let tcx = self.tcx;
- let Some(method_item) = self.associated_value(trait_def_id, m_name) else {
- tcx.sess.delay_span_bug(
- span,
- "operator trait does not have corresponding operator method",
- );
- return None;
- };
- let def_id = method_item.def_id;
- let generics = tcx.generics_of(def_id);
- assert_eq!(generics.params.len(), 0);
-
- debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
- let mut obligations = vec![];
-
- // Instantiate late-bound regions and substitute the trait
- // parameters into the method type to get the actual method type.
- //
- // N.B., instantiate late-bound regions first so that
- // `instantiate_type_scheme` can normalize associated types that
- // may reference those regions.
- let fn_sig = tcx.bound_fn_sig(def_id);
- let fn_sig = fn_sig.subst(self.tcx, substs);
- let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig);
-
- let InferOk { value, obligations: o } = if is_op {
- self.normalize_op_associated_types_in_as_infer_ok(span, fn_sig, opt_input_expr)
- } else {
- self.normalize_associated_types_in_as_infer_ok(span, fn_sig)
- };
- let fn_sig = {
- obligations.extend(o);
- value
- };
-
- // Register obligations for the parameters. This will include the
- // `Self` parameter, which in turn has a bound of the main trait,
- // so this also effectively registers `obligation` as well. (We
- // used to register `obligation` explicitly, but that resulted in
- // double error messages being reported.)
- //
- // Note that as the method comes from a trait, it should not have
- // any late-bound regions appearing in its bounds.
- let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs);
-
- let InferOk { value, obligations: o } = if is_op {
- self.normalize_op_associated_types_in_as_infer_ok(span, bounds, opt_input_expr)
- } else {
- self.normalize_associated_types_in_as_infer_ok(span, bounds)
- };
- let bounds = {
- obligations.extend(o);
- value
- };
-
- assert!(!bounds.has_escaping_bound_vars());
-
- let cause = if is_op {
- ObligationCause::new(
- span,
- self.body_id,
- traits::BinOp {
- rhs_span: opt_input_expr.map(|expr| expr.span),
- is_lit: opt_input_expr
- .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
- output_pred: None,
- },
- )
- } else {
- traits::ObligationCause::misc(span, self.body_id)
- };
- obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds));
-
- // Also add an obligation for the method type being well-formed.
- let method_ty = tcx.mk_fn_ptr(ty::Binder::dummy(fn_sig));
- debug!(
- "lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}",
- method_ty, obligation
- );
- obligations.push(traits::Obligation::new(
- cause,
- self.param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())).to_predicate(tcx),
- ));
-
- let callee = MethodCallee { def_id, substs, sig: fn_sig };
-
- debug!("callee = {:?}", callee);
-
- Some(InferOk { obligations, value: callee })
- }
-
- /// Performs a [full-qualified function call] (formerly "universal function call") lookup. If
- /// lookup is successful, it will return the type of definition and the [`DefId`] of the found
- /// function definition.
- ///
- /// [full-qualified function call]: https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls
- ///
- /// # Arguments
- ///
- /// Given a function call like `Foo::bar::<T1,...Tn>(...)`:
- ///
- /// * `self`: the surrounding `FnCtxt` (!)
- /// * `span`: the span of the call, excluding arguments (`Foo::bar::<T1, ...Tn>`)
- /// * `method_name`: the identifier of the function within the container type (`bar`)
- /// * `self_ty`: the type to search within (`Foo`)
- /// * `self_ty_span` the span for the type being searched within (span of `Foo`)
- /// * `expr_id`: the [`hir::HirId`] of the expression composing the entire call
- #[instrument(level = "debug", skip(self))]
- pub fn resolve_fully_qualified_call(
- &self,
- span: Span,
- method_name: Ident,
- self_ty: Ty<'tcx>,
- self_ty_span: Span,
- expr_id: hir::HirId,
- ) -> Result<(DefKind, DefId), MethodError<'tcx>> {
- debug!(
- "resolve_fully_qualified_call: method_name={:?} self_ty={:?} expr_id={:?}",
- method_name, self_ty, expr_id,
- );
-
- let tcx = self.tcx;
-
- // Check if we have an enum variant.
- if let ty::Adt(adt_def, _) = self_ty.kind() {
- if adt_def.is_enum() {
- let variant_def = adt_def
- .variants()
- .iter()
- .find(|vd| tcx.hygienic_eq(method_name, vd.ident(tcx), adt_def.did()));
- if let Some(variant_def) = variant_def {
- // Braced variants generate unusable names in value namespace (reserved for
- // possible future use), so variants resolved as associated items may refer to
- // them as well. It's ok to use the variant's id as a ctor id since an
- // error will be reported on any use of such resolution anyway.
- let ctor_def_id = variant_def.ctor_def_id.unwrap_or(variant_def.def_id);
- tcx.check_stability(ctor_def_id, Some(expr_id), span, Some(method_name.span));
- return Ok((
- DefKind::Ctor(CtorOf::Variant, variant_def.ctor_kind),
- ctor_def_id,
- ));
- }
- }
- }
-
- let pick = self.probe_for_name(
- span,
- probe::Mode::Path,
- method_name,
- IsSuggestion(false),
- self_ty,
- expr_id,
- ProbeScope::TraitsInScope,
- )?;
-
- self.lint_fully_qualified_call_from_2018(
- span,
- method_name,
- self_ty,
- self_ty_span,
- expr_id,
- &pick,
- );
-
- debug!("resolve_fully_qualified_call: pick={:?}", pick);
- {
- let mut typeck_results = self.typeck_results.borrow_mut();
- let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap();
- for import_id in pick.import_ids {
- debug!("resolve_fully_qualified_call: used_trait_import: {:?}", import_id);
- used_trait_imports.insert(import_id);
- }
- }
-
- let def_kind = pick.item.kind.as_def_kind();
- debug!(
- "resolve_fully_qualified_call: def_kind={:?}, def_id={:?}",
- def_kind, pick.item.def_id
- );
- tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span));
- Ok((def_kind, pick.item.def_id))
- }
-
- /// Finds item with name `item_name` defined in impl/trait `def_id`
- /// and return it, or `None`, if no such item was defined there.
- pub fn associated_value(&self, def_id: DefId, item_name: Ident) -> Option<ty::AssocItem> {
- self.tcx
- .associated_items(def_id)
- .find_by_name_and_namespace(self.tcx, item_name, Namespace::ValueNS, def_id)
- .copied()
- }
-}
diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs
deleted file mode 100644
index 17c2e4868..000000000
--- a/compiler/rustc_typeck/src/check/mod.rs
+++ /dev/null
@@ -1,970 +0,0 @@
-/*!
-
-# typeck: check phase
-
-Within the check phase of type check, we check each item one at a time
-(bodies of function expressions are checked as part of the containing
-function). Inference is used to supply types wherever they are unknown.
-
-By far the most complex case is checking the body of a function. This
-can be broken down into several distinct phases:
-
-- gather: creates type variables to represent the type of each local
- variable and pattern binding.
-
-- main: the main pass does the lion's share of the work: it
- determines the types of all expressions, resolves
- methods, checks for most invalid conditions, and so forth. In
- some cases, where a type is unknown, it may create a type or region
- variable and use that as the type of an expression.
-
- In the process of checking, various constraints will be placed on
- these type variables through the subtyping relationships requested
- through the `demand` module. The `infer` module is in charge
- of resolving those constraints.
-
-- regionck: after main is complete, the regionck pass goes over all
- types looking for regions and making sure that they did not escape
- into places where they are not in scope. This may also influence the
- final assignments of the various region variables if there is some
- flexibility.
-
-- writeback: writes the final types within a function body, replacing
- type variables with their final inferred types. These final types
- are written into the `tcx.node_types` table, which should *never* contain
- any reference to a type variable.
-
-## Intermediate types
-
-While type checking a function, the intermediate types for the
-expressions, blocks, and so forth contained within the function are
-stored in `fcx.node_types` and `fcx.node_substs`. These types
-may contain unresolved type variables. After type checking is
-complete, the functions in the writeback module are used to take the
-types from this table, resolve them, and then write them into their
-permanent home in the type context `tcx`.
-
-This means that during inferencing you should use `fcx.write_ty()`
-and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of
-nodes within the function.
-
-The types of top-level items, which never contain unbound type
-variables, are stored directly into the `tcx` typeck_results.
-
-N.B., a type variable is not the same thing as a type parameter. A
-type variable is an instance of a type parameter. That is,
-given a generic function `fn foo<T>(t: T)`, while checking the
-function `foo`, the type `ty_param(0)` refers to the type `T`, which
-is treated in abstract. However, when `foo()` is called, `T` will be
-substituted for a fresh type variable `N`. This variable will
-eventually be resolved to some concrete type (which might itself be
-a type parameter).
-
-*/
-
-pub mod _match;
-mod autoderef;
-mod callee;
-pub mod cast;
-mod check;
-mod closure;
-pub mod coercion;
-mod compare_method;
-pub mod demand;
-mod diverges;
-pub mod dropck;
-mod expectation;
-mod expr;
-mod fallback;
-mod fn_ctxt;
-mod gather_locals;
-mod generator_interior;
-mod inherited;
-pub mod intrinsic;
-mod intrinsicck;
-pub mod method;
-mod op;
-mod pat;
-mod place_op;
-mod region;
-pub mod regionck;
-pub mod rvalue_scopes;
-mod upvar;
-pub mod wfcheck;
-pub mod writeback;
-
-use check::{check_abi, check_fn, check_mod_item_types};
-pub use diverges::Diverges;
-pub use expectation::Expectation;
-pub use fn_ctxt::*;
-use hir::def::CtorOf;
-pub use inherited::{Inherited, InheritedBuilder};
-
-use crate::astconv::AstConv;
-use crate::check::gather_locals::GatherLocalsVisitor;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{
- pluralize, struct_span_err, Applicability, DiagnosticBuilder, EmissionGuarantee, MultiSpan,
-};
-use rustc_hir as hir;
-use rustc_hir::def::Res;
-use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::intravisit::Visitor;
-use rustc_hir::{HirIdMap, ImplicitSelfKind, Node};
-use rustc_index::bit_set::BitSet;
-use rustc_index::vec::Idx;
-use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
-use rustc_middle::ty::{self, Ty, TyCtxt, UserType};
-use rustc_session::config;
-use rustc_session::parse::feature_err;
-use rustc_session::Session;
-use rustc_span::source_map::DUMMY_SP;
-use rustc_span::symbol::{kw, Ident};
-use rustc_span::{self, BytePos, Span};
-use rustc_target::abi::VariantIdx;
-use rustc_target::spec::abi::Abi;
-use rustc_trait_selection::traits;
-use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error;
-use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
-use std::cell::RefCell;
-
-use crate::require_c_abi_if_c_variadic;
-use crate::util::common::indenter;
-
-use self::coercion::DynamicCoerceMany;
-use self::region::region_scope_tree;
-pub use self::Expectation::*;
-
-#[macro_export]
-macro_rules! type_error_struct {
- ($session:expr, $span:expr, $typ:expr, $code:ident, $($message:tt)*) => ({
- let mut err = rustc_errors::struct_span_err!($session, $span, $code, $($message)*);
-
- if $typ.references_error() {
- err.downgrade_to_delayed_bug();
- }
-
- err
- })
-}
-
-/// The type of a local binding, including the revealed type for anon types.
-#[derive(Copy, Clone, Debug)]
-pub struct LocalTy<'tcx> {
- decl_ty: Ty<'tcx>,
- revealed_ty: Ty<'tcx>,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum Needs {
- MutPlace,
- None,
-}
-
-impl Needs {
- fn maybe_mut_place(m: hir::Mutability) -> Self {
- match m {
- hir::Mutability::Mut => Needs::MutPlace,
- hir::Mutability::Not => Needs::None,
- }
- }
-}
-
-#[derive(Copy, Clone)]
-pub struct UnsafetyState {
- pub def: hir::HirId,
- pub unsafety: hir::Unsafety,
- from_fn: bool,
-}
-
-impl UnsafetyState {
- pub fn function(unsafety: hir::Unsafety, def: hir::HirId) -> UnsafetyState {
- UnsafetyState { def, unsafety, from_fn: true }
- }
-
- pub fn recurse(self, blk: &hir::Block<'_>) -> UnsafetyState {
- use hir::BlockCheckMode;
- match self.unsafety {
- // If this unsafe, then if the outer function was already marked as
- // unsafe we shouldn't attribute the unsafe'ness to the block. This
- // way the block can be warned about instead of ignoring this
- // extraneous block (functions are never warned about).
- hir::Unsafety::Unsafe if self.from_fn => self,
-
- unsafety => {
- let (unsafety, def) = match blk.rules {
- BlockCheckMode::UnsafeBlock(..) => (hir::Unsafety::Unsafe, blk.hir_id),
- BlockCheckMode::DefaultBlock => (unsafety, self.def),
- };
- UnsafetyState { def, unsafety, from_fn: false }
- }
- }
- }
-}
-
-#[derive(Debug, Copy, Clone)]
-pub enum PlaceOp {
- Deref,
- Index,
-}
-
-pub struct BreakableCtxt<'tcx> {
- may_break: bool,
-
- // this is `null` for loops where break with a value is illegal,
- // such as `while`, `for`, and `while let`
- coerce: Option<DynamicCoerceMany<'tcx>>,
-}
-
-pub struct EnclosingBreakables<'tcx> {
- stack: Vec<BreakableCtxt<'tcx>>,
- by_id: HirIdMap<usize>,
-}
-
-impl<'tcx> EnclosingBreakables<'tcx> {
- fn find_breakable(&mut self, target_id: hir::HirId) -> &mut BreakableCtxt<'tcx> {
- self.opt_find_breakable(target_id).unwrap_or_else(|| {
- bug!("could not find enclosing breakable with id {}", target_id);
- })
- }
-
- fn opt_find_breakable(&mut self, target_id: hir::HirId) -> Option<&mut BreakableCtxt<'tcx>> {
- match self.by_id.get(&target_id) {
- Some(ix) => Some(&mut self.stack[*ix]),
- None => None,
- }
- }
-}
-
-pub fn provide(providers: &mut Providers) {
- method::provide(providers);
- wfcheck::provide(providers);
- *providers = Providers {
- typeck_item_bodies,
- typeck_const_arg,
- typeck,
- diagnostic_only_typeck,
- has_typeck_results,
- adt_destructor,
- used_trait_imports,
- check_mod_item_types,
- region_scope_tree,
- ..*providers
- };
-}
-
-fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::Destructor> {
- tcx.calculate_dtor(def_id, dropck::check_drop_impl)
-}
-
-/// If this `DefId` is a "primary tables entry", returns
-/// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`.
-///
-/// If this function returns `Some`, then `typeck_results(def_id)` will
-/// succeed; if it returns `None`, then `typeck_results(def_id)` may or
-/// may not succeed. In some cases where this function returns `None`
-/// (notably closures), `typeck_results(def_id)` would wind up
-/// redirecting to the owning function.
-fn primary_body_of(
- tcx: TyCtxt<'_>,
- id: hir::HirId,
-) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> {
- match tcx.hir().get(id) {
- Node::Item(item) => match item.kind {
- hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => {
- Some((body, Some(ty), None))
- }
- hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))),
- _ => None,
- },
- Node::TraitItem(item) => match item.kind {
- hir::TraitItemKind::Const(ty, Some(body)) => Some((body, Some(ty), None)),
- hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
- Some((body, None, Some(sig)))
- }
- _ => None,
- },
- Node::ImplItem(item) => match item.kind {
- hir::ImplItemKind::Const(ty, body) => Some((body, Some(ty), None)),
- hir::ImplItemKind::Fn(ref sig, body) => Some((body, None, Some(sig))),
- _ => None,
- },
- Node::AnonConst(constant) => Some((constant.body, None, None)),
- _ => None,
- }
-}
-
-fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
- // Closures' typeck results come from their outermost function,
- // as they are part of the same "inference environment".
- let typeck_root_def_id = tcx.typeck_root_def_id(def_id);
- if typeck_root_def_id != def_id {
- return tcx.has_typeck_results(typeck_root_def_id);
- }
-
- if let Some(def_id) = def_id.as_local() {
- let id = tcx.hir().local_def_id_to_hir_id(def_id);
- primary_body_of(tcx, id).is_some()
- } else {
- false
- }
-}
-
-fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &FxHashSet<LocalDefId> {
- &*tcx.typeck(def_id).used_trait_imports
-}
-
-fn typeck_const_arg<'tcx>(
- tcx: TyCtxt<'tcx>,
- (did, param_did): (LocalDefId, DefId),
-) -> &ty::TypeckResults<'tcx> {
- let fallback = move || tcx.type_of(param_did);
- typeck_with_fallback(tcx, did, fallback)
-}
-
-fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> {
- if let Some(param_did) = tcx.opt_const_param_of(def_id) {
- tcx.typeck_const_arg((def_id, param_did))
- } else {
- let fallback = move || tcx.type_of(def_id.to_def_id());
- typeck_with_fallback(tcx, def_id, fallback)
- }
-}
-
-/// Used only to get `TypeckResults` for type inference during error recovery.
-/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors.
-fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> {
- let fallback = move || {
- let span = tcx.hir().span(tcx.hir().local_def_id_to_hir_id(def_id));
- tcx.ty_error_with_message(span, "diagnostic only typeck table used")
- };
- typeck_with_fallback(tcx, def_id, fallback)
-}
-
-#[instrument(skip(tcx, fallback))]
-fn typeck_with_fallback<'tcx>(
- tcx: TyCtxt<'tcx>,
- def_id: LocalDefId,
- fallback: impl Fn() -> Ty<'tcx> + 'tcx,
-) -> &'tcx ty::TypeckResults<'tcx> {
- // Closures' typeck results come from their outermost function,
- // as they are part of the same "inference environment".
- let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local();
- if typeck_root_def_id != def_id {
- return tcx.typeck(typeck_root_def_id);
- }
-
- let id = tcx.hir().local_def_id_to_hir_id(def_id);
- let span = tcx.hir().span(id);
-
- // Figure out what primary body this item has.
- let (body_id, body_ty, fn_sig) = primary_body_of(tcx, id).unwrap_or_else(|| {
- span_bug!(span, "can't type-check body of {:?}", def_id);
- });
- let body = tcx.hir().body(body_id);
-
- let typeck_results = Inherited::build(tcx, def_id).enter(|inh| {
- let param_env = tcx.param_env(def_id);
- let fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
- let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() {
- let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
- <dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None)
- } else {
- tcx.fn_sig(def_id)
- };
-
- check_abi(tcx, id, span, fn_sig.abi());
-
- // Compute the function signature from point of view of inside the fn.
- let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
- let fn_sig = inh.normalize_associated_types_in(
- body.value.span,
- body_id.hir_id,
- param_env,
- fn_sig,
- );
- check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0
- } else {
- let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
- let expected_type = body_ty
- .and_then(|ty| match ty.kind {
- hir::TyKind::Infer => Some(<dyn AstConv<'_>>::ast_ty_to_ty(&fcx, ty)),
- _ => None,
- })
- .unwrap_or_else(|| match tcx.hir().get(id) {
- Node::AnonConst(_) => match tcx.hir().get(tcx.hir().get_parent_node(id)) {
- Node::Expr(&hir::Expr {
- kind: hir::ExprKind::ConstBlock(ref anon_const),
- ..
- }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::TypeInference,
- span,
- }),
- Node::Ty(&hir::Ty {
- kind: hir::TyKind::Typeof(ref anon_const), ..
- }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::TypeInference,
- span,
- }),
- Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. })
- | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => {
- let operand_ty = asm
- .operands
- .iter()
- .filter_map(|(op, _op_sp)| match op {
- hir::InlineAsmOperand::Const { anon_const }
- if anon_const.hir_id == id =>
- {
- // Inline assembly constants must be integers.
- Some(fcx.next_int_var())
- }
- hir::InlineAsmOperand::SymFn { anon_const }
- if anon_const.hir_id == id =>
- {
- Some(fcx.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::MiscVariable,
- span,
- }))
- }
- _ => None,
- })
- .next();
- operand_ty.unwrap_or_else(fallback)
- }
- _ => fallback(),
- },
- _ => fallback(),
- });
-
- let expected_type = fcx.normalize_associated_types_in(body.value.span, expected_type);
- fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
-
- // Gather locals in statics (because of block expressions).
- GatherLocalsVisitor::new(&fcx).visit_body(body);
-
- fcx.check_expr_coercable_to_type(&body.value, expected_type, None);
-
- fcx.write_ty(id, expected_type);
-
- fcx
- };
-
- let fallback_has_occurred = fcx.type_inference_fallback();
-
- // Even though coercion casts provide type hints, we check casts after fallback for
- // backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
- fcx.check_casts();
- fcx.select_obligations_where_possible(fallback_has_occurred, |_| {});
-
- // Closure and generator analysis may run after fallback
- // because they don't constrain other type variables.
- fcx.closure_analyze(body);
- assert!(fcx.deferred_call_resolutions.borrow().is_empty());
- // Before the generator analysis, temporary scopes shall be marked to provide more
- // precise information on types to be captured.
- fcx.resolve_rvalue_scopes(def_id.to_def_id());
- fcx.resolve_generator_interiors(def_id.to_def_id());
-
- for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
- let ty = fcx.normalize_ty(span, ty);
- fcx.require_type_is_sized(ty, span, code);
- }
-
- fcx.select_all_obligations_or_error();
-
- if !fcx.infcx.is_tainted_by_errors() {
- fcx.check_transmutes();
- }
-
- fcx.check_asms();
-
- fcx.infcx.skip_region_resolution();
-
- fcx.resolve_type_vars_in_body(body)
- });
-
- // Consistency check our TypeckResults instance can hold all ItemLocalIds
- // it will need to hold.
- assert_eq!(typeck_results.hir_owner, id.owner);
-
- typeck_results
-}
-
-/// When `check_fn` is invoked on a generator (i.e., a body that
-/// includes yield), it returns back some information about the yield
-/// points.
-struct GeneratorTypes<'tcx> {
- /// Type of generator argument / values returned by `yield`.
- resume_ty: Ty<'tcx>,
-
- /// Type of value that is yielded.
- yield_ty: Ty<'tcx>,
-
- /// Types that are captured (see `GeneratorInterior` for more).
- interior: Ty<'tcx>,
-
- /// Indicates if the generator is movable or static (immovable).
- movability: hir::Movability,
-}
-
-/// Given a `DefId` for an opaque type in return position, find its parent item's return
-/// expressions.
-fn get_owner_return_paths<'tcx>(
- tcx: TyCtxt<'tcx>,
- def_id: LocalDefId,
-) -> Option<(LocalDefId, ReturnsVisitor<'tcx>)> {
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
- let parent_id = tcx.hir().get_parent_item(hir_id);
- tcx.hir().find_by_def_id(parent_id).and_then(|node| node.body_id()).map(|body_id| {
- let body = tcx.hir().body(body_id);
- let mut visitor = ReturnsVisitor::default();
- visitor.visit_body(body);
- (parent_id, visitor)
- })
-}
-
-// Forbid defining intrinsics in Rust code,
-// as they must always be defined by the compiler.
-fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) {
- if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi {
- tcx.sess.span_err(sp, "intrinsic must be in `extern \"rust-intrinsic\" { ... }` block");
- }
-}
-
-fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) {
- // Only restricted on wasm target for now
- if !tcx.sess.target.is_like_wasm {
- return;
- }
-
- // If `#[link_section]` is missing, then nothing to verify
- let attrs = tcx.codegen_fn_attrs(id);
- if attrs.link_section.is_none() {
- return;
- }
-
- // For the wasm32 target statics with `#[link_section]` are placed into custom
- // sections of the final output file, but this isn't link custom sections of
- // other executable formats. Namely we can only embed a list of bytes,
- // nothing with pointers to anything else or relocations. If any relocation
- // show up, reject them here.
- // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is
- // the consumer's responsibility to ensure all bytes that have been read
- // have defined values.
- if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id())
- && alloc.inner().relocations().len() != 0
- {
- let msg = "statics with a custom `#[link_section]` must be a \
- simple list of bytes on the wasm target with no \
- extra levels of indirection such as references";
- tcx.sess.span_err(tcx.def_span(id), msg);
- }
-}
-
-fn report_forbidden_specialization(
- tcx: TyCtxt<'_>,
- impl_item: &hir::ImplItemRef,
- parent_impl: DefId,
-) {
- let mut err = struct_span_err!(
- tcx.sess,
- impl_item.span,
- E0520,
- "`{}` specializes an item from a parent `impl`, but \
- that item is not marked `default`",
- impl_item.ident
- );
- err.span_label(impl_item.span, format!("cannot specialize default item `{}`", impl_item.ident));
-
- match tcx.span_of_impl(parent_impl) {
- Ok(span) => {
- err.span_label(span, "parent `impl` is here");
- err.note(&format!(
- "to specialize, `{}` in the parent `impl` must be marked `default`",
- impl_item.ident
- ));
- }
- Err(cname) => {
- err.note(&format!("parent implementation is in crate `{cname}`"));
- }
- }
-
- err.emit();
-}
-
-fn missing_items_err(
- tcx: TyCtxt<'_>,
- impl_span: Span,
- missing_items: &[&ty::AssocItem],
- full_impl_span: Span,
-) {
- let missing_items_msg = missing_items
- .iter()
- .map(|trait_item| trait_item.name.to_string())
- .collect::<Vec<_>>()
- .join("`, `");
-
- let mut err = struct_span_err!(
- tcx.sess,
- impl_span,
- E0046,
- "not all trait items implemented, missing: `{missing_items_msg}`",
- );
- err.span_label(impl_span, format!("missing `{missing_items_msg}` in implementation"));
-
- // `Span` before impl block closing brace.
- let hi = full_impl_span.hi() - BytePos(1);
- // Point at the place right before the closing brace of the relevant `impl` to suggest
- // adding the associated item at the end of its body.
- let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi);
- // Obtain the level of indentation ending in `sugg_sp`.
- let padding =
- tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new());
-
- for trait_item in missing_items {
- let snippet = suggestion_signature(trait_item, tcx);
- let code = format!("{}{}\n{}", padding, snippet, padding);
- let msg = format!("implement the missing item: `{snippet}`");
- let appl = Applicability::HasPlaceholders;
- if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) {
- err.span_label(span, format!("`{}` from trait", trait_item.name));
- err.tool_only_span_suggestion(sugg_sp, &msg, code, appl);
- } else {
- err.span_suggestion_hidden(sugg_sp, &msg, code, appl);
- }
- }
- err.emit();
-}
-
-fn missing_items_must_implement_one_of_err(
- tcx: TyCtxt<'_>,
- impl_span: Span,
- missing_items: &[Ident],
- annotation_span: Option<Span>,
-) {
- let missing_items_msg =
- missing_items.iter().map(Ident::to_string).collect::<Vec<_>>().join("`, `");
-
- let mut err = struct_span_err!(
- tcx.sess,
- impl_span,
- E0046,
- "not all trait items implemented, missing one of: `{missing_items_msg}`",
- );
- err.span_label(impl_span, format!("missing one of `{missing_items_msg}` in implementation"));
-
- if let Some(annotation_span) = annotation_span {
- err.span_note(annotation_span, "required because of this annotation");
- }
-
- err.emit();
-}
-
-/// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions.
-fn bounds_from_generic_predicates<'tcx>(
- tcx: TyCtxt<'tcx>,
- predicates: ty::GenericPredicates<'tcx>,
-) -> (String, String) {
- let mut types: FxHashMap<Ty<'tcx>, Vec<DefId>> = FxHashMap::default();
- let mut projections = vec![];
- for (predicate, _) in predicates.predicates {
- debug!("predicate {:?}", predicate);
- let bound_predicate = predicate.kind();
- match bound_predicate.skip_binder() {
- ty::PredicateKind::Trait(trait_predicate) => {
- let entry = types.entry(trait_predicate.self_ty()).or_default();
- let def_id = trait_predicate.def_id();
- if Some(def_id) != tcx.lang_items().sized_trait() {
- // Type params are `Sized` by default, do not add that restriction to the list
- // if it is a positive requirement.
- entry.push(trait_predicate.def_id());
- }
- }
- ty::PredicateKind::Projection(projection_pred) => {
- projections.push(bound_predicate.rebind(projection_pred));
- }
- _ => {}
- }
- }
- let generics = if types.is_empty() {
- "".to_string()
- } else {
- format!(
- "<{}>",
- types
- .keys()
- .filter_map(|t| match t.kind() {
- ty::Param(_) => Some(t.to_string()),
- // Avoid suggesting the following:
- // fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
- _ => None,
- })
- .collect::<Vec<_>>()
- .join(", ")
- )
- };
- let mut where_clauses = vec![];
- for (ty, bounds) in types {
- where_clauses
- .extend(bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound))));
- }
- for projection in &projections {
- let p = projection.skip_binder();
- // FIXME: this is not currently supported syntax, we should be looking at the `types` and
- // insert the associated types where they correspond, but for now let's be "lazy" and
- // propose this instead of the following valid resugaring:
- // `T: Trait, Trait::Assoc = K` → `T: Trait<Assoc = K>`
- where_clauses.push(format!(
- "{} = {}",
- tcx.def_path_str(p.projection_ty.item_def_id),
- p.term,
- ));
- }
- let where_clauses = if where_clauses.is_empty() {
- String::new()
- } else {
- format!(" where {}", where_clauses.join(", "))
- };
- (generics, where_clauses)
-}
-
-/// Return placeholder code for the given function.
-fn fn_sig_suggestion<'tcx>(
- tcx: TyCtxt<'tcx>,
- sig: ty::FnSig<'tcx>,
- ident: Ident,
- predicates: ty::GenericPredicates<'tcx>,
- assoc: &ty::AssocItem,
-) -> String {
- let args = sig
- .inputs()
- .iter()
- .enumerate()
- .map(|(i, ty)| {
- Some(match ty.kind() {
- ty::Param(_) if assoc.fn_has_self_parameter && i == 0 => "self".to_string(),
- ty::Ref(reg, ref_ty, mutability) if i == 0 => {
- let reg = format!("{reg} ");
- let reg = match &reg[..] {
- "'_ " | " " => "",
- reg => reg,
- };
- if assoc.fn_has_self_parameter {
- match ref_ty.kind() {
- ty::Param(param) if param.name == kw::SelfUpper => {
- format!("&{}{}self", reg, mutability.prefix_str())
- }
-
- _ => format!("self: {ty}"),
- }
- } else {
- format!("_: {ty}")
- }
- }
- _ => {
- if assoc.fn_has_self_parameter && i == 0 {
- format!("self: {ty}")
- } else {
- format!("_: {ty}")
- }
- }
- })
- })
- .chain(std::iter::once(if sig.c_variadic { Some("...".to_string()) } else { None }))
- .flatten()
- .collect::<Vec<String>>()
- .join(", ");
- let output = sig.output();
- let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() };
-
- let unsafety = sig.unsafety.prefix_str();
- let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates);
-
- // FIXME: this is not entirely correct, as the lifetimes from borrowed params will
- // not be present in the `fn` definition, not will we account for renamed
- // lifetimes between the `impl` and the `trait`, but this should be good enough to
- // fill in a significant portion of the missing code, and other subsequent
- // suggestions can help the user fix the code.
- format!("{unsafety}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}")
-}
-
-/// Return placeholder code for the given associated item.
-/// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a
-/// structured suggestion.
-fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String {
- match assoc.kind {
- ty::AssocKind::Fn => {
- // We skip the binder here because the binder would deanonymize all
- // late-bound regions, and we don't want method signatures to show up
- // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
- // regions just fine, showing `fn(&MyType)`.
- fn_sig_suggestion(
- tcx,
- tcx.fn_sig(assoc.def_id).skip_binder(),
- assoc.ident(tcx),
- tcx.predicates_of(assoc.def_id),
- assoc,
- )
- }
- ty::AssocKind::Type => format!("type {} = Type;", assoc.name),
- ty::AssocKind::Const => {
- let ty = tcx.type_of(assoc.def_id);
- let val = expr::ty_kind_suggestion(ty).unwrap_or("value");
- format!("const {}: {} = {};", assoc.name, ty, val)
- }
- }
-}
-
-/// Emit an error when encountering two or more variants in a transparent enum.
-fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, did: DefId) {
- let variant_spans: Vec<_> = adt
- .variants()
- .iter()
- .map(|variant| tcx.hir().span_if_local(variant.def_id).unwrap())
- .collect();
- let msg = format!("needs exactly one variant, but has {}", adt.variants().len(),);
- let mut err = struct_span_err!(tcx.sess, sp, E0731, "transparent enum {msg}");
- err.span_label(sp, &msg);
- if let [start @ .., end] = &*variant_spans {
- for variant_span in start {
- err.span_label(*variant_span, "");
- }
- err.span_label(*end, &format!("too many variants in `{}`", tcx.def_path_str(did)));
- }
- err.emit();
-}
-
-/// Emit an error when encountering two or more non-zero-sized fields in a transparent
-/// enum.
-fn bad_non_zero_sized_fields<'tcx>(
- tcx: TyCtxt<'tcx>,
- adt: ty::AdtDef<'tcx>,
- field_count: usize,
- field_spans: impl Iterator<Item = Span>,
- sp: Span,
-) {
- let msg = format!("needs at most one non-zero-sized field, but has {field_count}");
- let mut err = struct_span_err!(
- tcx.sess,
- sp,
- E0690,
- "{}transparent {} {}",
- if adt.is_enum() { "the variant of a " } else { "" },
- adt.descr(),
- msg,
- );
- err.span_label(sp, &msg);
- for sp in field_spans {
- err.span_label(sp, "this field is non-zero-sized");
- }
- err.emit();
-}
-
-fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, qpath: &hir::QPath<'_>, span: Span) {
- struct_span_err!(
- tcx.sess,
- span,
- E0533,
- "expected unit struct, unit variant or constant, found {} `{}`",
- res.descr(),
- rustc_hir_pretty::qpath_to_string(qpath),
- )
- .emit();
-}
-
-/// Controls whether the arguments are tupled. This is used for the call
-/// operator.
-///
-/// Tupling means that all call-side arguments are packed into a tuple and
-/// passed as a single parameter. For example, if tupling is enabled, this
-/// function:
-/// ```
-/// fn f(x: (isize, isize)) {}
-/// ```
-/// Can be called as:
-/// ```ignore UNSOLVED (can this be done in user code?)
-/// # fn f(x: (isize, isize)) {}
-/// f(1, 2);
-/// ```
-/// Instead of:
-/// ```
-/// # fn f(x: (isize, isize)) {}
-/// f((1, 2));
-/// ```
-#[derive(Clone, Eq, PartialEq)]
-enum TupleArgumentsFlag {
- DontTupleArguments,
- TupleArguments,
-}
-
-fn typeck_item_bodies(tcx: TyCtxt<'_>, (): ()) {
- tcx.hir().par_body_owners(|body_owner_def_id| tcx.ensure().typeck(body_owner_def_id));
-}
-
-fn fatally_break_rust(sess: &Session) {
- let handler = sess.diagnostic();
- handler.span_bug_no_panic(
- MultiSpan::new(),
- "It looks like you're trying to break rust; would you like some ICE?",
- );
- handler.note_without_error("the compiler expectedly panicked. this is a feature.");
- handler.note_without_error(
- "we would appreciate a joke overview: \
- https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675",
- );
- handler.note_without_error(&format!(
- "rustc {} running on {}",
- option_env!("CFG_VERSION").unwrap_or("unknown_version"),
- config::host_triple(),
- ));
-}
-
-fn potentially_plural_count(count: usize, word: &str) -> String {
- format!("{} {}{}", count, word, pluralize!(count))
-}
-
-fn has_expected_num_generic_args<'tcx>(
- tcx: TyCtxt<'tcx>,
- trait_did: Option<DefId>,
- expected: usize,
-) -> bool {
- trait_did.map_or(true, |trait_did| {
- let generics = tcx.generics_of(trait_did);
- generics.count() == expected + if generics.has_self { 1 } else { 0 }
- })
-}
-
-/// Suggests calling the constructor of a tuple struct or enum variant
-///
-/// * `snippet` - The snippet of code that references the constructor
-/// * `span` - The span of the snippet
-/// * `params` - The number of parameters the constructor accepts
-/// * `err` - A mutable diagnostic builder to add the suggestion to
-fn suggest_call_constructor<G: EmissionGuarantee>(
- span: Span,
- kind: CtorOf,
- params: usize,
- err: &mut DiagnosticBuilder<'_, G>,
-) {
- // Note: tuple-structs don't have named fields, so just use placeholders
- let args = vec!["_"; params].join(", ");
- let applicable = if params > 0 {
- Applicability::HasPlaceholders
- } else {
- // When n = 0, it's an empty-tuple struct/enum variant
- // so we trivially know how to construct it
- Applicability::MachineApplicable
- };
- let kind = match kind {
- CtorOf::Struct => "a struct",
- CtorOf::Variant => "an enum variant",
- };
- err.span_label(span, &format!("this is the constructor of {kind}"));
- err.multipart_suggestion(
- "call the constructor",
- vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))],
- applicable,
- );
-}
diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs
deleted file mode 100644
index 920b3e688..000000000
--- a/compiler/rustc_typeck/src/check/op.rs
+++ /dev/null
@@ -1,1076 +0,0 @@
-//! Code related to processing overloaded binary and unary operators.
-
-use super::method::MethodCallee;
-use super::{has_expected_num_generic_args, FnCtxt};
-use crate::check::Expectation;
-use rustc_ast as ast;
-use rustc_errors::{self, struct_span_err, Applicability, Diagnostic};
-use rustc_hir as hir;
-use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_infer::traits::ObligationCauseCode;
-use rustc_middle::ty::adjustment::{
- Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
-};
-use rustc_middle::ty::{
- self, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor,
-};
-use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{sym, Ident};
-use rustc_span::Span;
-use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt as _;
-use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt};
-use rustc_type_ir::sty::TyKind::*;
-
-use std::ops::ControlFlow;
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- /// Checks a `a <op>= b`
- pub fn check_binop_assign(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- op: hir::BinOp,
- lhs: &'tcx hir::Expr<'tcx>,
- rhs: &'tcx hir::Expr<'tcx>,
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- let (lhs_ty, rhs_ty, return_ty) =
- self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected);
-
- let ty =
- if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
- self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op);
- self.tcx.mk_unit()
- } else {
- return_ty
- };
-
- self.check_lhs_assignable(lhs, "E0067", op.span, |err| {
- if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
- if self
- .lookup_op_method(
- lhs_deref_ty,
- Some(rhs_ty),
- Some(rhs),
- Op::Binary(op, IsAssign::Yes),
- expected,
- )
- .is_ok()
- {
- // Suppress this error, since we already emitted
- // a deref suggestion in check_overloaded_binop
- err.delay_as_bug();
- }
- }
- });
-
- ty
- }
-
- /// Checks a potentially overloaded binary operator.
- pub fn check_binop(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- op: hir::BinOp,
- lhs_expr: &'tcx hir::Expr<'tcx>,
- rhs_expr: &'tcx hir::Expr<'tcx>,
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- let tcx = self.tcx;
-
- debug!(
- "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
- expr.hir_id, expr, op, lhs_expr, rhs_expr
- );
-
- match BinOpCategory::from(op) {
- BinOpCategory::Shortcircuit => {
- // && and || are a simple case.
- self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool, None);
- let lhs_diverges = self.diverges.get();
- self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool, None);
-
- // Depending on the LHS' value, the RHS can never execute.
- self.diverges.set(lhs_diverges);
-
- tcx.types.bool
- }
- _ => {
- // Otherwise, we always treat operators as if they are
- // overloaded. This is the way to be most flexible w/r/t
- // types that get inferred.
- let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop(
- expr,
- lhs_expr,
- rhs_expr,
- op,
- IsAssign::No,
- expected,
- );
-
- // Supply type inference hints if relevant. Probably these
- // hints should be enforced during select as part of the
- // `consider_unification_despite_ambiguity` routine, but this
- // more convenient for now.
- //
- // The basic idea is to help type inference by taking
- // advantage of things we know about how the impls for
- // scalar types are arranged. This is important in a
- // scenario like `1_u32 << 2`, because it lets us quickly
- // deduce that the result type should be `u32`, even
- // though we don't know yet what type 2 has and hence
- // can't pin this down to a specific impl.
- if !lhs_ty.is_ty_var()
- && !rhs_ty.is_ty_var()
- && is_builtin_binop(lhs_ty, rhs_ty, op)
- {
- let builtin_return_ty = self.enforce_builtin_binop_types(
- lhs_expr.span,
- lhs_ty,
- rhs_expr.span,
- rhs_ty,
- op,
- );
- self.demand_suptype(expr.span, builtin_return_ty, return_ty);
- }
-
- return_ty
- }
- }
- }
-
- fn enforce_builtin_binop_types(
- &self,
- lhs_span: Span,
- lhs_ty: Ty<'tcx>,
- rhs_span: Span,
- rhs_ty: Ty<'tcx>,
- op: hir::BinOp,
- ) -> Ty<'tcx> {
- debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
-
- // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
- // (See https://github.com/rust-lang/rust/issues/57447.)
- let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
-
- let tcx = self.tcx;
- match BinOpCategory::from(op) {
- BinOpCategory::Shortcircuit => {
- self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty);
- self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty);
- tcx.types.bool
- }
-
- BinOpCategory::Shift => {
- // result type is same as LHS always
- lhs_ty
- }
-
- BinOpCategory::Math | BinOpCategory::Bitwise => {
- // both LHS and RHS and result will have the same type
- self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
- lhs_ty
- }
-
- BinOpCategory::Comparison => {
- // both LHS and RHS and result will have the same type
- self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
- tcx.types.bool
- }
- }
- }
-
- fn check_overloaded_binop(
- &self,
- expr: &'tcx hir::Expr<'tcx>,
- lhs_expr: &'tcx hir::Expr<'tcx>,
- rhs_expr: &'tcx hir::Expr<'tcx>,
- op: hir::BinOp,
- is_assign: IsAssign,
- expected: Expectation<'tcx>,
- ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
- debug!(
- "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})",
- expr.hir_id, op, is_assign
- );
-
- let lhs_ty = match is_assign {
- IsAssign::No => {
- // Find a suitable supertype of the LHS expression's type, by coercing to
- // a type variable, to pass as the `Self` to the trait, avoiding invariant
- // trait matching creating lifetime constraints that are too strict.
- // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
- // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
- let lhs_ty = self.check_expr(lhs_expr);
- let fresh_var = self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::MiscVariable,
- span: lhs_expr.span,
- });
- self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
- }
- IsAssign::Yes => {
- // rust-lang/rust#52126: We have to use strict
- // equivalence on the LHS of an assign-op like `+=`;
- // overwritten or mutably-borrowed places cannot be
- // coerced to a supertype.
- self.check_expr(lhs_expr)
- }
- };
- let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
-
- // N.B., as we have not yet type-checked the RHS, we don't have the
- // type at hand. Make a variable to represent it. The whole reason
- // for this indirection is so that, below, we can check the expr
- // using this variable as the expected type, which sometimes lets
- // us do better coercions than we would be able to do otherwise,
- // particularly for things like `String + &String`.
- let rhs_ty_var = self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::MiscVariable,
- span: rhs_expr.span,
- });
-
- let result = self.lookup_op_method(
- lhs_ty,
- Some(rhs_ty_var),
- Some(rhs_expr),
- Op::Binary(op, is_assign),
- expected,
- );
-
- // see `NB` above
- let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr));
- let rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
-
- let return_ty = match result {
- Ok(method) => {
- let by_ref_binop = !op.node.is_by_value();
- if is_assign == IsAssign::Yes || by_ref_binop {
- if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() {
- let mutbl = match mutbl {
- hir::Mutability::Not => AutoBorrowMutability::Not,
- hir::Mutability::Mut => AutoBorrowMutability::Mut {
- // Allow two-phase borrows for binops in initial deployment
- // since they desugar to methods
- allow_two_phase_borrow: AllowTwoPhase::Yes,
- },
- };
- let autoref = Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
- target: method.sig.inputs()[0],
- };
- self.apply_adjustments(lhs_expr, vec![autoref]);
- }
- }
- if by_ref_binop {
- if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind() {
- let mutbl = match mutbl {
- hir::Mutability::Not => AutoBorrowMutability::Not,
- hir::Mutability::Mut => AutoBorrowMutability::Mut {
- // Allow two-phase borrows for binops in initial deployment
- // since they desugar to methods
- allow_two_phase_borrow: AllowTwoPhase::Yes,
- },
- };
- let autoref = Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
- target: method.sig.inputs()[1],
- };
- // HACK(eddyb) Bypass checks due to reborrows being in
- // some cases applied on the RHS, on top of which we need
- // to autoref, which is not allowed by apply_adjustments.
- // self.apply_adjustments(rhs_expr, vec![autoref]);
- self.typeck_results
- .borrow_mut()
- .adjustments_mut()
- .entry(rhs_expr.hir_id)
- .or_default()
- .push(autoref);
- }
- }
- self.write_method_call(expr.hir_id, method);
-
- method.sig.output()
- }
- // error types are considered "builtin"
- Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(),
- Err(errors) => {
- let source_map = self.tcx.sess.source_map();
- let (mut err, missing_trait, use_output) = match is_assign {
- IsAssign::Yes => {
- let mut err = struct_span_err!(
- self.tcx.sess,
- expr.span,
- E0368,
- "binary assignment operation `{}=` cannot be applied to type `{}`",
- op.node.as_str(),
- lhs_ty,
- );
- err.span_label(
- lhs_expr.span,
- format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty),
- );
- let missing_trait = match op.node {
- hir::BinOpKind::Add => Some("std::ops::AddAssign"),
- hir::BinOpKind::Sub => Some("std::ops::SubAssign"),
- hir::BinOpKind::Mul => Some("std::ops::MulAssign"),
- hir::BinOpKind::Div => Some("std::ops::DivAssign"),
- hir::BinOpKind::Rem => Some("std::ops::RemAssign"),
- hir::BinOpKind::BitAnd => Some("std::ops::BitAndAssign"),
- hir::BinOpKind::BitXor => Some("std::ops::BitXorAssign"),
- hir::BinOpKind::BitOr => Some("std::ops::BitOrAssign"),
- hir::BinOpKind::Shl => Some("std::ops::ShlAssign"),
- hir::BinOpKind::Shr => Some("std::ops::ShrAssign"),
- _ => None,
- };
- self.note_unmet_impls_on_type(&mut err, errors);
- (err, missing_trait, false)
- }
- IsAssign::No => {
- let (message, missing_trait, use_output) = match op.node {
- hir::BinOpKind::Add => (
- format!("cannot add `{rhs_ty}` to `{lhs_ty}`"),
- Some("std::ops::Add"),
- true,
- ),
- hir::BinOpKind::Sub => (
- format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`"),
- Some("std::ops::Sub"),
- true,
- ),
- hir::BinOpKind::Mul => (
- format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`"),
- Some("std::ops::Mul"),
- true,
- ),
- hir::BinOpKind::Div => (
- format!("cannot divide `{lhs_ty}` by `{rhs_ty}`"),
- Some("std::ops::Div"),
- true,
- ),
- hir::BinOpKind::Rem => (
- format!("cannot mod `{lhs_ty}` by `{rhs_ty}`"),
- Some("std::ops::Rem"),
- true,
- ),
- hir::BinOpKind::BitAnd => (
- format!("no implementation for `{lhs_ty} & {rhs_ty}`"),
- Some("std::ops::BitAnd"),
- true,
- ),
- hir::BinOpKind::BitXor => (
- format!("no implementation for `{lhs_ty} ^ {rhs_ty}`"),
- Some("std::ops::BitXor"),
- true,
- ),
- hir::BinOpKind::BitOr => (
- format!("no implementation for `{lhs_ty} | {rhs_ty}`"),
- Some("std::ops::BitOr"),
- true,
- ),
- hir::BinOpKind::Shl => (
- format!("no implementation for `{lhs_ty} << {rhs_ty}`"),
- Some("std::ops::Shl"),
- true,
- ),
- hir::BinOpKind::Shr => (
- format!("no implementation for `{lhs_ty} >> {rhs_ty}`"),
- Some("std::ops::Shr"),
- true,
- ),
- hir::BinOpKind::Eq | hir::BinOpKind::Ne => (
- format!(
- "binary operation `{}` cannot be applied to type `{}`",
- op.node.as_str(),
- lhs_ty
- ),
- Some("std::cmp::PartialEq"),
- false,
- ),
- hir::BinOpKind::Lt
- | hir::BinOpKind::Le
- | hir::BinOpKind::Gt
- | hir::BinOpKind::Ge => (
- format!(
- "binary operation `{}` cannot be applied to type `{}`",
- op.node.as_str(),
- lhs_ty
- ),
- Some("std::cmp::PartialOrd"),
- false,
- ),
- _ => (
- format!(
- "binary operation `{}` cannot be applied to type `{}`",
- op.node.as_str(),
- lhs_ty
- ),
- None,
- false,
- ),
- };
- let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}");
- if !lhs_expr.span.eq(&rhs_expr.span) {
- self.add_type_neq_err_label(
- &mut err,
- lhs_expr.span,
- lhs_ty,
- rhs_ty,
- rhs_expr,
- op,
- is_assign,
- expected,
- );
- self.add_type_neq_err_label(
- &mut err,
- rhs_expr.span,
- rhs_ty,
- lhs_ty,
- lhs_expr,
- op,
- is_assign,
- expected,
- );
- }
- self.note_unmet_impls_on_type(&mut err, errors);
- (err, missing_trait, use_output)
- }
- };
-
- let mut suggest_deref_binop = |lhs_deref_ty: Ty<'tcx>| {
- if self
- .lookup_op_method(
- lhs_deref_ty,
- Some(rhs_ty),
- Some(rhs_expr),
- Op::Binary(op, is_assign),
- expected,
- )
- .is_ok()
- {
- if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
- let msg = &format!(
- "`{}{}` can be used on `{}`, you can dereference `{}`",
- op.node.as_str(),
- match is_assign {
- IsAssign::Yes => "=",
- IsAssign::No => "",
- },
- lhs_deref_ty.peel_refs(),
- lstring,
- );
- err.span_suggestion_verbose(
- lhs_expr.span.shrink_to_lo(),
- msg,
- "*",
- rustc_errors::Applicability::MachineApplicable,
- );
- }
- }
- };
-
- // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
- // `a += b` => `*a += b` if a is a mut ref.
- if is_assign == IsAssign::Yes
- && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
- suggest_deref_binop(lhs_deref_ty);
- } else if is_assign == IsAssign::No
- && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() {
- if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) {
- suggest_deref_binop(*lhs_deref_ty);
- }
- }
- if let Some(missing_trait) = missing_trait {
- let mut visitor = TypeParamVisitor(vec![]);
- visitor.visit_ty(lhs_ty);
-
- if op.node == hir::BinOpKind::Add
- && self.check_str_addition(
- lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op,
- )
- {
- // This has nothing here because it means we did string
- // concatenation (e.g., "Hello " + "World!"). This means
- // we don't want the note in the else clause to be emitted
- } else if let [ty] = &visitor.0[..] {
- // Look for a TraitPredicate in the Fulfillment errors,
- // and use it to generate a suggestion.
- //
- // Note that lookup_op_method must be called again but
- // with a specific rhs_ty instead of a placeholder so
- // the resulting predicate generates a more specific
- // suggestion for the user.
- let errors = self
- .lookup_op_method(
- lhs_ty,
- Some(rhs_ty),
- Some(rhs_expr),
- Op::Binary(op, is_assign),
- expected,
- )
- .unwrap_err();
- if !errors.is_empty() {
- for error in errors {
- if let Some(trait_pred) =
- error.obligation.predicate.to_opt_poly_trait_pred()
- {
- let proj_pred = match error.obligation.cause.code() {
- ObligationCauseCode::BinOp {
- output_pred: Some(output_pred),
- ..
- } if use_output => {
- output_pred.to_opt_poly_projection_pred()
- }
- _ => None,
- };
-
- self.suggest_restricting_param_bound(
- &mut err,
- trait_pred,
- proj_pred,
- self.body_id,
- );
- }
- }
- } else if *ty != lhs_ty {
- // When we know that a missing bound is responsible, we don't show
- // this note as it is redundant.
- err.note(&format!(
- "the trait `{missing_trait}` is not implemented for `{lhs_ty}`"
- ));
- }
- }
- }
- err.emit();
- self.tcx.ty_error()
- }
- };
-
- (lhs_ty, rhs_ty, return_ty)
- }
-
- /// If one of the types is an uncalled function and calling it would yield the other type,
- /// suggest calling the function. Returns `true` if suggestion would apply (even if not given).
- fn add_type_neq_err_label(
- &self,
- err: &mut Diagnostic,
- span: Span,
- ty: Ty<'tcx>,
- other_ty: Ty<'tcx>,
- other_expr: &'tcx hir::Expr<'tcx>,
- op: hir::BinOp,
- is_assign: IsAssign,
- expected: Expectation<'tcx>,
- ) -> bool /* did we suggest to call a function because of missing parentheses? */ {
- err.span_label(span, ty.to_string());
- if let FnDef(def_id, _) = *ty.kind() {
- if !self.tcx.has_typeck_results(def_id) {
- return false;
- }
- // FIXME: Instead of exiting early when encountering bound vars in
- // the function signature, consider keeping the binder here and
- // propagating it downwards.
- let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() else {
- return false;
- };
-
- let other_ty = if let FnDef(def_id, _) = *other_ty.kind() {
- if !self.tcx.has_typeck_results(def_id) {
- return false;
- }
- // We're emitting a suggestion, so we can just ignore regions
- self.tcx.fn_sig(def_id).skip_binder().output()
- } else {
- other_ty
- };
-
- if self
- .lookup_op_method(
- fn_sig.output(),
- Some(other_ty),
- Some(other_expr),
- Op::Binary(op, is_assign),
- expected,
- )
- .is_ok()
- {
- let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() {
- ("( /* arguments */ )", Applicability::HasPlaceholders)
- } else {
- ("()", Applicability::MaybeIncorrect)
- };
-
- err.span_suggestion_verbose(
- span.shrink_to_hi(),
- "you might have forgotten to call this function",
- variable_snippet,
- applicability,
- );
- return true;
- }
- }
- false
- }
-
- /// Provide actionable suggestions when trying to add two strings with incorrect types,
- /// like `&str + &str`, `String + String` and `&str + &String`.
- ///
- /// If this function returns `true` it means a note was printed, so we don't need
- /// to print the normal "implementation of `std::ops::Add` might be missing" note
- fn check_str_addition(
- &self,
- lhs_expr: &'tcx hir::Expr<'tcx>,
- rhs_expr: &'tcx hir::Expr<'tcx>,
- lhs_ty: Ty<'tcx>,
- rhs_ty: Ty<'tcx>,
- err: &mut Diagnostic,
- is_assign: IsAssign,
- op: hir::BinOp,
- ) -> bool {
- let str_concat_note = "string concatenation requires an owned `String` on the left";
- let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
- let to_owned_msg = "create an owned `String` from a string reference";
-
- let is_std_string = |ty: Ty<'tcx>| {
- ty.ty_adt_def()
- .map_or(false, |ty_def| self.tcx.is_diagnostic_item(sym::String, ty_def.did()))
- };
-
- match (lhs_ty.kind(), rhs_ty.kind()) {
- (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str
- if (*l_ty.kind() == Str || is_std_string(l_ty))
- && (*r_ty.kind() == Str
- || is_std_string(r_ty)
- || matches!(
- r_ty.kind(), Ref(_, inner_ty, _) if *inner_ty.kind() == Str
- )) =>
- {
- if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str`
- err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings");
- err.note(str_concat_note);
- if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
- err.span_suggestion_verbose(
- lhs_expr.span.until(lhs_inner_expr.span),
- rm_borrow_msg,
- "",
- Applicability::MachineApplicable
- );
- } else {
- err.span_suggestion_verbose(
- lhs_expr.span.shrink_to_hi(),
- to_owned_msg,
- ".to_owned()",
- Applicability::MachineApplicable
- );
- }
- }
- true
- }
- (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String`
- if (*l_ty.kind() == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
- {
- err.span_label(
- op.span,
- "`+` cannot be used to concatenate a `&str` with a `String`",
- );
- match is_assign {
- IsAssign::No => {
- let sugg_msg;
- let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
- sugg_msg = "remove the borrow on the left and add one on the right";
- (lhs_expr.span.until(lhs_inner_expr.span), "".to_owned())
- } else {
- sugg_msg = "create an owned `String` on the left and add a borrow on the right";
- (lhs_expr.span.shrink_to_hi(), ".to_owned()".to_owned())
- };
- let suggestions = vec![
- lhs_sugg,
- (rhs_expr.span.shrink_to_lo(), "&".to_owned()),
- ];
- err.multipart_suggestion_verbose(
- sugg_msg,
- suggestions,
- Applicability::MachineApplicable,
- );
- }
- IsAssign::Yes => {
- err.note(str_concat_note);
- }
- }
- true
- }
- _ => false,
- }
- }
-
- pub fn check_user_unop(
- &self,
- ex: &'tcx hir::Expr<'tcx>,
- operand_ty: Ty<'tcx>,
- op: hir::UnOp,
- expected: Expectation<'tcx>,
- ) -> Ty<'tcx> {
- assert!(op.is_by_value());
- match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span), expected) {
- Ok(method) => {
- self.write_method_call(ex.hir_id, method);
- method.sig.output()
- }
- Err(errors) => {
- let actual = self.resolve_vars_if_possible(operand_ty);
- if !actual.references_error() {
- let mut err = struct_span_err!(
- self.tcx.sess,
- ex.span,
- E0600,
- "cannot apply unary operator `{}` to type `{}`",
- op.as_str(),
- actual
- );
- err.span_label(
- ex.span,
- format!("cannot apply unary operator `{}`", op.as_str()),
- );
-
- let mut visitor = TypeParamVisitor(vec![]);
- visitor.visit_ty(operand_ty);
- if let [_] = &visitor.0[..] && let ty::Param(_) = *operand_ty.kind() {
- let predicates = errors
- .iter()
- .filter_map(|error| {
- error.obligation.predicate.to_opt_poly_trait_pred()
- });
- for pred in predicates {
- self.suggest_restricting_param_bound(
- &mut err,
- pred,
- None,
- self.body_id,
- );
- }
- }
-
- let sp = self.tcx.sess.source_map().start_point(ex.span);
- if let Some(sp) =
- self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp)
- {
- // If the previous expression was a block expression, suggest parentheses
- // (turning this into a binary subtraction operation instead.)
- // for example, `{2} - 2` -> `({2}) - 2` (see src\test\ui\parser\expr-as-stmt.rs)
- self.tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp);
- } else {
- match actual.kind() {
- Uint(_) if op == hir::UnOp::Neg => {
- err.note("unsigned values cannot be negated");
-
- if let hir::ExprKind::Unary(
- _,
- hir::Expr {
- kind:
- hir::ExprKind::Lit(Spanned {
- node: ast::LitKind::Int(1, _),
- ..
- }),
- ..
- },
- ) = ex.kind
- {
- err.span_suggestion(
- ex.span,
- &format!(
- "you may have meant the maximum value of `{actual}`",
- ),
- format!("{actual}::MAX"),
- Applicability::MaybeIncorrect,
- );
- }
- }
- Str | Never | Char | Tuple(_) | Array(_, _) => {}
- Ref(_, lty, _) if *lty.kind() == Str => {}
- _ => {
- self.note_unmet_impls_on_type(&mut err, errors);
- }
- }
- }
- err.emit();
- }
- self.tcx.ty_error()
- }
- }
- }
-
- fn lookup_op_method(
- &self,
- lhs_ty: Ty<'tcx>,
- other_ty: Option<Ty<'tcx>>,
- other_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
- op: Op,
- expected: Expectation<'tcx>,
- ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
- let lang = self.tcx.lang_items();
-
- let span = match op {
- Op::Binary(op, _) => op.span,
- Op::Unary(_, span) => span,
- };
- let (opname, trait_did) = if let Op::Binary(op, IsAssign::Yes) = op {
- match op.node {
- hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()),
- hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()),
- hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()),
- hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()),
- hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()),
- hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()),
- hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()),
- hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()),
- hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()),
- hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()),
- hir::BinOpKind::Lt
- | hir::BinOpKind::Le
- | hir::BinOpKind::Ge
- | hir::BinOpKind::Gt
- | hir::BinOpKind::Eq
- | hir::BinOpKind::Ne
- | hir::BinOpKind::And
- | hir::BinOpKind::Or => {
- span_bug!(span, "impossible assignment operation: {}=", op.node.as_str())
- }
- }
- } else if let Op::Binary(op, IsAssign::No) = op {
- match op.node {
- hir::BinOpKind::Add => (sym::add, lang.add_trait()),
- hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
- hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()),
- hir::BinOpKind::Div => (sym::div, lang.div_trait()),
- hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()),
- hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()),
- hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()),
- hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()),
- hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()),
- hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()),
- hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()),
- hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()),
- hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()),
- hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()),
- hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()),
- hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
- hir::BinOpKind::And | hir::BinOpKind::Or => {
- span_bug!(span, "&& and || are not overloadable")
- }
- }
- } else if let Op::Unary(hir::UnOp::Not, _) = op {
- (sym::not, lang.not_trait())
- } else if let Op::Unary(hir::UnOp::Neg, _) = op {
- (sym::neg, lang.neg_trait())
- } else {
- bug!("lookup_op_method: op not supported: {:?}", op)
- };
-
- debug!(
- "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
- lhs_ty, op, opname, trait_did
- );
-
- // Catches cases like #83893, where a lang item is declared with the
- // wrong number of generic arguments. Should have yielded an error
- // elsewhere by now, but we have to catch it here so that we do not
- // index `other_tys` out of bounds (if the lang item has too many
- // generic arguments, `other_tys` is too short).
- if !has_expected_num_generic_args(
- self.tcx,
- trait_did,
- match op {
- // Binary ops have a generic right-hand side, unary ops don't
- Op::Binary(..) => 1,
- Op::Unary(..) => 0,
- },
- ) {
- return Err(vec![]);
- }
-
- let opname = Ident::with_dummy_span(opname);
- let method = trait_did.and_then(|trait_did| {
- self.lookup_op_method_in_trait(
- span,
- opname,
- trait_did,
- lhs_ty,
- other_ty,
- other_ty_expr,
- expected,
- )
- });
-
- match (method, trait_did) {
- (Some(ok), _) => {
- let method = self.register_infer_ok_obligations(ok);
- self.select_obligations_where_possible(false, |_| {});
- Ok(method)
- }
- (None, None) => Err(vec![]),
- (None, Some(trait_did)) => {
- let (obligation, _) = self.obligation_for_op_method(
- span,
- trait_did,
- lhs_ty,
- other_ty,
- other_ty_expr,
- expected,
- );
- let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx);
- fulfill.register_predicate_obligation(self, obligation);
- Err(fulfill.select_where_possible(&self.infcx))
- }
- }
- }
-}
-
-// Binary operator categories. These categories summarize the behavior
-// with respect to the builtin operations supported.
-enum BinOpCategory {
- /// &&, || -- cannot be overridden
- Shortcircuit,
-
- /// <<, >> -- when shifting a single integer, rhs can be any
- /// integer type. For simd, types must match.
- Shift,
-
- /// +, -, etc -- takes equal types, produces same type as input,
- /// applicable to ints/floats/simd
- Math,
-
- /// &, |, ^ -- takes equal types, produces same type as input,
- /// applicable to ints/floats/simd/bool
- Bitwise,
-
- /// ==, !=, etc -- takes equal types, produces bools, except for simd,
- /// which produce the input type
- Comparison,
-}
-
-impl BinOpCategory {
- fn from(op: hir::BinOp) -> BinOpCategory {
- match op.node {
- hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift,
-
- hir::BinOpKind::Add
- | hir::BinOpKind::Sub
- | hir::BinOpKind::Mul
- | hir::BinOpKind::Div
- | hir::BinOpKind::Rem => BinOpCategory::Math,
-
- hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => {
- BinOpCategory::Bitwise
- }
-
- hir::BinOpKind::Eq
- | hir::BinOpKind::Ne
- | hir::BinOpKind::Lt
- | hir::BinOpKind::Le
- | hir::BinOpKind::Ge
- | hir::BinOpKind::Gt => BinOpCategory::Comparison,
-
- hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit,
- }
- }
-}
-
-/// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
-#[derive(Clone, Copy, Debug, PartialEq)]
-enum IsAssign {
- No,
- Yes,
-}
-
-#[derive(Clone, Copy, Debug)]
-enum Op {
- Binary(hir::BinOp, IsAssign),
- Unary(hir::UnOp, Span),
-}
-
-/// Dereferences a single level of immutable referencing.
-fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> {
- match ty.kind() {
- ty::Ref(_, ty, hir::Mutability::Not) => *ty,
- _ => ty,
- }
-}
-
-/// Returns `true` if this is a built-in arithmetic operation (e.g., u32
-/// + u32, i16x4 == i16x4) and false if these types would have to be
-/// overloaded to be legal. There are two reasons that we distinguish
-/// builtin operations from overloaded ones (vs trying to drive
-/// everything uniformly through the trait system and intrinsics or
-/// something like that):
-///
-/// 1. Builtin operations can trivially be evaluated in constants.
-/// 2. For comparison operators applied to SIMD types the result is
-/// not of type `bool`. For example, `i16x4 == i16x4` yields a
-/// type like `i16x4`. This means that the overloaded trait
-/// `PartialEq` is not applicable.
-///
-/// Reason #2 is the killer. I tried for a while to always use
-/// overloaded logic and just check the types in constants/codegen after
-/// the fact, and it worked fine, except for SIMD types. -nmatsakis
-fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool {
- // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
- // (See https://github.com/rust-lang/rust/issues/57447.)
- let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
-
- match BinOpCategory::from(op) {
- BinOpCategory::Shortcircuit => true,
-
- BinOpCategory::Shift => {
- lhs.references_error()
- || rhs.references_error()
- || lhs.is_integral() && rhs.is_integral()
- }
-
- BinOpCategory::Math => {
- lhs.references_error()
- || rhs.references_error()
- || lhs.is_integral() && rhs.is_integral()
- || lhs.is_floating_point() && rhs.is_floating_point()
- }
-
- BinOpCategory::Bitwise => {
- lhs.references_error()
- || rhs.references_error()
- || lhs.is_integral() && rhs.is_integral()
- || lhs.is_floating_point() && rhs.is_floating_point()
- || lhs.is_bool() && rhs.is_bool()
- }
-
- BinOpCategory::Comparison => {
- lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
- }
- }
-}
-
-struct TypeParamVisitor<'tcx>(Vec<Ty<'tcx>>);
-
-impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> {
- fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
- if let ty::Param(_) = ty.kind() {
- self.0.push(ty);
- }
- ty.super_visit_with(self)
- }
-}
-
-struct TypeParamEraser<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, Span);
-
-impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> {
- fn tcx(&self) -> TyCtxt<'tcx> {
- self.0.tcx
- }
-
- fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
- match ty.kind() {
- ty::Param(_) => self.0.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::MiscVariable,
- span: self.1,
- }),
- _ => ty.super_fold_with(self),
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs
deleted file mode 100644
index d49a6138f..000000000
--- a/compiler/rustc_typeck/src/check/regionck.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use crate::outlives::outlives_bounds::InferCtxtExt as _;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_hir as hir;
-use rustc_infer::infer::outlives::env::OutlivesEnvironment;
-use rustc_infer::infer::InferCtxt;
-use rustc_middle::ty::Ty;
-
-pub(crate) trait OutlivesEnvironmentExt<'tcx> {
- fn add_implied_bounds(
- &mut self,
- infcx: &InferCtxt<'_, 'tcx>,
- fn_sig_tys: FxHashSet<Ty<'tcx>>,
- body_id: hir::HirId,
- );
-}
-
-impl<'tcx> OutlivesEnvironmentExt<'tcx> for OutlivesEnvironment<'tcx> {
- /// This method adds "implied bounds" into the outlives environment.
- /// Implied bounds are outlives relationships that we can deduce
- /// on the basis that certain types must be well-formed -- these are
- /// either the types that appear in the function signature or else
- /// the input types to an impl. For example, if you have a function
- /// like
- ///
- /// ```
- /// fn foo<'a, 'b, T>(x: &'a &'b [T]) { }
- /// ```
- ///
- /// we can assume in the caller's body that `'b: 'a` and that `T:
- /// 'b` (and hence, transitively, that `T: 'a`). This method would
- /// add those assumptions into the outlives-environment.
- ///
- /// Tests: `src/test/ui/regions/regions-free-region-ordering-*.rs`
- #[instrument(level = "debug", skip(self, infcx))]
- fn add_implied_bounds<'a>(
- &mut self,
- infcx: &InferCtxt<'a, 'tcx>,
- fn_sig_tys: FxHashSet<Ty<'tcx>>,
- body_id: hir::HirId,
- ) {
- for ty in fn_sig_tys {
- let ty = infcx.resolve_vars_if_possible(ty);
- let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty);
- self.add_outlives_bounds(Some(infcx), implied_bounds)
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/check_unused.rs b/compiler/rustc_typeck/src/check_unused.rs
deleted file mode 100644
index 4a3cfa1ca..000000000
--- a/compiler/rustc_typeck/src/check_unused.rs
+++ /dev/null
@@ -1,196 +0,0 @@
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_hir::def::DefKind;
-use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_middle::ty::TyCtxt;
-use rustc_session::lint;
-use rustc_span::{Span, Symbol};
-
-pub fn check_crate(tcx: TyCtxt<'_>) {
- let mut used_trait_imports: FxHashSet<LocalDefId> = FxHashSet::default();
-
- for item_def_id in tcx.hir().body_owners() {
- let imports = tcx.used_trait_imports(item_def_id);
- debug!("GatherVisitor: item_def_id={:?} with imports {:#?}", item_def_id, imports);
- used_trait_imports.extend(imports.iter());
- }
-
- for &id in tcx.maybe_unused_trait_imports(()) {
- debug_assert_eq!(tcx.def_kind(id), DefKind::Use);
- if tcx.visibility(id).is_public() {
- continue;
- }
- if used_trait_imports.contains(&id) {
- continue;
- }
- let item = tcx.hir().expect_item(id);
- if item.span.is_dummy() {
- continue;
- }
- let hir::ItemKind::Use(path, _) = item.kind else { unreachable!() };
- tcx.struct_span_lint_hir(lint::builtin::UNUSED_IMPORTS, item.hir_id(), path.span, |lint| {
- let msg = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) {
- format!("unused import: `{}`", snippet)
- } else {
- "unused import".to_owned()
- };
- lint.build(&msg).emit();
- });
- }
-
- unused_crates_lint(tcx);
-}
-
-fn unused_crates_lint(tcx: TyCtxt<'_>) {
- let lint = lint::builtin::UNUSED_EXTERN_CRATES;
-
- // Collect first the crates that are completely unused. These we
- // can always suggest removing (no matter which edition we are
- // in).
- let unused_extern_crates: FxHashMap<LocalDefId, Span> = tcx
- .maybe_unused_extern_crates(())
- .iter()
- .filter(|&&(def_id, _)| {
- // The `def_id` here actually was calculated during resolution (at least
- // at the time of this writing) and is being shipped to us via a side
- // channel of the tcx. There may have been extra expansion phases,
- // however, which ended up removing the `def_id` *after* expansion.
- //
- // As a result we need to verify that `def_id` is indeed still valid for
- // our AST and actually present in the HIR map. If it's not there then
- // there's safely nothing to warn about, and otherwise we carry on with
- // our execution.
- //
- // Note that if we carry through to the `extern_mod_stmt_cnum` query
- // below it'll cause a panic because `def_id` is actually bogus at this
- // point in time otherwise.
- if tcx.hir().find(tcx.hir().local_def_id_to_hir_id(def_id)).is_none() {
- return false;
- }
- true
- })
- .filter(|&&(def_id, _)| {
- tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| {
- !tcx.is_compiler_builtins(cnum)
- && !tcx.is_panic_runtime(cnum)
- && !tcx.has_global_allocator(cnum)
- && !tcx.has_panic_handler(cnum)
- })
- })
- .cloned()
- .collect();
-
- // Collect all the extern crates (in a reliable order).
- let mut crates_to_lint = vec![];
-
- for id in tcx.hir().items() {
- if matches!(tcx.def_kind(id.def_id), DefKind::ExternCrate) {
- let item = tcx.hir().item(id);
- if let hir::ItemKind::ExternCrate(orig_name) = item.kind {
- crates_to_lint.push(ExternCrateToLint {
- def_id: item.def_id.to_def_id(),
- span: item.span,
- orig_name,
- warn_if_unused: !item.ident.as_str().starts_with('_'),
- });
- }
- }
- }
-
- let extern_prelude = &tcx.resolutions(()).extern_prelude;
-
- for extern_crate in &crates_to_lint {
- let def_id = extern_crate.def_id.expect_local();
- let item = tcx.hir().expect_item(def_id);
-
- // If the crate is fully unused, we suggest removing it altogether.
- // We do this in any edition.
- if extern_crate.warn_if_unused {
- if let Some(&span) = unused_extern_crates.get(&def_id) {
- let id = tcx.hir().local_def_id_to_hir_id(def_id);
- tcx.struct_span_lint_hir(lint, id, span, |lint| {
- // Removal suggestion span needs to include attributes (Issue #54400)
- let span_with_attrs = tcx
- .hir()
- .attrs(id)
- .iter()
- .map(|attr| attr.span)
- .fold(span, |acc, attr_span| acc.to(attr_span));
-
- lint.build("unused extern crate")
- .span_suggestion_short(
- span_with_attrs,
- "remove it",
- "",
- Applicability::MachineApplicable,
- )
- .emit();
- });
- continue;
- }
- }
-
- // If we are not in Rust 2018 edition, then we don't make any further
- // suggestions.
- if !tcx.sess.rust_2018() {
- continue;
- }
-
- // If the extern crate isn't in the extern prelude,
- // there is no way it can be written as a `use`.
- let orig_name = extern_crate.orig_name.unwrap_or(item.ident.name);
- if !extern_prelude.get(&orig_name).map_or(false, |from_item| !from_item) {
- continue;
- }
-
- // If the extern crate is renamed, then we cannot suggest replacing it with a use as this
- // would not insert the new name into the prelude, where other imports in the crate may be
- // expecting it.
- if extern_crate.orig_name.is_some() {
- continue;
- }
-
- let id = tcx.hir().local_def_id_to_hir_id(def_id);
- // If the extern crate has any attributes, they may have funky
- // semantics we can't faithfully represent using `use` (most
- // notably `#[macro_use]`). Ignore it.
- if !tcx.hir().attrs(id).is_empty() {
- continue;
- }
- tcx.struct_span_lint_hir(lint, id, extern_crate.span, |lint| {
- // Otherwise, we can convert it into a `use` of some kind.
- let base_replacement = match extern_crate.orig_name {
- Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name),
- None => format!("use {};", item.ident.name),
- };
- let vis = tcx.sess.source_map().span_to_snippet(item.vis_span).unwrap_or_default();
- let add_vis = |to| if vis.is_empty() { to } else { format!("{} {}", vis, to) };
- lint.build("`extern crate` is not idiomatic in the new edition")
- .span_suggestion_short(
- extern_crate.span,
- &format!("convert it to a `{}`", add_vis("use".to_string())),
- add_vis(base_replacement),
- Applicability::MachineApplicable,
- )
- .emit();
- })
- }
-}
-
-struct ExternCrateToLint {
- /// `DefId` of the extern crate
- def_id: DefId,
-
- /// span from the item
- span: Span,
-
- /// if `Some`, then this is renamed (`extern crate orig_name as
- /// crate_name`), and -- perhaps surprisingly -- this stores the
- /// *original* name (`item.name` will contain the new name)
- orig_name: Option<Symbol>,
-
- /// if `false`, the original name started with `_`, so we shouldn't lint
- /// about it going unused (but we should still emit idiom lints).
- warn_if_unused: bool,
-}
diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs
deleted file mode 100644
index 50946cc1d..000000000
--- a/compiler/rustc_typeck/src/coherence/builtin.rs
+++ /dev/null
@@ -1,603 +0,0 @@
-//! Check properties that are required by built-in traits and set
-//! up data structures required by type-checking/codegen.
-
-use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem};
-use rustc_errors::{struct_span_err, MultiSpan};
-use rustc_hir as hir;
-use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::lang_items::LangItem;
-use rustc_hir::ItemKind;
-use rustc_infer::infer;
-use rustc_infer::infer::outlives::env::OutlivesEnvironment;
-use rustc_infer::infer::TyCtxtInferExt;
-use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
-use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitable};
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
-use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError};
-use rustc_trait_selection::traits::predicate_for_trait_def;
-use rustc_trait_selection::traits::{self, ObligationCause, TraitEngine, TraitEngineExt};
-use std::collections::BTreeMap;
-
-pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) {
- let lang_items = tcx.lang_items();
- Checker { tcx, trait_def_id }
- .check(lang_items.drop_trait(), visit_implementation_of_drop)
- .check(lang_items.copy_trait(), visit_implementation_of_copy)
- .check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)
- .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn);
-}
-
-struct Checker<'tcx> {
- tcx: TyCtxt<'tcx>,
- trait_def_id: DefId,
-}
-
-impl<'tcx> Checker<'tcx> {
- fn check<F>(&self, trait_def_id: Option<DefId>, mut f: F) -> &Self
- where
- F: FnMut(TyCtxt<'tcx>, LocalDefId),
- {
- if Some(self.trait_def_id) == trait_def_id {
- for &impl_def_id in self.tcx.hir().trait_impls(self.trait_def_id) {
- f(self.tcx, impl_def_id);
- }
- }
- self
- }
-}
-
-fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
- // Destructors only work on nominal types.
- if let ty::Adt(..) | ty::Error(_) = tcx.type_of(impl_did).kind() {
- return;
- }
-
- let sp = match tcx.hir().expect_item(impl_did).kind {
- ItemKind::Impl(ref impl_) => impl_.self_ty.span,
- _ => bug!("expected Drop impl item"),
- };
-
- tcx.sess.emit_err(DropImplOnWrongItem { span: sp });
-}
-
-fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
- debug!("visit_implementation_of_copy: impl_did={:?}", impl_did);
-
- let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
-
- let self_type = tcx.type_of(impl_did);
- debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type);
-
- let span = tcx.hir().span(impl_hir_id);
- let param_env = tcx.param_env(impl_did);
- assert!(!self_type.has_escaping_bound_vars());
-
- debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type);
-
- let cause = traits::ObligationCause::misc(span, impl_hir_id);
- match can_type_implement_copy(tcx, param_env, self_type, cause) {
- Ok(()) => {}
- Err(CopyImplementationError::InfrigingFields(fields)) => {
- let item = tcx.hir().expect_item(impl_did);
- let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(ref tr), .. }) = item.kind {
- tr.path.span
- } else {
- span
- };
-
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0204,
- "the trait `Copy` may not be implemented for this type"
- );
-
- // We'll try to suggest constraining type parameters to fulfill the requirements of
- // their `Copy` implementation.
- let mut errors: BTreeMap<_, Vec<_>> = Default::default();
- let mut bounds = vec![];
-
- for (field, ty) in fields {
- let field_span = tcx.def_span(field.did);
- let field_ty_span = match tcx.hir().get_if_local(field.did) {
- Some(hir::Node::Field(field_def)) => field_def.ty.span,
- _ => field_span,
- };
- err.span_label(field_span, "this field does not implement `Copy`");
- // Spin up a new FulfillmentContext, so we can get the _precise_ reason
- // why this field does not implement Copy. This is useful because sometimes
- // it is not immediately clear why Copy is not implemented for a field, since
- // all we point at is the field itself.
- tcx.infer_ctxt().ignoring_regions().enter(|infcx| {
- let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
- fulfill_cx.register_bound(
- &infcx,
- param_env,
- ty,
- tcx.lang_items().copy_trait().unwrap(),
- traits::ObligationCause::dummy_with_span(field_ty_span),
- );
- for error in fulfill_cx.select_all_or_error(&infcx) {
- let error_predicate = error.obligation.predicate;
- // Only note if it's not the root obligation, otherwise it's trivial and
- // should be self-explanatory (i.e. a field literally doesn't implement Copy).
-
- // FIXME: This error could be more descriptive, especially if the error_predicate
- // contains a foreign type or if it's a deeply nested type...
- if error_predicate != error.root_obligation.predicate {
- errors
- .entry((ty.to_string(), error_predicate.to_string()))
- .or_default()
- .push(error.obligation.cause.span);
- }
- if let ty::PredicateKind::Trait(ty::TraitPredicate {
- trait_ref,
- polarity: ty::ImplPolarity::Positive,
- ..
- }) = error_predicate.kind().skip_binder()
- {
- let ty = trait_ref.self_ty();
- if let ty::Param(_) = ty.kind() {
- bounds.push((
- format!("{ty}"),
- trait_ref.print_only_trait_path().to_string(),
- Some(trait_ref.def_id),
- ));
- }
- }
- }
- });
- }
- for ((ty, error_predicate), spans) in errors {
- let span: MultiSpan = spans.into();
- err.span_note(
- span,
- &format!("the `Copy` impl for `{}` requires that `{}`", ty, error_predicate),
- );
- }
- suggest_constraining_type_params(
- tcx,
- tcx.hir().get_generics(impl_did).expect("impls always have generics"),
- &mut err,
- bounds.iter().map(|(param, constraint, def_id)| {
- (param.as_str(), constraint.as_str(), *def_id)
- }),
- );
- err.emit();
- }
- Err(CopyImplementationError::NotAnAdt) => {
- let item = tcx.hir().expect_item(impl_did);
- let span =
- if let ItemKind::Impl(ref impl_) = item.kind { impl_.self_ty.span } else { span };
-
- tcx.sess.emit_err(CopyImplOnNonAdt { span });
- }
- Err(CopyImplementationError::HasDestructor) => {
- tcx.sess.emit_err(CopyImplOnTypeWithDtor { span });
- }
- }
-}
-
-fn visit_implementation_of_coerce_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) {
- debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did);
-
- // Just compute this for the side-effects, in particular reporting
- // errors; other parts of the code may demand it for the info of
- // course.
- let span = tcx.def_span(impl_did);
- tcx.at(span).coerce_unsized_info(impl_did);
-}
-
-fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) {
- debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did);
-
- let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
- let span = tcx.hir().span(impl_hir_id);
-
- let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span));
-
- let source = tcx.type_of(impl_did);
- assert!(!source.has_escaping_bound_vars());
- let target = {
- let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
- assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait);
-
- trait_ref.substs.type_at(1)
- };
-
- debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target);
-
- let param_env = tcx.param_env(impl_did);
-
- let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg);
-
- tcx.infer_ctxt().enter(|infcx| {
- let cause = ObligationCause::misc(span, impl_hir_id);
-
- use rustc_type_ir::sty::TyKind::*;
- match (source.kind(), target.kind()) {
- (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))
- if infcx.at(&cause, param_env).eq(r_a, *r_b).is_ok() && mutbl_a == *mutbl_b => {}
- (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (),
- (&Adt(def_a, substs_a), &Adt(def_b, substs_b))
- if def_a.is_struct() && def_b.is_struct() =>
- {
- if def_a != def_b {
- let source_path = tcx.def_path_str(def_a.did());
- let target_path = tcx.def_path_str(def_b.did());
-
- create_err(&format!(
- "the trait `DispatchFromDyn` may only be implemented \
- for a coercion between structures with the same \
- definition; expected `{}`, found `{}`",
- source_path, target_path,
- ))
- .emit();
-
- return;
- }
-
- if def_a.repr().c() || def_a.repr().packed() {
- create_err(
- "structs implementing `DispatchFromDyn` may not have \
- `#[repr(packed)]` or `#[repr(C)]`",
- )
- .emit();
- }
-
- let fields = &def_a.non_enum_variant().fields;
-
- let coerced_fields = fields
- .iter()
- .filter(|field| {
- let ty_a = field.ty(tcx, substs_a);
- let ty_b = field.ty(tcx, substs_b);
-
- if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) {
- if layout.is_zst() && layout.align.abi.bytes() == 1 {
- // ignore ZST fields with alignment of 1 byte
- return false;
- }
- }
-
- if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) {
- if ok.obligations.is_empty() {
- create_err(
- "the trait `DispatchFromDyn` may only be implemented \
- for structs containing the field being coerced, \
- ZST fields with 1 byte alignment, and nothing else",
- )
- .note(&format!(
- "extra field `{}` of type `{}` is not allowed",
- field.name, ty_a,
- ))
- .emit();
-
- return false;
- }
- }
-
- return true;
- })
- .collect::<Vec<_>>();
-
- if coerced_fields.is_empty() {
- create_err(
- "the trait `DispatchFromDyn` may only be implemented \
- for a coercion between structures with a single field \
- being coerced, none found",
- )
- .emit();
- } else if coerced_fields.len() > 1 {
- create_err(
- "implementing the `DispatchFromDyn` trait requires multiple coercions",
- )
- .note(
- "the trait `DispatchFromDyn` may only be implemented \
- for a coercion between structures with a single field \
- being coerced",
- )
- .note(&format!(
- "currently, {} fields need coercions: {}",
- coerced_fields.len(),
- coerced_fields
- .iter()
- .map(|field| {
- format!(
- "`{}` (`{}` to `{}`)",
- field.name,
- field.ty(tcx, substs_a),
- field.ty(tcx, substs_b),
- )
- })
- .collect::<Vec<_>>()
- .join(", ")
- ))
- .emit();
- } else {
- let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
-
- for field in coerced_fields {
- let predicate = predicate_for_trait_def(
- tcx,
- param_env,
- cause.clone(),
- dispatch_from_dyn_trait,
- 0,
- field.ty(tcx, substs_a),
- &[field.ty(tcx, substs_b).into()],
- );
-
- fulfill_cx.register_predicate_obligation(&infcx, predicate);
- }
-
- // Check that all transitive obligations are satisfied.
- let errors = fulfill_cx.select_all_or_error(&infcx);
- if !errors.is_empty() {
- infcx.report_fulfillment_errors(&errors, None, false);
- }
-
- // Finally, resolve all regions.
- let outlives_env = OutlivesEnvironment::new(param_env);
- infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env);
- }
- }
- _ => {
- create_err(
- "the trait `DispatchFromDyn` may only be implemented \
- for a coercion between structures",
- )
- .emit();
- }
- }
- })
-}
-
-pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo {
- debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did);
-
- // this provider should only get invoked for local def-ids
- let impl_did = impl_did.expect_local();
- let span = tcx.def_span(impl_did);
-
- let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span));
-
- let unsize_trait = tcx.lang_items().require(LangItem::Unsize).unwrap_or_else(|err| {
- tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
- });
-
- let source = tcx.type_of(impl_did);
- let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
- assert_eq!(trait_ref.def_id, coerce_unsized_trait);
- let target = trait_ref.substs.type_at(1);
- debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target);
-
- let param_env = tcx.param_env(impl_did);
- assert!(!source.has_escaping_bound_vars());
-
- let err_info = CoerceUnsizedInfo { custom_kind: None };
-
- debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target);
-
- tcx.infer_ctxt().enter(|infcx| {
- let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
- let cause = ObligationCause::misc(span, impl_hir_id);
- let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
- mt_b: ty::TypeAndMut<'tcx>,
- mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| {
- if (mt_a.mutbl, mt_b.mutbl) == (hir::Mutability::Not, hir::Mutability::Mut) {
- infcx
- .report_mismatched_types(
- &cause,
- mk_ptr(mt_b.ty),
- target,
- ty::error::TypeError::Mutability,
- )
- .emit();
- }
- (mt_a.ty, mt_b.ty, unsize_trait, None)
- };
- let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) {
- (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
- infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
- let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
- let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b };
- check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
- }
-
- (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => {
- let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
- check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
- }
-
- (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => {
- check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
- }
-
- (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b))
- if def_a.is_struct() && def_b.is_struct() =>
- {
- if def_a != def_b {
- let source_path = tcx.def_path_str(def_a.did());
- let target_path = tcx.def_path_str(def_b.did());
- struct_span_err!(
- tcx.sess,
- span,
- E0377,
- "the trait `CoerceUnsized` may only be implemented \
- for a coercion between structures with the same \
- definition; expected `{}`, found `{}`",
- source_path,
- target_path
- )
- .emit();
- return err_info;
- }
-
- // Here we are considering a case of converting
- // `S<P0...Pn>` to S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`,
- // which acts like a pointer to `U`, but carries along some extra data of type `T`:
- //
- // struct Foo<T, U> {
- // extra: T,
- // ptr: *mut U,
- // }
- //
- // We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized
- // to `Foo<T, [i32]>`. That impl would look like:
- //
- // impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {}
- //
- // Here `U = [i32; 3]` and `V = [i32]`. At runtime,
- // when this coercion occurs, we would be changing the
- // field `ptr` from a thin pointer of type `*mut [i32;
- // 3]` to a fat pointer of type `*mut [i32]` (with
- // extra data `3`). **The purpose of this check is to
- // make sure that we know how to do this conversion.**
- //
- // To check if this impl is legal, we would walk down
- // the fields of `Foo` and consider their types with
- // both substitutes. We are looking to find that
- // exactly one (non-phantom) field has changed its
- // type, which we will expect to be the pointer that
- // is becoming fat (we could probably generalize this
- // to multiple thin pointers of the same type becoming
- // fat, but we don't). In this case:
- //
- // - `extra` has type `T` before and type `T` after
- // - `ptr` has type `*mut U` before and type `*mut V` after
- //
- // Since just one field changed, we would then check
- // that `*mut U: CoerceUnsized<*mut V>` is implemented
- // (in other words, that we know how to do this
- // conversion). This will work out because `U:
- // Unsize<V>`, and we have a builtin rule that `*mut
- // U` can be coerced to `*mut V` if `U: Unsize<V>`.
- let fields = &def_a.non_enum_variant().fields;
- let diff_fields = fields
- .iter()
- .enumerate()
- .filter_map(|(i, f)| {
- let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
-
- if tcx.type_of(f.did).is_phantom_data() {
- // Ignore PhantomData fields
- return None;
- }
-
- // Ignore fields that aren't changed; it may
- // be that we could get away with subtyping or
- // something more accepting, but we use
- // equality because we want to be able to
- // perform this check without computing
- // variance where possible. (This is because
- // we may have to evaluate constraint
- // expressions in the course of execution.)
- // See e.g., #41936.
- if let Ok(ok) = infcx.at(&cause, param_env).eq(a, b) {
- if ok.obligations.is_empty() {
- return None;
- }
- }
-
- // Collect up all fields that were significantly changed
- // i.e., those that contain T in coerce_unsized T -> U
- Some((i, a, b))
- })
- .collect::<Vec<_>>();
-
- if diff_fields.is_empty() {
- struct_span_err!(
- tcx.sess,
- span,
- E0374,
- "the trait `CoerceUnsized` may only be implemented \
- for a coercion between structures with one field \
- being coerced, none found"
- )
- .emit();
- return err_info;
- } else if diff_fields.len() > 1 {
- let item = tcx.hir().expect_item(impl_did);
- let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(ref t), .. }) =
- item.kind
- {
- t.path.span
- } else {
- tcx.def_span(impl_did)
- };
-
- struct_span_err!(
- tcx.sess,
- span,
- E0375,
- "implementing the trait \
- `CoerceUnsized` requires multiple \
- coercions"
- )
- .note(
- "`CoerceUnsized` may only be implemented for \
- a coercion between structures with one field being coerced",
- )
- .note(&format!(
- "currently, {} fields need coercions: {}",
- diff_fields.len(),
- diff_fields
- .iter()
- .map(|&(i, a, b)| {
- format!("`{}` (`{}` to `{}`)", fields[i].name, a, b)
- })
- .collect::<Vec<_>>()
- .join(", ")
- ))
- .span_label(span, "requires multiple coercions")
- .emit();
- return err_info;
- }
-
- let (i, a, b) = diff_fields[0];
- let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
- (a, b, coerce_unsized_trait, Some(kind))
- }
-
- _ => {
- struct_span_err!(
- tcx.sess,
- span,
- E0376,
- "the trait `CoerceUnsized` may only be implemented \
- for a coercion between structures"
- )
- .emit();
- return err_info;
- }
- };
-
- let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
-
- // Register an obligation for `A: Trait<B>`.
- let cause = traits::ObligationCause::misc(span, impl_hir_id);
- let predicate = predicate_for_trait_def(
- tcx,
- param_env,
- cause,
- trait_def_id,
- 0,
- source,
- &[target.into()],
- );
- fulfill_cx.register_predicate_obligation(&infcx, predicate);
-
- // Check that all transitive obligations are satisfied.
- let errors = fulfill_cx.select_all_or_error(&infcx);
- if !errors.is_empty() {
- infcx.report_fulfillment_errors(&errors, None, false);
- }
-
- // Finally, resolve all regions.
- let outlives_env = OutlivesEnvironment::new(param_env);
- infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env);
-
- CoerceUnsizedInfo { custom_kind: kind }
- })
-}
diff --git a/compiler/rustc_typeck/src/coherence/unsafety.rs b/compiler/rustc_typeck/src/coherence/unsafety.rs
deleted file mode 100644
index e45fb5fe4..000000000
--- a/compiler/rustc_typeck/src/coherence/unsafety.rs
+++ /dev/null
@@ -1,66 +0,0 @@
-//! Unsafety checker: every impl either implements a trait defined in this
-//! crate or pertains to a type defined in this crate.
-
-use rustc_errors::struct_span_err;
-use rustc_hir as hir;
-use rustc_hir::def::DefKind;
-use rustc_hir::Unsafety;
-use rustc_middle::ty::TyCtxt;
-use rustc_span::def_id::LocalDefId;
-
-pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
- debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Impl));
- let item = tcx.hir().expect_item(def_id);
- let hir::ItemKind::Impl(ref impl_) = item.kind else { bug!() };
-
- if let Some(trait_ref) = tcx.impl_trait_ref(item.def_id) {
- let trait_def = tcx.trait_def(trait_ref.def_id);
- let unsafe_attr =
- impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
- match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) {
- (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => {
- struct_span_err!(
- tcx.sess,
- item.span,
- E0199,
- "implementing the trait `{}` is not unsafe",
- trait_ref.print_only_trait_path()
- )
- .emit();
- }
-
- (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => {
- struct_span_err!(
- tcx.sess,
- item.span,
- E0200,
- "the trait `{}` requires an `unsafe impl` declaration",
- trait_ref.print_only_trait_path()
- )
- .emit();
- }
-
- (Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => {
- struct_span_err!(
- tcx.sess,
- item.span,
- E0569,
- "requires an `unsafe impl` declaration due to `#[{}]` attribute",
- attr_name
- )
- .emit();
- }
-
- (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => {
- // Reported in AST validation
- tcx.sess.delay_span_bug(item.span, "unsafe negative impl");
- }
- (_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_))
- | (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive)
- | (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive)
- | (Unsafety::Normal, None, Unsafety::Normal, _) => {
- // OK
- }
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
deleted file mode 100644
index 99996e80c..000000000
--- a/compiler/rustc_typeck/src/collect.rs
+++ /dev/null
@@ -1,3361 +0,0 @@
-//! "Collection" is the process of determining the type and other external
-//! details of each item in Rust. Collection is specifically concerned
-//! with *inter-procedural* things -- for example, for a function
-//! definition, collection will figure out the type and signature of the
-//! function, but it will not visit the *body* of the function in any way,
-//! nor examine type annotations on local variables (that's the job of
-//! type *checking*).
-//!
-//! Collecting is ultimately defined by a bundle of queries that
-//! inquire after various facts about the items in the crate (e.g.,
-//! `type_of`, `generics_of`, `predicates_of`, etc). See the `provide` function
-//! for the full set.
-//!
-//! At present, however, we do run collection across all items in the
-//! crate as a kind of pass. This should eventually be factored away.
-
-use crate::astconv::AstConv;
-use crate::bounds::Bounds;
-use crate::check::intrinsic::intrinsic_operation_unsafety;
-use crate::constrained_generic_params as cgp;
-use crate::errors;
-use crate::middle::resolve_lifetime as rl;
-use rustc_ast as ast;
-use rustc_ast::{MetaItemKind, NestedMetaItem};
-use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr};
-use rustc_data_structures::captures::Captures;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
-use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, DefKind};
-use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
-use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::weak_lang_items;
-use rustc_hir::{GenericParamKind, HirId, Node};
-use rustc_middle::hir::nested_filter;
-use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
-use rustc_middle::mir::mono::Linkage;
-use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::subst::InternalSubsts;
-use rustc_middle::ty::util::Discr;
-use rustc_middle::ty::util::IntTypeExt;
-use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, IsSuggestable, Ty, TyCtxt};
-use rustc_middle::ty::{ReprOptions, ToPredicate};
-use rustc_session::lint;
-use rustc_session::parse::feature_err;
-use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{Span, DUMMY_SP};
-use rustc_target::spec::{abi, SanitizerSet};
-use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
-use std::iter;
-
-mod item_bounds;
-mod type_of;
-
-#[derive(Debug)]
-struct OnlySelfBounds(bool);
-
-///////////////////////////////////////////////////////////////////////////
-// Main entry point
-
-fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
- tcx.hir().visit_item_likes_in_module(module_def_id, &mut CollectItemTypesVisitor { tcx });
-}
-
-pub fn provide(providers: &mut Providers) {
- *providers = Providers {
- opt_const_param_of: type_of::opt_const_param_of,
- type_of: type_of::type_of,
- item_bounds: item_bounds::item_bounds,
- explicit_item_bounds: item_bounds::explicit_item_bounds,
- generics_of,
- predicates_of,
- predicates_defined_on,
- explicit_predicates_of,
- super_predicates_of,
- super_predicates_that_define_assoc_type,
- trait_explicit_predicates_and_bounds,
- type_param_predicates,
- trait_def,
- adt_def,
- fn_sig,
- impl_trait_ref,
- impl_polarity,
- is_foreign_item,
- generator_kind,
- codegen_fn_attrs,
- asm_target_features,
- collect_mod_item_types,
- should_inherit_track_caller,
- ..*providers
- };
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-/// Context specific to some particular item. This is what implements
-/// `AstConv`. It has information about the predicates that are defined
-/// on the trait. Unfortunately, this predicate information is
-/// available in various different forms at various points in the
-/// process. So we can't just store a pointer to e.g., the AST or the
-/// parsed ty form, we have to be more flexible. To this end, the
-/// `ItemCtxt` is parameterized by a `DefId` that it uses to satisfy
-/// `get_type_parameter_bounds` requests, drawing the information from
-/// the AST (`hir::Generics`), recursively.
-pub struct ItemCtxt<'tcx> {
- tcx: TyCtxt<'tcx>,
- item_def_id: DefId,
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-#[derive(Default)]
-pub(crate) struct HirPlaceholderCollector(pub(crate) Vec<Span>);
-
-impl<'v> Visitor<'v> for HirPlaceholderCollector {
- fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
- if let hir::TyKind::Infer = t.kind {
- self.0.push(t.span);
- }
- intravisit::walk_ty(self, t)
- }
- fn visit_generic_arg(&mut self, generic_arg: &'v hir::GenericArg<'v>) {
- match generic_arg {
- hir::GenericArg::Infer(inf) => {
- self.0.push(inf.span);
- intravisit::walk_inf(self, inf);
- }
- hir::GenericArg::Type(t) => self.visit_ty(t),
- _ => {}
- }
- }
- fn visit_array_length(&mut self, length: &'v hir::ArrayLen) {
- if let &hir::ArrayLen::Infer(_, span) = length {
- self.0.push(span);
- }
- intravisit::walk_array_len(self, length)
- }
-}
-
-struct CollectItemTypesVisitor<'tcx> {
- tcx: TyCtxt<'tcx>,
-}
-
-/// If there are any placeholder types (`_`), emit an error explaining that this is not allowed
-/// and suggest adding type parameters in the appropriate place, taking into consideration any and
-/// all already existing generic type parameters to avoid suggesting a name that is already in use.
-pub(crate) fn placeholder_type_error<'tcx>(
- tcx: TyCtxt<'tcx>,
- generics: Option<&hir::Generics<'_>>,
- placeholder_types: Vec<Span>,
- suggest: bool,
- hir_ty: Option<&hir::Ty<'_>>,
- kind: &'static str,
-) {
- if placeholder_types.is_empty() {
- return;
- }
-
- placeholder_type_error_diag(tcx, generics, placeholder_types, vec![], suggest, hir_ty, kind)
- .emit();
-}
-
-pub(crate) fn placeholder_type_error_diag<'tcx>(
- tcx: TyCtxt<'tcx>,
- generics: Option<&hir::Generics<'_>>,
- placeholder_types: Vec<Span>,
- additional_spans: Vec<Span>,
- suggest: bool,
- hir_ty: Option<&hir::Ty<'_>>,
- kind: &'static str,
-) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- if placeholder_types.is_empty() {
- return bad_placeholder(tcx, additional_spans, kind);
- }
-
- let params = generics.map(|g| g.params).unwrap_or_default();
- let type_name = params.next_type_param_name(None);
- let mut sugg: Vec<_> =
- placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect();
-
- if let Some(generics) = generics {
- if let Some(arg) = params.iter().find(|arg| {
- matches!(arg.name, hir::ParamName::Plain(Ident { name: kw::Underscore, .. }))
- }) {
- // Account for `_` already present in cases like `struct S<_>(_);` and suggest
- // `struct S<T>(T);` instead of `struct S<_, T>(T);`.
- sugg.push((arg.span, (*type_name).to_string()));
- } else if let Some(span) = generics.span_for_param_suggestion() {
- // Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
- sugg.push((span, format!(", {}", type_name)));
- } else {
- sugg.push((generics.span, format!("<{}>", type_name)));
- }
- }
-
- let mut err =
- bad_placeholder(tcx, placeholder_types.into_iter().chain(additional_spans).collect(), kind);
-
- // Suggest, but only if it is not a function in const or static
- if suggest {
- let mut is_fn = false;
- let mut is_const_or_static = false;
-
- if let Some(hir_ty) = hir_ty && let hir::TyKind::BareFn(_) = hir_ty.kind {
- is_fn = true;
-
- // Check if parent is const or static
- let parent_id = tcx.hir().get_parent_node(hir_ty.hir_id);
- let parent_node = tcx.hir().get(parent_id);
-
- is_const_or_static = matches!(
- parent_node,
- Node::Item(&hir::Item {
- kind: hir::ItemKind::Const(..) | hir::ItemKind::Static(..),
- ..
- }) | Node::TraitItem(&hir::TraitItem {
- kind: hir::TraitItemKind::Const(..),
- ..
- }) | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. })
- );
- }
-
- // if function is wrapped around a const or static,
- // then don't show the suggestion
- if !(is_fn && is_const_or_static) {
- err.multipart_suggestion(
- "use type parameters instead",
- sugg,
- Applicability::HasPlaceholders,
- );
- }
- }
-
- err
-}
-
-fn reject_placeholder_type_signatures_in_item<'tcx>(
- tcx: TyCtxt<'tcx>,
- item: &'tcx hir::Item<'tcx>,
-) {
- let (generics, suggest) = match &item.kind {
- hir::ItemKind::Union(_, generics)
- | hir::ItemKind::Enum(_, generics)
- | hir::ItemKind::TraitAlias(generics, _)
- | hir::ItemKind::Trait(_, _, generics, ..)
- | hir::ItemKind::Impl(hir::Impl { generics, .. })
- | hir::ItemKind::Struct(_, generics) => (generics, true),
- hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. })
- | hir::ItemKind::TyAlias(_, generics) => (generics, false),
- // `static`, `fn` and `const` are handled elsewhere to suggest appropriate type.
- _ => return,
- };
-
- let mut visitor = HirPlaceholderCollector::default();
- visitor.visit_item(item);
-
- placeholder_type_error(tcx, Some(generics), visitor.0, suggest, None, item.kind.descr());
-}
-
-impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
- type NestedFilter = nested_filter::OnlyBodies;
-
- fn nested_visit_map(&mut self) -> Self::Map {
- self.tcx.hir()
- }
-
- fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
- convert_item(self.tcx, item.item_id());
- reject_placeholder_type_signatures_in_item(self.tcx, item);
- intravisit::walk_item(self, item);
- }
-
- fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
- for param in generics.params {
- match param.kind {
- hir::GenericParamKind::Lifetime { .. } => {}
- hir::GenericParamKind::Type { default: Some(_), .. } => {
- let def_id = self.tcx.hir().local_def_id(param.hir_id);
- self.tcx.ensure().type_of(def_id);
- }
- hir::GenericParamKind::Type { .. } => {}
- hir::GenericParamKind::Const { default, .. } => {
- let def_id = self.tcx.hir().local_def_id(param.hir_id);
- self.tcx.ensure().type_of(def_id);
- if let Some(default) = default {
- let default_def_id = self.tcx.hir().local_def_id(default.hir_id);
- // need to store default and type of default
- self.tcx.ensure().type_of(default_def_id);
- self.tcx.ensure().const_param_default(def_id);
- }
- }
- }
- }
- intravisit::walk_generics(self, generics);
- }
-
- fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
- if let hir::ExprKind::Closure { .. } = expr.kind {
- let def_id = self.tcx.hir().local_def_id(expr.hir_id);
- self.tcx.ensure().generics_of(def_id);
- // We do not call `type_of` for closures here as that
- // depends on typecheck and would therefore hide
- // any further errors in case one typeck fails.
- }
- intravisit::walk_expr(self, expr);
- }
-
- fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
- convert_trait_item(self.tcx, trait_item.trait_item_id());
- intravisit::walk_trait_item(self, trait_item);
- }
-
- fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
- convert_impl_item(self.tcx, impl_item.impl_item_id());
- intravisit::walk_impl_item(self, impl_item);
- }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Utility types and common code for the above passes.
-
-fn bad_placeholder<'tcx>(
- tcx: TyCtxt<'tcx>,
- mut spans: Vec<Span>,
- kind: &'static str,
-) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- let kind = if kind.ends_with('s') { format!("{}es", kind) } else { format!("{}s", kind) };
-
- spans.sort();
- let mut err = struct_span_err!(
- tcx.sess,
- spans.clone(),
- E0121,
- "the placeholder `_` is not allowed within types on item signatures for {}",
- kind
- );
- for span in spans {
- err.span_label(span, "not allowed in type signatures");
- }
- err
-}
-
-impl<'tcx> ItemCtxt<'tcx> {
- pub fn new(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> ItemCtxt<'tcx> {
- ItemCtxt { tcx, item_def_id }
- }
-
- pub fn to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
- <dyn AstConv<'_>>::ast_ty_to_ty(self, ast_ty)
- }
-
- pub fn hir_id(&self) -> hir::HirId {
- self.tcx.hir().local_def_id_to_hir_id(self.item_def_id.expect_local())
- }
-
- pub fn node(&self) -> hir::Node<'tcx> {
- self.tcx.hir().get(self.hir_id())
- }
-}
-
-impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
- fn tcx(&self) -> TyCtxt<'tcx> {
- self.tcx
- }
-
- fn item_def_id(&self) -> Option<DefId> {
- Some(self.item_def_id)
- }
-
- fn get_type_parameter_bounds(
- &self,
- span: Span,
- def_id: DefId,
- assoc_name: Ident,
- ) -> ty::GenericPredicates<'tcx> {
- self.tcx.at(span).type_param_predicates((
- self.item_def_id,
- def_id.expect_local(),
- assoc_name,
- ))
- }
-
- fn re_infer(&self, _: Option<&ty::GenericParamDef>, _: Span) -> Option<ty::Region<'tcx>> {
- None
- }
-
- fn allow_ty_infer(&self) -> bool {
- false
- }
-
- fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
- self.tcx().ty_error_with_message(span, "bad placeholder type")
- }
-
- fn ct_infer(&self, ty: Ty<'tcx>, _: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> {
- let ty = self.tcx.fold_regions(ty, |r, _| match *r {
- ty::ReErased => self.tcx.lifetimes.re_static,
- _ => r,
- });
- self.tcx().const_error_with_message(ty, span, "bad placeholder constant")
- }
-
- fn projected_ty_from_poly_trait_ref(
- &self,
- span: Span,
- item_def_id: DefId,
- item_segment: &hir::PathSegment<'_>,
- poly_trait_ref: ty::PolyTraitRef<'tcx>,
- ) -> Ty<'tcx> {
- if let Some(trait_ref) = poly_trait_ref.no_bound_vars() {
- let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item(
- self,
- self.tcx,
- span,
- item_def_id,
- item_segment,
- trait_ref.substs,
- );
- self.tcx().mk_projection(item_def_id, item_substs)
- } else {
- // There are no late-bound regions; we can just ignore the binder.
- let mut err = struct_span_err!(
- self.tcx().sess,
- span,
- E0212,
- "cannot use the associated type of a trait \
- with uninferred generic parameters"
- );
-
- match self.node() {
- hir::Node::Field(_) | hir::Node::Ctor(_) | hir::Node::Variant(_) => {
- let item =
- self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(self.hir_id()));
- match &item.kind {
- hir::ItemKind::Enum(_, generics)
- | hir::ItemKind::Struct(_, generics)
- | hir::ItemKind::Union(_, generics) => {
- let lt_name = get_new_lifetime_name(self.tcx, poly_trait_ref, generics);
- let (lt_sp, sugg) = match generics.params {
- [] => (generics.span, format!("<{}>", lt_name)),
- [bound, ..] => {
- (bound.span.shrink_to_lo(), format!("{}, ", lt_name))
- }
- };
- let suggestions = vec![
- (lt_sp, sugg),
- (
- span.with_hi(item_segment.ident.span.lo()),
- format!(
- "{}::",
- // Replace the existing lifetimes with a new named lifetime.
- self.tcx.replace_late_bound_regions_uncached(
- poly_trait_ref,
- |_| {
- self.tcx.mk_region(ty::ReEarlyBound(
- ty::EarlyBoundRegion {
- def_id: item_def_id,
- index: 0,
- name: Symbol::intern(&lt_name),
- },
- ))
- }
- ),
- ),
- ),
- ];
- err.multipart_suggestion(
- "use a fully qualified path with explicit lifetimes",
- suggestions,
- Applicability::MaybeIncorrect,
- );
- }
- _ => {}
- }
- }
- hir::Node::Item(hir::Item {
- kind:
- hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..),
- ..
- }) => {}
- hir::Node::Item(_)
- | hir::Node::ForeignItem(_)
- | hir::Node::TraitItem(_)
- | hir::Node::ImplItem(_) => {
- err.span_suggestion_verbose(
- span.with_hi(item_segment.ident.span.lo()),
- "use a fully qualified path with inferred lifetimes",
- format!(
- "{}::",
- // Erase named lt, we want `<A as B<'_>::C`, not `<A as B<'a>::C`.
- self.tcx.anonymize_late_bound_regions(poly_trait_ref).skip_binder(),
- ),
- Applicability::MaybeIncorrect,
- );
- }
- _ => {}
- }
- err.emit();
- self.tcx().ty_error()
- }
- }
-
- fn normalize_ty(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
- // Types in item signatures are not normalized to avoid undue dependencies.
- ty
- }
-
- fn set_tainted_by_errors(&self) {
- // There's no obvious place to track this, so just let it go.
- }
-
- fn record_ty(&self, _hir_id: hir::HirId, _ty: Ty<'tcx>, _span: Span) {
- // There's no place to record types from signatures?
- }
-}
-
-/// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present.
-fn get_new_lifetime_name<'tcx>(
- tcx: TyCtxt<'tcx>,
- poly_trait_ref: ty::PolyTraitRef<'tcx>,
- generics: &hir::Generics<'tcx>,
-) -> String {
- let existing_lifetimes = tcx
- .collect_referenced_late_bound_regions(&poly_trait_ref)
- .into_iter()
- .filter_map(|lt| {
- if let ty::BoundRegionKind::BrNamed(_, name) = lt {
- Some(name.as_str().to_string())
- } else {
- None
- }
- })
- .chain(generics.params.iter().filter_map(|param| {
- if let hir::GenericParamKind::Lifetime { .. } = &param.kind {
- Some(param.name.ident().as_str().to_string())
- } else {
- None
- }
- }))
- .collect::<FxHashSet<String>>();
-
- let a_to_z_repeat_n = |n| {
- (b'a'..=b'z').map(move |c| {
- let mut s = '\''.to_string();
- s.extend(std::iter::repeat(char::from(c)).take(n));
- s
- })
- };
-
- // If all single char lifetime names are present, we wrap around and double the chars.
- (1..).flat_map(a_to_z_repeat_n).find(|lt| !existing_lifetimes.contains(lt.as_str())).unwrap()
-}
-
-/// Returns the predicates defined on `item_def_id` of the form
-/// `X: Foo` where `X` is the type parameter `def_id`.
-fn type_param_predicates(
- tcx: TyCtxt<'_>,
- (item_def_id, def_id, assoc_name): (DefId, LocalDefId, Ident),
-) -> ty::GenericPredicates<'_> {
- use rustc_hir::*;
-
- // In the AST, bounds can derive from two places. Either
- // written inline like `<T: Foo>` or in a where-clause like
- // `where T: Foo`.
-
- let param_id = tcx.hir().local_def_id_to_hir_id(def_id);
- let param_owner = tcx.hir().ty_param_owner(def_id);
- let generics = tcx.generics_of(param_owner);
- let index = generics.param_def_id_to_index[&def_id.to_def_id()];
- let ty = tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id));
-
- // Don't look for bounds where the type parameter isn't in scope.
- let parent = if item_def_id == param_owner.to_def_id() {
- None
- } else {
- tcx.generics_of(item_def_id).parent
- };
-
- let mut result = parent
- .map(|parent| {
- let icx = ItemCtxt::new(tcx, parent);
- icx.get_type_parameter_bounds(DUMMY_SP, def_id.to_def_id(), assoc_name)
- })
- .unwrap_or_default();
- let mut extend = None;
-
- let item_hir_id = tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local());
- let ast_generics = match tcx.hir().get(item_hir_id) {
- Node::TraitItem(item) => &item.generics,
-
- Node::ImplItem(item) => &item.generics,
-
- Node::Item(item) => {
- match item.kind {
- ItemKind::Fn(.., ref generics, _)
- | ItemKind::Impl(hir::Impl { ref generics, .. })
- | ItemKind::TyAlias(_, ref generics)
- | ItemKind::OpaqueTy(OpaqueTy {
- ref generics,
- origin: hir::OpaqueTyOrigin::TyAlias,
- ..
- })
- | ItemKind::Enum(_, ref generics)
- | ItemKind::Struct(_, ref generics)
- | ItemKind::Union(_, ref generics) => generics,
- ItemKind::Trait(_, _, ref generics, ..) => {
- // Implied `Self: Trait` and supertrait bounds.
- if param_id == item_hir_id {
- let identity_trait_ref = ty::TraitRef::identity(tcx, item_def_id);
- extend =
- Some((identity_trait_ref.without_const().to_predicate(tcx), item.span));
- }
- generics
- }
- _ => return result,
- }
- }
-
- Node::ForeignItem(item) => match item.kind {
- ForeignItemKind::Fn(_, _, ref generics) => generics,
- _ => return result,
- },
-
- _ => return result,
- };
-
- let icx = ItemCtxt::new(tcx, item_def_id);
- let extra_predicates = extend.into_iter().chain(
- icx.type_parameter_bounds_in_generics(
- ast_generics,
- param_id,
- ty,
- OnlySelfBounds(true),
- Some(assoc_name),
- )
- .into_iter()
- .filter(|(predicate, _)| match predicate.kind().skip_binder() {
- ty::PredicateKind::Trait(data) => data.self_ty().is_param(index),
- _ => false,
- }),
- );
- result.predicates =
- tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(extra_predicates));
- result
-}
-
-impl<'tcx> ItemCtxt<'tcx> {
- /// Finds bounds from `hir::Generics`. This requires scanning through the
- /// AST. We do this to avoid having to convert *all* the bounds, which
- /// would create artificial cycles. Instead, we can only convert the
- /// bounds for a type parameter `X` if `X::Foo` is used.
- #[instrument(level = "trace", skip(self, ast_generics))]
- fn type_parameter_bounds_in_generics(
- &self,
- ast_generics: &'tcx hir::Generics<'tcx>,
- param_id: hir::HirId,
- ty: Ty<'tcx>,
- only_self_bounds: OnlySelfBounds,
- assoc_name: Option<Ident>,
- ) -> Vec<(ty::Predicate<'tcx>, Span)> {
- let param_def_id = self.tcx.hir().local_def_id(param_id).to_def_id();
- debug!(?param_def_id);
- ast_generics
- .predicates
- .iter()
- .filter_map(|wp| match *wp {
- hir::WherePredicate::BoundPredicate(ref bp) => Some(bp),
- _ => None,
- })
- .flat_map(|bp| {
- let bt = if bp.is_param_bound(param_def_id) {
- Some(ty)
- } else if !only_self_bounds.0 {
- Some(self.to_ty(bp.bounded_ty))
- } else {
- None
- };
- let bvars = self.tcx.late_bound_vars(bp.bounded_ty.hir_id);
-
- bp.bounds.iter().filter_map(move |b| bt.map(|bt| (bt, b, bvars))).filter(
- |(_, b, _)| match assoc_name {
- Some(assoc_name) => self.bound_defines_assoc_item(b, assoc_name),
- None => true,
- },
- )
- })
- .flat_map(|(bt, b, bvars)| predicates_from_bound(self, bt, b, bvars))
- .collect()
- }
-
- fn bound_defines_assoc_item(&self, b: &hir::GenericBound<'_>, assoc_name: Ident) -> bool {
- debug!("bound_defines_assoc_item(b={:?}, assoc_name={:?})", b, assoc_name);
-
- match b {
- hir::GenericBound::Trait(poly_trait_ref, _) => {
- let trait_ref = &poly_trait_ref.trait_ref;
- if let Some(trait_did) = trait_ref.trait_def_id() {
- self.tcx.trait_may_define_assoc_type(trait_did, assoc_name)
- } else {
- false
- }
- }
- _ => false,
- }
- }
-}
-
-fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
- let it = tcx.hir().item(item_id);
- debug!("convert: item {} with id {}", it.ident, it.hir_id());
- let def_id = item_id.def_id;
-
- match it.kind {
- // These don't define types.
- hir::ItemKind::ExternCrate(_)
- | hir::ItemKind::Use(..)
- | hir::ItemKind::Macro(..)
- | hir::ItemKind::Mod(_)
- | hir::ItemKind::GlobalAsm(_) => {}
- hir::ItemKind::ForeignMod { items, .. } => {
- for item in items {
- let item = tcx.hir().foreign_item(item.id);
- tcx.ensure().generics_of(item.def_id);
- tcx.ensure().type_of(item.def_id);
- tcx.ensure().predicates_of(item.def_id);
- match item.kind {
- hir::ForeignItemKind::Fn(..) => tcx.ensure().fn_sig(item.def_id),
- hir::ForeignItemKind::Static(..) => {
- let mut visitor = HirPlaceholderCollector::default();
- visitor.visit_foreign_item(item);
- placeholder_type_error(
- tcx,
- None,
- visitor.0,
- false,
- None,
- "static variable",
- );
- }
- _ => (),
- }
- }
- }
- hir::ItemKind::Enum(ref enum_definition, _) => {
- tcx.ensure().generics_of(def_id);
- tcx.ensure().type_of(def_id);
- tcx.ensure().predicates_of(def_id);
- convert_enum_variant_types(tcx, def_id.to_def_id(), enum_definition.variants);
- }
- hir::ItemKind::Impl { .. } => {
- tcx.ensure().generics_of(def_id);
- tcx.ensure().type_of(def_id);
- tcx.ensure().impl_trait_ref(def_id);
- tcx.ensure().predicates_of(def_id);
- }
- hir::ItemKind::Trait(..) => {
- tcx.ensure().generics_of(def_id);
- tcx.ensure().trait_def(def_id);
- tcx.at(it.span).super_predicates_of(def_id);
- tcx.ensure().predicates_of(def_id);
- }
- hir::ItemKind::TraitAlias(..) => {
- tcx.ensure().generics_of(def_id);
- tcx.at(it.span).super_predicates_of(def_id);
- tcx.ensure().predicates_of(def_id);
- }
- hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => {
- tcx.ensure().generics_of(def_id);
- tcx.ensure().type_of(def_id);
- tcx.ensure().predicates_of(def_id);
-
- for f in struct_def.fields() {
- let def_id = tcx.hir().local_def_id(f.hir_id);
- tcx.ensure().generics_of(def_id);
- tcx.ensure().type_of(def_id);
- tcx.ensure().predicates_of(def_id);
- }
-
- if let Some(ctor_hir_id) = struct_def.ctor_hir_id() {
- convert_variant_ctor(tcx, ctor_hir_id);
- }
- }
-
- // Desugared from `impl Trait`, so visited by the function's return type.
- hir::ItemKind::OpaqueTy(hir::OpaqueTy {
- origin: hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..),
- ..
- }) => {}
-
- // Don't call `type_of` on opaque types, since that depends on type
- // checking function bodies. `check_item_type` ensures that it's called
- // instead.
- hir::ItemKind::OpaqueTy(..) => {
- tcx.ensure().generics_of(def_id);
- tcx.ensure().predicates_of(def_id);
- tcx.ensure().explicit_item_bounds(def_id);
- }
- hir::ItemKind::TyAlias(..)
- | hir::ItemKind::Static(..)
- | hir::ItemKind::Const(..)
- | hir::ItemKind::Fn(..) => {
- tcx.ensure().generics_of(def_id);
- tcx.ensure().type_of(def_id);
- tcx.ensure().predicates_of(def_id);
- match it.kind {
- hir::ItemKind::Fn(..) => tcx.ensure().fn_sig(def_id),
- hir::ItemKind::OpaqueTy(..) => tcx.ensure().item_bounds(def_id),
- hir::ItemKind::Const(ty, ..) | hir::ItemKind::Static(ty, ..) => {
- if !is_suggestable_infer_ty(ty) {
- let mut visitor = HirPlaceholderCollector::default();
- visitor.visit_item(it);
- placeholder_type_error(tcx, None, visitor.0, false, None, it.kind.descr());
- }
- }
- _ => (),
- }
- }
- }
-}
-
-fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
- let trait_item = tcx.hir().trait_item(trait_item_id);
- tcx.ensure().generics_of(trait_item_id.def_id);
-
- match trait_item.kind {
- hir::TraitItemKind::Fn(..) => {
- tcx.ensure().type_of(trait_item_id.def_id);
- tcx.ensure().fn_sig(trait_item_id.def_id);
- }
-
- hir::TraitItemKind::Const(.., Some(_)) => {
- tcx.ensure().type_of(trait_item_id.def_id);
- }
-
- hir::TraitItemKind::Const(..) => {
- tcx.ensure().type_of(trait_item_id.def_id);
- // Account for `const C: _;`.
- let mut visitor = HirPlaceholderCollector::default();
- visitor.visit_trait_item(trait_item);
- placeholder_type_error(tcx, None, visitor.0, false, None, "constant");
- }
-
- hir::TraitItemKind::Type(_, Some(_)) => {
- tcx.ensure().item_bounds(trait_item_id.def_id);
- tcx.ensure().type_of(trait_item_id.def_id);
- // Account for `type T = _;`.
- let mut visitor = HirPlaceholderCollector::default();
- visitor.visit_trait_item(trait_item);
- placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
- }
-
- hir::TraitItemKind::Type(_, None) => {
- tcx.ensure().item_bounds(trait_item_id.def_id);
- // #74612: Visit and try to find bad placeholders
- // even if there is no concrete type.
- let mut visitor = HirPlaceholderCollector::default();
- visitor.visit_trait_item(trait_item);
-
- placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
- }
- };
-
- tcx.ensure().predicates_of(trait_item_id.def_id);
-}
-
-fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
- let def_id = impl_item_id.def_id;
- tcx.ensure().generics_of(def_id);
- tcx.ensure().type_of(def_id);
- tcx.ensure().predicates_of(def_id);
- let impl_item = tcx.hir().impl_item(impl_item_id);
- match impl_item.kind {
- hir::ImplItemKind::Fn(..) => {
- tcx.ensure().fn_sig(def_id);
- }
- hir::ImplItemKind::TyAlias(_) => {
- // Account for `type T = _;`
- let mut visitor = HirPlaceholderCollector::default();
- visitor.visit_impl_item(impl_item);
-
- placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
- }
- hir::ImplItemKind::Const(..) => {}
- }
-}
-
-fn convert_variant_ctor(tcx: TyCtxt<'_>, ctor_id: hir::HirId) {
- let def_id = tcx.hir().local_def_id(ctor_id);
- tcx.ensure().generics_of(def_id);
- tcx.ensure().type_of(def_id);
- tcx.ensure().predicates_of(def_id);
-}
-
-fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId, variants: &[hir::Variant<'_>]) {
- let def = tcx.adt_def(def_id);
- let repr_type = def.repr().discr_type();
- let initial = repr_type.initial_discriminant(tcx);
- let mut prev_discr = None::<Discr<'_>>;
-
- // fill the discriminant values and field types
- for variant in variants {
- let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx));
- prev_discr = Some(
- if let Some(ref e) = variant.disr_expr {
- let expr_did = tcx.hir().local_def_id(e.hir_id);
- def.eval_explicit_discr(tcx, expr_did.to_def_id())
- } else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) {
- Some(discr)
- } else {
- struct_span_err!(tcx.sess, variant.span, E0370, "enum discriminant overflowed")
- .span_label(
- variant.span,
- format!("overflowed on value after {}", prev_discr.unwrap()),
- )
- .note(&format!(
- "explicitly set `{} = {}` if that is desired outcome",
- variant.ident, wrapped_discr
- ))
- .emit();
- None
- }
- .unwrap_or(wrapped_discr),
- );
-
- for f in variant.data.fields() {
- let def_id = tcx.hir().local_def_id(f.hir_id);
- tcx.ensure().generics_of(def_id);
- tcx.ensure().type_of(def_id);
- tcx.ensure().predicates_of(def_id);
- }
-
- // Convert the ctor, if any. This also registers the variant as
- // an item.
- if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
- convert_variant_ctor(tcx, ctor_hir_id);
- }
- }
-}
-
-fn convert_variant(
- tcx: TyCtxt<'_>,
- variant_did: Option<LocalDefId>,
- ctor_did: Option<LocalDefId>,
- ident: Ident,
- discr: ty::VariantDiscr,
- def: &hir::VariantData<'_>,
- adt_kind: ty::AdtKind,
- parent_did: LocalDefId,
-) -> ty::VariantDef {
- let mut seen_fields: FxHashMap<Ident, Span> = Default::default();
- let fields = def
- .fields()
- .iter()
- .map(|f| {
- let fid = tcx.hir().local_def_id(f.hir_id);
- let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned();
- if let Some(prev_span) = dup_span {
- tcx.sess.emit_err(errors::FieldAlreadyDeclared {
- field_name: f.ident,
- span: f.span,
- prev_span,
- });
- } else {
- seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span);
- }
-
- ty::FieldDef { did: fid.to_def_id(), name: f.ident.name, vis: tcx.visibility(fid) }
- })
- .collect();
- let recovered = match def {
- hir::VariantData::Struct(_, r) => *r,
- _ => false,
- };
- ty::VariantDef::new(
- ident.name,
- variant_did.map(LocalDefId::to_def_id),
- ctor_did.map(LocalDefId::to_def_id),
- discr,
- fields,
- CtorKind::from_hir(def),
- adt_kind,
- parent_did.to_def_id(),
- recovered,
- adt_kind == AdtKind::Struct && tcx.has_attr(parent_did.to_def_id(), sym::non_exhaustive)
- || variant_did.map_or(false, |variant_did| {
- tcx.has_attr(variant_did.to_def_id(), sym::non_exhaustive)
- }),
- )
-}
-
-fn adt_def<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::AdtDef<'tcx> {
- use rustc_hir::*;
-
- let def_id = def_id.expect_local();
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
- let Node::Item(item) = tcx.hir().get(hir_id) else {
- bug!();
- };
-
- let repr = ReprOptions::new(tcx, def_id.to_def_id());
- let (kind, variants) = match item.kind {
- ItemKind::Enum(ref def, _) => {
- let mut distance_from_explicit = 0;
- let variants = def
- .variants
- .iter()
- .map(|v| {
- let variant_did = Some(tcx.hir().local_def_id(v.id));
- let ctor_did =
- v.data.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id));
-
- let discr = if let Some(ref e) = v.disr_expr {
- distance_from_explicit = 0;
- ty::VariantDiscr::Explicit(tcx.hir().local_def_id(e.hir_id).to_def_id())
- } else {
- ty::VariantDiscr::Relative(distance_from_explicit)
- };
- distance_from_explicit += 1;
-
- convert_variant(
- tcx,
- variant_did,
- ctor_did,
- v.ident,
- discr,
- &v.data,
- AdtKind::Enum,
- def_id,
- )
- })
- .collect();
-
- (AdtKind::Enum, variants)
- }
- ItemKind::Struct(ref def, _) => {
- let variant_did = None::<LocalDefId>;
- let ctor_did = def.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id));
-
- let variants = std::iter::once(convert_variant(
- tcx,
- variant_did,
- ctor_did,
- item.ident,
- ty::VariantDiscr::Relative(0),
- def,
- AdtKind::Struct,
- def_id,
- ))
- .collect();
-
- (AdtKind::Struct, variants)
- }
- ItemKind::Union(ref def, _) => {
- let variant_did = None;
- let ctor_did = def.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id));
-
- let variants = std::iter::once(convert_variant(
- tcx,
- variant_did,
- ctor_did,
- item.ident,
- ty::VariantDiscr::Relative(0),
- def,
- AdtKind::Union,
- def_id,
- ))
- .collect();
-
- (AdtKind::Union, variants)
- }
- _ => bug!(),
- };
- tcx.alloc_adt_def(def_id.to_def_id(), kind, variants, repr)
-}
-
-/// Ensures that the super-predicates of the trait with a `DefId`
-/// of `trait_def_id` are converted and stored. This also ensures that
-/// the transitive super-predicates are converted.
-fn super_predicates_of(tcx: TyCtxt<'_>, trait_def_id: DefId) -> ty::GenericPredicates<'_> {
- debug!("super_predicates(trait_def_id={:?})", trait_def_id);
- tcx.super_predicates_that_define_assoc_type((trait_def_id, None))
-}
-
-/// Ensures that the super-predicates of the trait with a `DefId`
-/// of `trait_def_id` are converted and stored. This also ensures that
-/// the transitive super-predicates are converted.
-fn super_predicates_that_define_assoc_type(
- tcx: TyCtxt<'_>,
- (trait_def_id, assoc_name): (DefId, Option<Ident>),
-) -> ty::GenericPredicates<'_> {
- debug!(
- "super_predicates_that_define_assoc_type(trait_def_id={:?}, assoc_name={:?})",
- trait_def_id, assoc_name
- );
- if trait_def_id.is_local() {
- debug!("super_predicates_that_define_assoc_type: local trait_def_id={:?}", trait_def_id);
- let trait_hir_id = tcx.hir().local_def_id_to_hir_id(trait_def_id.expect_local());
-
- let Node::Item(item) = tcx.hir().get(trait_hir_id) else {
- bug!("trait_node_id {} is not an item", trait_hir_id);
- };
-
- let (generics, bounds) = match item.kind {
- hir::ItemKind::Trait(.., ref generics, ref supertraits, _) => (generics, supertraits),
- hir::ItemKind::TraitAlias(ref generics, ref supertraits) => (generics, supertraits),
- _ => span_bug!(item.span, "super_predicates invoked on non-trait"),
- };
-
- let icx = ItemCtxt::new(tcx, trait_def_id);
-
- // Convert the bounds that follow the colon, e.g., `Bar + Zed` in `trait Foo: Bar + Zed`.
- let self_param_ty = tcx.types.self_param;
- let superbounds1 = if let Some(assoc_name) = assoc_name {
- <dyn AstConv<'_>>::compute_bounds_that_match_assoc_type(
- &icx,
- self_param_ty,
- bounds,
- assoc_name,
- )
- } else {
- <dyn AstConv<'_>>::compute_bounds(&icx, self_param_ty, bounds)
- };
-
- let superbounds1 = superbounds1.predicates(tcx, self_param_ty);
-
- // Convert any explicit superbounds in the where-clause,
- // e.g., `trait Foo where Self: Bar`.
- // In the case of trait aliases, however, we include all bounds in the where-clause,
- // so e.g., `trait Foo = where u32: PartialEq<Self>` would include `u32: PartialEq<Self>`
- // as one of its "superpredicates".
- let is_trait_alias = tcx.is_trait_alias(trait_def_id);
- let superbounds2 = icx.type_parameter_bounds_in_generics(
- generics,
- item.hir_id(),
- self_param_ty,
- OnlySelfBounds(!is_trait_alias),
- assoc_name,
- );
-
- // Combine the two lists to form the complete set of superbounds:
- let superbounds = &*tcx.arena.alloc_from_iter(superbounds1.into_iter().chain(superbounds2));
- debug!(?superbounds);
-
- // Now require that immediate supertraits are converted,
- // which will, in turn, reach indirect supertraits.
- if assoc_name.is_none() {
- // Now require that immediate supertraits are converted,
- // which will, in turn, reach indirect supertraits.
- for &(pred, span) in superbounds {
- debug!("superbound: {:?}", pred);
- if let ty::PredicateKind::Trait(bound) = pred.kind().skip_binder() {
- tcx.at(span).super_predicates_of(bound.def_id());
- }
- }
- }
-
- ty::GenericPredicates { parent: None, predicates: superbounds }
- } else {
- // if `assoc_name` is None, then the query should've been redirected to an
- // external provider
- assert!(assoc_name.is_some());
- tcx.super_predicates_of(trait_def_id)
- }
-}
-
-fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
- let item = tcx.hir().expect_item(def_id.expect_local());
-
- let (is_auto, unsafety, items) = match item.kind {
- hir::ItemKind::Trait(is_auto, unsafety, .., items) => {
- (is_auto == hir::IsAuto::Yes, unsafety, items)
- }
- hir::ItemKind::TraitAlias(..) => (false, hir::Unsafety::Normal, &[][..]),
- _ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"),
- };
-
- let paren_sugar = tcx.has_attr(def_id, sym::rustc_paren_sugar);
- if paren_sugar && !tcx.features().unboxed_closures {
- tcx.sess
- .struct_span_err(
- item.span,
- "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling \
- which traits can use parenthetical notation",
- )
- .help("add `#![feature(unboxed_closures)]` to the crate attributes to use it")
- .emit();
- }
-
- let is_marker = tcx.has_attr(def_id, sym::marker);
- let skip_array_during_method_dispatch =
- tcx.has_attr(def_id, sym::rustc_skip_array_during_method_dispatch);
- let spec_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) {
- ty::trait_def::TraitSpecializationKind::Marker
- } else if tcx.has_attr(def_id, sym::rustc_specialization_trait) {
- ty::trait_def::TraitSpecializationKind::AlwaysApplicable
- } else {
- ty::trait_def::TraitSpecializationKind::None
- };
- let must_implement_one_of = tcx
- .get_attr(def_id, sym::rustc_must_implement_one_of)
- // Check that there are at least 2 arguments of `#[rustc_must_implement_one_of]`
- // and that they are all identifiers
- .and_then(|attr| match attr.meta_item_list() {
- Some(items) if items.len() < 2 => {
- tcx.sess
- .struct_span_err(
- attr.span,
- "the `#[rustc_must_implement_one_of]` attribute must be \
- used with at least 2 args",
- )
- .emit();
-
- None
- }
- Some(items) => items
- .into_iter()
- .map(|item| item.ident().ok_or(item.span()))
- .collect::<Result<Box<[_]>, _>>()
- .map_err(|span| {
- tcx.sess
- .struct_span_err(span, "must be a name of an associated function")
- .emit();
- })
- .ok()
- .zip(Some(attr.span)),
- // Error is reported by `rustc_attr!`
- None => None,
- })
- // Check that all arguments of `#[rustc_must_implement_one_of]` reference
- // functions in the trait with default implementations
- .and_then(|(list, attr_span)| {
- let errors = list.iter().filter_map(|ident| {
- let item = items.iter().find(|item| item.ident == *ident);
-
- match item {
- Some(item) if matches!(item.kind, hir::AssocItemKind::Fn { .. }) => {
- if !tcx.impl_defaultness(item.id.def_id).has_value() {
- tcx.sess
- .struct_span_err(
- item.span,
- "This function doesn't have a default implementation",
- )
- .span_note(attr_span, "required by this annotation")
- .emit();
-
- return Some(());
- }
-
- return None;
- }
- Some(item) => {
- tcx.sess
- .struct_span_err(item.span, "Not a function")
- .span_note(attr_span, "required by this annotation")
- .note(
- "All `#[rustc_must_implement_one_of]` arguments \
- must be associated function names",
- )
- .emit();
- }
- None => {
- tcx.sess
- .struct_span_err(ident.span, "Function not found in this trait")
- .emit();
- }
- }
-
- Some(())
- });
-
- (errors.count() == 0).then_some(list)
- })
- // Check for duplicates
- .and_then(|list| {
- let mut set: FxHashMap<Symbol, Span> = FxHashMap::default();
- let mut no_dups = true;
-
- for ident in &*list {
- if let Some(dup) = set.insert(ident.name, ident.span) {
- tcx.sess
- .struct_span_err(vec![dup, ident.span], "Functions names are duplicated")
- .note(
- "All `#[rustc_must_implement_one_of]` arguments \
- must be unique",
- )
- .emit();
-
- no_dups = false;
- }
- }
-
- no_dups.then_some(list)
- });
-
- ty::TraitDef::new(
- def_id,
- unsafety,
- paren_sugar,
- is_auto,
- is_marker,
- skip_array_during_method_dispatch,
- spec_kind,
- must_implement_one_of,
- )
-}
-
-fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<Span> {
- struct LateBoundRegionsDetector<'tcx> {
- tcx: TyCtxt<'tcx>,
- outer_index: ty::DebruijnIndex,
- has_late_bound_regions: Option<Span>,
- }
-
- impl<'tcx> Visitor<'tcx> for LateBoundRegionsDetector<'tcx> {
- fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
- if self.has_late_bound_regions.is_some() {
- return;
- }
- match ty.kind {
- hir::TyKind::BareFn(..) => {
- self.outer_index.shift_in(1);
- intravisit::walk_ty(self, ty);
- self.outer_index.shift_out(1);
- }
- _ => intravisit::walk_ty(self, ty),
- }
- }
-
- fn visit_poly_trait_ref(
- &mut self,
- tr: &'tcx hir::PolyTraitRef<'tcx>,
- m: hir::TraitBoundModifier,
- ) {
- if self.has_late_bound_regions.is_some() {
- return;
- }
- self.outer_index.shift_in(1);
- intravisit::walk_poly_trait_ref(self, tr, m);
- self.outer_index.shift_out(1);
- }
-
- fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) {
- if self.has_late_bound_regions.is_some() {
- return;
- }
-
- match self.tcx.named_region(lt.hir_id) {
- Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {}
- Some(rl::Region::LateBound(debruijn, _, _)) if debruijn < self.outer_index => {}
- Some(rl::Region::LateBound(..) | rl::Region::Free(..)) | None => {
- self.has_late_bound_regions = Some(lt.span);
- }
- }
- }
- }
-
- fn has_late_bound_regions<'tcx>(
- tcx: TyCtxt<'tcx>,
- generics: &'tcx hir::Generics<'tcx>,
- decl: &'tcx hir::FnDecl<'tcx>,
- ) -> Option<Span> {
- let mut visitor = LateBoundRegionsDetector {
- tcx,
- outer_index: ty::INNERMOST,
- has_late_bound_regions: None,
- };
- for param in generics.params {
- if let GenericParamKind::Lifetime { .. } = param.kind {
- if tcx.is_late_bound(param.hir_id) {
- return Some(param.span);
- }
- }
- }
- visitor.visit_fn_decl(decl);
- visitor.has_late_bound_regions
- }
-
- match node {
- Node::TraitItem(item) => match item.kind {
- hir::TraitItemKind::Fn(ref sig, _) => {
- has_late_bound_regions(tcx, &item.generics, sig.decl)
- }
- _ => None,
- },
- Node::ImplItem(item) => match item.kind {
- hir::ImplItemKind::Fn(ref sig, _) => {
- has_late_bound_regions(tcx, &item.generics, sig.decl)
- }
- _ => None,
- },
- Node::ForeignItem(item) => match item.kind {
- hir::ForeignItemKind::Fn(fn_decl, _, ref generics) => {
- has_late_bound_regions(tcx, generics, fn_decl)
- }
- _ => None,
- },
- Node::Item(item) => match item.kind {
- hir::ItemKind::Fn(ref sig, .., ref generics, _) => {
- has_late_bound_regions(tcx, generics, sig.decl)
- }
- _ => None,
- },
- _ => None,
- }
-}
-
-struct AnonConstInParamTyDetector {
- in_param_ty: bool,
- found_anon_const_in_param_ty: bool,
- ct: HirId,
-}
-
-impl<'v> Visitor<'v> for AnonConstInParamTyDetector {
- fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) {
- if let GenericParamKind::Const { ty, default: _ } = p.kind {
- let prev = self.in_param_ty;
- self.in_param_ty = true;
- self.visit_ty(ty);
- self.in_param_ty = prev;
- }
- }
-
- fn visit_anon_const(&mut self, c: &'v hir::AnonConst) {
- if self.in_param_ty && self.ct == c.hir_id {
- self.found_anon_const_in_param_ty = true;
- } else {
- intravisit::walk_anon_const(self, c)
- }
- }
-}
-
-fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
- use rustc_hir::*;
-
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
-
- let node = tcx.hir().get(hir_id);
- let parent_def_id = match node {
- Node::ImplItem(_)
- | Node::TraitItem(_)
- | Node::Variant(_)
- | Node::Ctor(..)
- | Node::Field(_) => {
- let parent_id = tcx.hir().get_parent_item(hir_id);
- Some(parent_id.to_def_id())
- }
- // FIXME(#43408) always enable this once `lazy_normalization` is
- // stable enough and does not need a feature gate anymore.
- Node::AnonConst(_) => {
- let parent_def_id = tcx.hir().get_parent_item(hir_id);
-
- let mut in_param_ty = false;
- for (_parent, node) in tcx.hir().parent_iter(hir_id) {
- if let Some(generics) = node.generics() {
- let mut visitor = AnonConstInParamTyDetector {
- in_param_ty: false,
- found_anon_const_in_param_ty: false,
- ct: hir_id,
- };
-
- visitor.visit_generics(generics);
- in_param_ty = visitor.found_anon_const_in_param_ty;
- break;
- }
- }
-
- if in_param_ty {
- // We do not allow generic parameters in anon consts if we are inside
- // of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed.
- None
- } else if tcx.lazy_normalization() {
- if let Some(param_id) = tcx.hir().opt_const_param_default_param_hir_id(hir_id) {
- // If the def_id we are calling generics_of on is an anon ct default i.e:
- //
- // struct Foo<const N: usize = { .. }>;
- // ^^^ ^ ^^^^^^ def id of this anon const
- // ^ ^ param_id
- // ^ parent_def_id
- //
- // then we only want to return generics for params to the left of `N`. If we don't do that we
- // end up with that const looking like: `ty::ConstKind::Unevaluated(def_id, substs: [N#0])`.
- //
- // This causes ICEs (#86580) when building the substs for Foo in `fn foo() -> Foo { .. }` as
- // we substitute the defaults with the partially built substs when we build the substs. Subst'ing
- // the `N#0` on the unevaluated const indexes into the empty substs we're in the process of building.
- //
- // We fix this by having this function return the parent's generics ourselves and truncating the
- // generics to only include non-forward declared params (with the exception of the `Self` ty)
- //
- // For the above code example that means we want `substs: []`
- // For the following struct def we want `substs: [N#0]` when generics_of is called on
- // the def id of the `{ N + 1 }` anon const
- // struct Foo<const N: usize, const M: usize = { N + 1 }>;
- //
- // This has some implications for how we get the predicates available to the anon const
- // see `explicit_predicates_of` for more information on this
- let generics = tcx.generics_of(parent_def_id.to_def_id());
- let param_def = tcx.hir().local_def_id(param_id).to_def_id();
- let param_def_idx = generics.param_def_id_to_index[&param_def];
- // In the above example this would be .params[..N#0]
- let params = generics.params[..param_def_idx as usize].to_owned();
- let param_def_id_to_index =
- params.iter().map(|param| (param.def_id, param.index)).collect();
-
- return ty::Generics {
- // we set the parent of these generics to be our parent's parent so that we
- // dont end up with substs: [N, M, N] for the const default on a struct like this:
- // struct Foo<const N: usize, const M: usize = { ... }>;
- parent: generics.parent,
- parent_count: generics.parent_count,
- params,
- param_def_id_to_index,
- has_self: generics.has_self,
- has_late_bound_regions: generics.has_late_bound_regions,
- };
- }
-
- // HACK(eddyb) this provides the correct generics when
- // `feature(generic_const_expressions)` is enabled, so that const expressions
- // used with const generics, e.g. `Foo<{N+1}>`, can work at all.
- //
- // Note that we do not supply the parent generics when using
- // `min_const_generics`.
- Some(parent_def_id.to_def_id())
- } else {
- let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
- match parent_node {
- // HACK(eddyb) this provides the correct generics for repeat
- // expressions' count (i.e. `N` in `[x; N]`), and explicit
- // `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`),
- // as they shouldn't be able to cause query cycle errors.
- Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
- if constant.hir_id() == hir_id =>
- {
- Some(parent_def_id.to_def_id())
- }
- Node::Variant(Variant { disr_expr: Some(ref constant), .. })
- if constant.hir_id == hir_id =>
- {
- Some(parent_def_id.to_def_id())
- }
- Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) => {
- Some(tcx.typeck_root_def_id(def_id))
- }
- // Exclude `GlobalAsm` here which cannot have generics.
- Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
- if asm.operands.iter().any(|(op, _op_sp)| match op {
- hir::InlineAsmOperand::Const { anon_const }
- | hir::InlineAsmOperand::SymFn { anon_const } => {
- anon_const.hir_id == hir_id
- }
- _ => false,
- }) =>
- {
- Some(parent_def_id.to_def_id())
- }
- _ => None,
- }
- }
- }
- Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => {
- Some(tcx.typeck_root_def_id(def_id))
- }
- Node::Item(item) => match item.kind {
- ItemKind::OpaqueTy(hir::OpaqueTy {
- origin:
- hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
- ..
- }) => Some(fn_def_id.to_def_id()),
- ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => {
- let parent_id = tcx.hir().get_parent_item(hir_id);
- assert_ne!(parent_id, CRATE_DEF_ID);
- debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id);
- // Opaque types are always nested within another item, and
- // inherit the generics of the item.
- Some(parent_id.to_def_id())
- }
- _ => None,
- },
- _ => None,
- };
-
- let no_generics = hir::Generics::empty();
- let ast_generics = node.generics().unwrap_or(&no_generics);
- let (opt_self, allow_defaults) = match node {
- Node::Item(item) => {
- match item.kind {
- ItemKind::Trait(..) | ItemKind::TraitAlias(..) => {
- // Add in the self type parameter.
- //
- // Something of a hack: use the node id for the trait, also as
- // the node id for the Self type parameter.
- let opt_self = Some(ty::GenericParamDef {
- index: 0,
- name: kw::SelfUpper,
- def_id,
- pure_wrt_drop: false,
- kind: ty::GenericParamDefKind::Type {
- has_default: false,
- object_lifetime_default: rl::Set1::Empty,
- synthetic: false,
- },
- });
-
- (opt_self, true)
- }
- ItemKind::TyAlias(..)
- | ItemKind::Enum(..)
- | ItemKind::Struct(..)
- | ItemKind::OpaqueTy(..)
- | ItemKind::Union(..) => (None, true),
- _ => (None, false),
- }
- }
- _ => (None, false),
- };
-
- let has_self = opt_self.is_some();
- let mut parent_has_self = false;
- let mut own_start = has_self as u32;
- let parent_count = parent_def_id.map_or(0, |def_id| {
- let generics = tcx.generics_of(def_id);
- assert!(!has_self);
- parent_has_self = generics.has_self;
- own_start = generics.count() as u32;
- generics.parent_count + generics.params.len()
- });
-
- let mut params: Vec<_> = Vec::with_capacity(ast_generics.params.len() + has_self as usize);
-
- if let Some(opt_self) = opt_self {
- params.push(opt_self);
- }
-
- let early_lifetimes = early_bound_lifetimes_from_generics(tcx, ast_generics);
- params.extend(early_lifetimes.enumerate().map(|(i, param)| ty::GenericParamDef {
- name: param.name.ident().name,
- index: own_start + i as u32,
- def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
- pure_wrt_drop: param.pure_wrt_drop,
- kind: ty::GenericParamDefKind::Lifetime,
- }));
-
- let object_lifetime_defaults = tcx.object_lifetime_defaults(hir_id.owner);
-
- // Now create the real type and const parameters.
- let type_start = own_start - has_self as u32 + params.len() as u32;
- let mut i = 0;
-
- params.extend(ast_generics.params.iter().filter_map(|param| match param.kind {
- GenericParamKind::Lifetime { .. } => None,
- GenericParamKind::Type { ref default, synthetic, .. } => {
- if !allow_defaults && default.is_some() {
- if !tcx.features().default_type_parameter_fallback {
- tcx.struct_span_lint_hir(
- lint::builtin::INVALID_TYPE_PARAM_DEFAULT,
- param.hir_id,
- param.span,
- |lint| {
- lint.build(
- "defaults for type parameters are only allowed in \
- `struct`, `enum`, `type`, or `trait` definitions",
- )
- .emit();
- },
- );
- }
- }
-
- let kind = ty::GenericParamDefKind::Type {
- has_default: default.is_some(),
- object_lifetime_default: object_lifetime_defaults
- .as_ref()
- .map_or(rl::Set1::Empty, |o| o[i]),
- synthetic,
- };
-
- let param_def = ty::GenericParamDef {
- index: type_start + i as u32,
- name: param.name.ident().name,
- def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
- pure_wrt_drop: param.pure_wrt_drop,
- kind,
- };
- i += 1;
- Some(param_def)
- }
- GenericParamKind::Const { default, .. } => {
- if !allow_defaults && default.is_some() {
- tcx.sess.span_err(
- param.span,
- "defaults for const parameters are only allowed in \
- `struct`, `enum`, `type`, or `trait` definitions",
- );
- }
-
- let param_def = ty::GenericParamDef {
- index: type_start + i as u32,
- name: param.name.ident().name,
- def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
- pure_wrt_drop: param.pure_wrt_drop,
- kind: ty::GenericParamDefKind::Const { has_default: default.is_some() },
- };
- i += 1;
- Some(param_def)
- }
- }));
-
- // provide junk type parameter defs - the only place that
- // cares about anything but the length is instantiation,
- // and we don't do that for closures.
- if let Node::Expr(&hir::Expr {
- kind: hir::ExprKind::Closure(hir::Closure { movability: gen, .. }),
- ..
- }) = node
- {
- let dummy_args = if gen.is_some() {
- &["<resume_ty>", "<yield_ty>", "<return_ty>", "<witness>", "<upvars>"][..]
- } else {
- &["<closure_kind>", "<closure_signature>", "<upvars>"][..]
- };
-
- params.extend(dummy_args.iter().enumerate().map(|(i, &arg)| ty::GenericParamDef {
- index: type_start + i as u32,
- name: Symbol::intern(arg),
- def_id,
- pure_wrt_drop: false,
- kind: ty::GenericParamDefKind::Type {
- has_default: false,
- object_lifetime_default: rl::Set1::Empty,
- synthetic: false,
- },
- }));
- }
-
- // provide junk type parameter defs for const blocks.
- if let Node::AnonConst(_) = node {
- let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
- if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node {
- params.push(ty::GenericParamDef {
- index: type_start,
- name: Symbol::intern("<const_ty>"),
- def_id,
- pure_wrt_drop: false,
- kind: ty::GenericParamDefKind::Type {
- has_default: false,
- object_lifetime_default: rl::Set1::Empty,
- synthetic: false,
- },
- });
- }
- }
-
- let param_def_id_to_index = params.iter().map(|param| (param.def_id, param.index)).collect();
-
- ty::Generics {
- parent: parent_def_id,
- parent_count,
- params,
- param_def_id_to_index,
- has_self: has_self || parent_has_self,
- has_late_bound_regions: has_late_bound_regions(tcx, node),
- }
-}
-
-fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool {
- generic_args.iter().any(|arg| match arg {
- hir::GenericArg::Type(ty) => is_suggestable_infer_ty(ty),
- hir::GenericArg::Infer(_) => true,
- _ => false,
- })
-}
-
-/// Whether `ty` is a type with `_` placeholders that can be inferred. Used in diagnostics only to
-/// use inference to provide suggestions for the appropriate type if possible.
-fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool {
- debug!(?ty);
- use hir::TyKind::*;
- match &ty.kind {
- Infer => true,
- Slice(ty) => is_suggestable_infer_ty(ty),
- Array(ty, length) => {
- is_suggestable_infer_ty(ty) || matches!(length, hir::ArrayLen::Infer(_, _))
- }
- Tup(tys) => tys.iter().any(is_suggestable_infer_ty),
- Ptr(mut_ty) | Rptr(_, mut_ty) => is_suggestable_infer_ty(mut_ty.ty),
- OpaqueDef(_, generic_args) => are_suggestable_generic_args(generic_args),
- Path(hir::QPath::TypeRelative(ty, segment)) => {
- is_suggestable_infer_ty(ty) || are_suggestable_generic_args(segment.args().args)
- }
- Path(hir::QPath::Resolved(ty_opt, hir::Path { segments, .. })) => {
- ty_opt.map_or(false, is_suggestable_infer_ty)
- || segments.iter().any(|segment| are_suggestable_generic_args(segment.args().args))
- }
- _ => false,
- }
-}
-
-pub fn get_infer_ret_ty<'hir>(output: &'hir hir::FnRetTy<'hir>) -> Option<&'hir hir::Ty<'hir>> {
- if let hir::FnRetTy::Return(ty) = output {
- if is_suggestable_infer_ty(ty) {
- return Some(&*ty);
- }
- }
- None
-}
-
-fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
- use rustc_hir::Node::*;
- use rustc_hir::*;
-
- let def_id = def_id.expect_local();
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
-
- let icx = ItemCtxt::new(tcx, def_id.to_def_id());
-
- match tcx.hir().get(hir_id) {
- TraitItem(hir::TraitItem {
- kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)),
- generics,
- ..
- })
- | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => {
- infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
- }
-
- ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
- // Do not try to inference the return type for a impl method coming from a trait
- if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) =
- tcx.hir().get(tcx.hir().get_parent_node(hir_id))
- && i.of_trait.is_some()
- {
- <dyn AstConv<'_>>::ty_of_fn(
- &icx,
- hir_id,
- sig.header.unsafety,
- sig.header.abi,
- sig.decl,
- Some(generics),
- None,
- )
- } else {
- infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
- }
- }
-
- TraitItem(hir::TraitItem {
- kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _),
- generics,
- ..
- }) => <dyn AstConv<'_>>::ty_of_fn(
- &icx,
- hir_id,
- header.unsafety,
- header.abi,
- decl,
- Some(generics),
- None,
- ),
-
- ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => {
- let abi = tcx.hir().get_foreign_abi(hir_id);
- compute_sig_of_foreign_fn_decl(tcx, def_id.to_def_id(), fn_decl, abi)
- }
-
- Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor_hir_id().is_some() => {
- let ty = tcx.type_of(tcx.hir().get_parent_item(hir_id));
- let inputs =
- data.fields().iter().map(|f| tcx.type_of(tcx.hir().local_def_id(f.hir_id)));
- ty::Binder::dummy(tcx.mk_fn_sig(
- inputs,
- ty,
- false,
- hir::Unsafety::Normal,
- abi::Abi::Rust,
- ))
- }
-
- Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => {
- // Closure signatures are not like other function
- // signatures and cannot be accessed through `fn_sig`. For
- // example, a closure signature excludes the `self`
- // argument. In any case they are embedded within the
- // closure type as part of the `ClosureSubsts`.
- //
- // To get the signature of a closure, you should use the
- // `sig` method on the `ClosureSubsts`:
- //
- // substs.as_closure().sig(def_id, tcx)
- bug!(
- "to get the signature of a closure, use `substs.as_closure().sig()` not `fn_sig()`",
- );
- }
-
- x => {
- bug!("unexpected sort of node in fn_sig(): {:?}", x);
- }
- }
-}
-
-fn infer_return_ty_for_fn_sig<'tcx>(
- tcx: TyCtxt<'tcx>,
- sig: &hir::FnSig<'_>,
- generics: &hir::Generics<'_>,
- def_id: LocalDefId,
- icx: &ItemCtxt<'tcx>,
-) -> ty::PolyFnSig<'tcx> {
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
-
- match get_infer_ret_ty(&sig.decl.output) {
- Some(ty) => {
- let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
- // Typeck doesn't expect erased regions to be returned from `type_of`.
- let fn_sig = tcx.fold_regions(fn_sig, |r, _| match *r {
- ty::ReErased => tcx.lifetimes.re_static,
- _ => r,
- });
- let fn_sig = ty::Binder::dummy(fn_sig);
-
- let mut visitor = HirPlaceholderCollector::default();
- visitor.visit_ty(ty);
- let mut diag = bad_placeholder(tcx, visitor.0, "return type");
- let ret_ty = fn_sig.skip_binder().output();
- if ret_ty.is_suggestable(tcx, false) {
- diag.span_suggestion(
- ty.span,
- "replace with the correct return type",
- ret_ty,
- Applicability::MachineApplicable,
- );
- } else if matches!(ret_ty.kind(), ty::FnDef(..)) {
- let fn_sig = ret_ty.fn_sig(tcx);
- if fn_sig
- .skip_binder()
- .inputs_and_output
- .iter()
- .all(|t| t.is_suggestable(tcx, false))
- {
- diag.span_suggestion(
- ty.span,
- "replace with the correct return type",
- fn_sig,
- Applicability::MachineApplicable,
- );
- }
- } else if ret_ty.is_closure() {
- // We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds
- // to prevent the user from getting a papercut while trying to use the unique closure
- // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`).
- diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
- diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html");
- }
- diag.emit();
-
- fn_sig
- }
- None => <dyn AstConv<'_>>::ty_of_fn(
- icx,
- hir_id,
- sig.header.unsafety,
- sig.header.abi,
- sig.decl,
- Some(generics),
- None,
- ),
- }
-}
-
-fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> {
- let icx = ItemCtxt::new(tcx, def_id);
- match tcx.hir().expect_item(def_id.expect_local()).kind {
- hir::ItemKind::Impl(ref impl_) => impl_.of_trait.as_ref().map(|ast_trait_ref| {
- let selfty = tcx.type_of(def_id);
- <dyn AstConv<'_>>::instantiate_mono_trait_ref(&icx, ast_trait_ref, selfty)
- }),
- _ => bug!(),
- }
-}
-
-fn impl_polarity(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ImplPolarity {
- let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl);
- let item = tcx.hir().expect_item(def_id.expect_local());
- match &item.kind {
- hir::ItemKind::Impl(hir::Impl {
- polarity: hir::ImplPolarity::Negative(span),
- of_trait,
- ..
- }) => {
- if is_rustc_reservation {
- let span = span.to(of_trait.as_ref().map_or(*span, |t| t.path.span));
- tcx.sess.span_err(span, "reservation impls can't be negative");
- }
- ty::ImplPolarity::Negative
- }
- hir::ItemKind::Impl(hir::Impl {
- polarity: hir::ImplPolarity::Positive,
- of_trait: None,
- ..
- }) => {
- if is_rustc_reservation {
- tcx.sess.span_err(item.span, "reservation impls can't be inherent");
- }
- ty::ImplPolarity::Positive
- }
- hir::ItemKind::Impl(hir::Impl {
- polarity: hir::ImplPolarity::Positive,
- of_trait: Some(_),
- ..
- }) => {
- if is_rustc_reservation {
- ty::ImplPolarity::Reservation
- } else {
- ty::ImplPolarity::Positive
- }
- }
- item => bug!("impl_polarity: {:?} not an impl", item),
- }
-}
-
-/// Returns the early-bound lifetimes declared in this generics
-/// listing. For anything other than fns/methods, this is just all
-/// the lifetimes that are declared. For fns or methods, we have to
-/// screen out those that do not appear in any where-clauses etc using
-/// `resolve_lifetime::early_bound_lifetimes`.
-fn early_bound_lifetimes_from_generics<'a, 'tcx: 'a>(
- tcx: TyCtxt<'tcx>,
- generics: &'a hir::Generics<'a>,
-) -> impl Iterator<Item = &'a hir::GenericParam<'a>> + Captures<'tcx> {
- generics.params.iter().filter(move |param| match param.kind {
- GenericParamKind::Lifetime { .. } => !tcx.is_late_bound(param.hir_id),
- _ => false,
- })
-}
-
-/// Returns a list of type predicates for the definition with ID `def_id`, including inferred
-/// lifetime constraints. This includes all predicates returned by `explicit_predicates_of`, plus
-/// inferred constraints concerning which regions outlive other regions.
-fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
- debug!("predicates_defined_on({:?})", def_id);
- let mut result = tcx.explicit_predicates_of(def_id);
- debug!("predicates_defined_on: explicit_predicates_of({:?}) = {:?}", def_id, result,);
- let inferred_outlives = tcx.inferred_outlives_of(def_id);
- if !inferred_outlives.is_empty() {
- debug!(
- "predicates_defined_on: inferred_outlives_of({:?}) = {:?}",
- def_id, inferred_outlives,
- );
- if result.predicates.is_empty() {
- result.predicates = inferred_outlives;
- } else {
- result.predicates = tcx
- .arena
- .alloc_from_iter(result.predicates.iter().chain(inferred_outlives).copied());
- }
- }
-
- debug!("predicates_defined_on({:?}) = {:?}", def_id, result);
- result
-}
-
-/// Returns a list of all type predicates (explicit and implicit) for the definition with
-/// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus
-/// `Self: Trait` predicates for traits.
-fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
- let mut result = tcx.predicates_defined_on(def_id);
-
- if tcx.is_trait(def_id) {
- // For traits, add `Self: Trait` predicate. This is
- // not part of the predicates that a user writes, but it
- // is something that one must prove in order to invoke a
- // method or project an associated type.
- //
- // In the chalk setup, this predicate is not part of the
- // "predicates" for a trait item. But it is useful in
- // rustc because if you directly (e.g.) invoke a trait
- // method like `Trait::method(...)`, you must naturally
- // prove that the trait applies to the types that were
- // used, and adding the predicate into this list ensures
- // that this is done.
- //
- // We use a DUMMY_SP here as a way to signal trait bounds that come
- // from the trait itself that *shouldn't* be shown as the source of
- // an obligation and instead be skipped. Otherwise we'd use
- // `tcx.def_span(def_id);`
-
- let constness = if tcx.has_attr(def_id, sym::const_trait) {
- ty::BoundConstness::ConstIfConst
- } else {
- ty::BoundConstness::NotConst
- };
-
- let span = rustc_span::DUMMY_SP;
- result.predicates =
- tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once((
- ty::TraitRef::identity(tcx, def_id).with_constness(constness).to_predicate(tcx),
- span,
- ))));
- }
- debug!("predicates_of(def_id={:?}) = {:?}", def_id, result);
- result
-}
-
-/// Returns a list of user-specified type predicates for the definition with ID `def_id`.
-/// N.B., this does not include any implied/inferred constraints.
-fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
- use rustc_hir::*;
-
- debug!("explicit_predicates_of(def_id={:?})", def_id);
-
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
- let node = tcx.hir().get(hir_id);
-
- let mut is_trait = None;
- let mut is_default_impl_trait = None;
-
- let icx = ItemCtxt::new(tcx, def_id);
-
- const NO_GENERICS: &hir::Generics<'_> = hir::Generics::empty();
-
- // We use an `IndexSet` to preserves order of insertion.
- // Preserving the order of insertion is important here so as not to break UI tests.
- let mut predicates: FxIndexSet<(ty::Predicate<'_>, Span)> = FxIndexSet::default();
-
- let ast_generics = match node {
- Node::TraitItem(item) => item.generics,
-
- Node::ImplItem(item) => item.generics,
-
- Node::Item(item) => {
- match item.kind {
- ItemKind::Impl(ref impl_) => {
- if impl_.defaultness.is_default() {
- is_default_impl_trait = tcx.impl_trait_ref(def_id).map(ty::Binder::dummy);
- }
- &impl_.generics
- }
- ItemKind::Fn(.., ref generics, _)
- | ItemKind::TyAlias(_, ref generics)
- | ItemKind::Enum(_, ref generics)
- | ItemKind::Struct(_, ref generics)
- | ItemKind::Union(_, ref generics) => *generics,
-
- ItemKind::Trait(_, _, ref generics, ..) => {
- is_trait = Some(ty::TraitRef::identity(tcx, def_id));
- *generics
- }
- ItemKind::TraitAlias(ref generics, _) => {
- is_trait = Some(ty::TraitRef::identity(tcx, def_id));
- *generics
- }
- ItemKind::OpaqueTy(OpaqueTy {
- origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
- ..
- }) => {
- // return-position impl trait
- //
- // We don't inherit predicates from the parent here:
- // If we have, say `fn f<'a, T: 'a>() -> impl Sized {}`
- // then the return type is `f::<'static, T>::{{opaque}}`.
- //
- // If we inherited the predicates of `f` then we would
- // require that `T: 'static` to show that the return
- // type is well-formed.
- //
- // The only way to have something with this opaque type
- // is from the return type of the containing function,
- // which will ensure that the function's predicates
- // hold.
- return ty::GenericPredicates { parent: None, predicates: &[] };
- }
- ItemKind::OpaqueTy(OpaqueTy {
- ref generics,
- origin: hir::OpaqueTyOrigin::TyAlias,
- ..
- }) => {
- // type-alias impl trait
- generics
- }
-
- _ => NO_GENERICS,
- }
- }
-
- Node::ForeignItem(item) => match item.kind {
- ForeignItemKind::Static(..) => NO_GENERICS,
- ForeignItemKind::Fn(_, _, ref generics) => *generics,
- ForeignItemKind::Type => NO_GENERICS,
- },
-
- _ => NO_GENERICS,
- };
-
- let generics = tcx.generics_of(def_id);
- let parent_count = generics.parent_count as u32;
- let has_own_self = generics.has_self && parent_count == 0;
-
- // Below we'll consider the bounds on the type parameters (including `Self`)
- // and the explicit where-clauses, but to get the full set of predicates
- // on a trait we need to add in the supertrait bounds and bounds found on
- // associated types.
- if let Some(_trait_ref) = is_trait {
- predicates.extend(tcx.super_predicates_of(def_id).predicates.iter().cloned());
- }
-
- // In default impls, we can assume that the self type implements
- // the trait. So in:
- //
- // default impl Foo for Bar { .. }
- //
- // we add a default where clause `Foo: Bar`. We do a similar thing for traits
- // (see below). Recall that a default impl is not itself an impl, but rather a
- // set of defaults that can be incorporated into another impl.
- if let Some(trait_ref) = is_default_impl_trait {
- predicates.insert((trait_ref.without_const().to_predicate(tcx), tcx.def_span(def_id)));
- }
-
- // Collect the region predicates that were declared inline as
- // well. In the case of parameters declared on a fn or method, we
- // have to be careful to only iterate over early-bound regions.
- let mut index = parent_count
- + has_own_self as u32
- + early_bound_lifetimes_from_generics(tcx, ast_generics).count() as u32;
-
- // Collect the predicates that were written inline by the user on each
- // type parameter (e.g., `<T: Foo>`).
- for param in ast_generics.params {
- match param.kind {
- // We already dealt with early bound lifetimes above.
- GenericParamKind::Lifetime { .. } => (),
- GenericParamKind::Type { .. } => {
- let name = param.name.ident().name;
- let param_ty = ty::ParamTy::new(index, name).to_ty(tcx);
- index += 1;
-
- let mut bounds = Bounds::default();
- // Params are implicitly sized unless a `?Sized` bound is found
- <dyn AstConv<'_>>::add_implicitly_sized(
- &icx,
- &mut bounds,
- &[],
- Some((param.hir_id, ast_generics.predicates)),
- param.span,
- );
- predicates.extend(bounds.predicates(tcx, param_ty));
- }
- GenericParamKind::Const { .. } => {
- // Bounds on const parameters are currently not possible.
- index += 1;
- }
- }
- }
-
- // Add in the bounds that appear in the where-clause.
- for predicate in ast_generics.predicates {
- match predicate {
- hir::WherePredicate::BoundPredicate(bound_pred) => {
- let ty = icx.to_ty(bound_pred.bounded_ty);
- let bound_vars = icx.tcx.late_bound_vars(bound_pred.bounded_ty.hir_id);
-
- // Keep the type around in a dummy predicate, in case of no bounds.
- // That way, `where Ty:` is not a complete noop (see #53696) and `Ty`
- // is still checked for WF.
- if bound_pred.bounds.is_empty() {
- if let ty::Param(_) = ty.kind() {
- // This is a `where T:`, which can be in the HIR from the
- // transformation that moves `?Sized` to `T`'s declaration.
- // We can skip the predicate because type parameters are
- // trivially WF, but also we *should*, to avoid exposing
- // users who never wrote `where Type:,` themselves, to
- // compiler/tooling bugs from not handling WF predicates.
- } else {
- let span = bound_pred.bounded_ty.span;
- let predicate = ty::Binder::bind_with_vars(
- ty::PredicateKind::WellFormed(ty.into()),
- bound_vars,
- );
- predicates.insert((predicate.to_predicate(tcx), span));
- }
- }
-
- let mut bounds = Bounds::default();
- <dyn AstConv<'_>>::add_bounds(
- &icx,
- ty,
- bound_pred.bounds.iter(),
- &mut bounds,
- bound_vars,
- );
- predicates.extend(bounds.predicates(tcx, ty));
- }
-
- hir::WherePredicate::RegionPredicate(region_pred) => {
- let r1 = <dyn AstConv<'_>>::ast_region_to_region(&icx, &region_pred.lifetime, None);
- predicates.extend(region_pred.bounds.iter().map(|bound| {
- let (r2, span) = match bound {
- hir::GenericBound::Outlives(lt) => {
- (<dyn AstConv<'_>>::ast_region_to_region(&icx, lt, None), lt.span)
- }
- _ => bug!(),
- };
- let pred = ty::Binder::dummy(ty::PredicateKind::RegionOutlives(
- ty::OutlivesPredicate(r1, r2),
- ))
- .to_predicate(icx.tcx);
-
- (pred, span)
- }))
- }
-
- hir::WherePredicate::EqPredicate(..) => {
- // FIXME(#20041)
- }
- }
- }
-
- if tcx.features().generic_const_exprs {
- predicates.extend(const_evaluatable_predicates_of(tcx, def_id.expect_local()));
- }
-
- let mut predicates: Vec<_> = predicates.into_iter().collect();
-
- // Subtle: before we store the predicates into the tcx, we
- // sort them so that predicates like `T: Foo<Item=U>` come
- // before uses of `U`. This avoids false ambiguity errors
- // in trait checking. See `setup_constraining_predicates`
- // for details.
- if let Node::Item(&Item { kind: ItemKind::Impl { .. }, .. }) = node {
- let self_ty = tcx.type_of(def_id);
- let trait_ref = tcx.impl_trait_ref(def_id);
- cgp::setup_constraining_predicates(
- tcx,
- &mut predicates,
- trait_ref,
- &mut cgp::parameters_for_impl(self_ty, trait_ref),
- );
- }
-
- let result = ty::GenericPredicates {
- parent: generics.parent,
- predicates: tcx.arena.alloc_from_iter(predicates),
- };
- debug!("explicit_predicates_of(def_id={:?}) = {:?}", def_id, result);
- result
-}
-
-fn const_evaluatable_predicates_of<'tcx>(
- tcx: TyCtxt<'tcx>,
- def_id: LocalDefId,
-) -> FxIndexSet<(ty::Predicate<'tcx>, Span)> {
- struct ConstCollector<'tcx> {
- tcx: TyCtxt<'tcx>,
- preds: FxIndexSet<(ty::Predicate<'tcx>, Span)>,
- }
-
- impl<'tcx> intravisit::Visitor<'tcx> for ConstCollector<'tcx> {
- fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
- let def_id = self.tcx.hir().local_def_id(c.hir_id);
- let ct = ty::Const::from_anon_const(self.tcx, def_id);
- if let ty::ConstKind::Unevaluated(uv) = ct.kind() {
- assert_eq!(uv.promoted, None);
- let span = self.tcx.hir().span(c.hir_id);
- self.preds.insert((
- ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(uv.shrink()))
- .to_predicate(self.tcx),
- span,
- ));
- }
- }
-
- fn visit_const_param_default(&mut self, _param: HirId, _ct: &'tcx hir::AnonConst) {
- // Do not look into const param defaults,
- // these get checked when they are actually instantiated.
- //
- // We do not want the following to error:
- //
- // struct Foo<const N: usize, const M: usize = { N + 1 }>;
- // struct Bar<const N: usize>(Foo<N, 3>);
- }
- }
-
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
- let node = tcx.hir().get(hir_id);
-
- let mut collector = ConstCollector { tcx, preds: FxIndexSet::default() };
- if let hir::Node::Item(item) = node && let hir::ItemKind::Impl(ref impl_) = item.kind {
- if let Some(of_trait) = &impl_.of_trait {
- debug!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id);
- collector.visit_trait_ref(of_trait);
- }
-
- debug!("const_evaluatable_predicates_of({:?}): visit_self_ty", def_id);
- collector.visit_ty(impl_.self_ty);
- }
-
- if let Some(generics) = node.generics() {
- debug!("const_evaluatable_predicates_of({:?}): visit_generics", def_id);
- collector.visit_generics(generics);
- }
-
- if let Some(fn_sig) = tcx.hir().fn_sig_by_hir_id(hir_id) {
- debug!("const_evaluatable_predicates_of({:?}): visit_fn_decl", def_id);
- collector.visit_fn_decl(fn_sig.decl);
- }
- debug!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.preds);
-
- collector.preds
-}
-
-fn trait_explicit_predicates_and_bounds(
- tcx: TyCtxt<'_>,
- def_id: LocalDefId,
-) -> ty::GenericPredicates<'_> {
- assert_eq!(tcx.def_kind(def_id), DefKind::Trait);
- gather_explicit_predicates_of(tcx, def_id.to_def_id())
-}
-
-fn explicit_predicates_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::GenericPredicates<'tcx> {
- let def_kind = tcx.def_kind(def_id);
- if let DefKind::Trait = def_kind {
- // Remove bounds on associated types from the predicates, they will be
- // returned by `explicit_item_bounds`.
- let predicates_and_bounds = tcx.trait_explicit_predicates_and_bounds(def_id.expect_local());
- let trait_identity_substs = InternalSubsts::identity_for_item(tcx, def_id);
-
- let is_assoc_item_ty = |ty: Ty<'tcx>| {
- // For a predicate from a where clause to become a bound on an
- // associated type:
- // * It must use the identity substs of the item.
- // * Since any generic parameters on the item are not in scope,
- // this means that the item is not a GAT, and its identity
- // substs are the same as the trait's.
- // * It must be an associated type for this trait (*not* a
- // supertrait).
- if let ty::Projection(projection) = ty.kind() {
- projection.substs == trait_identity_substs
- && tcx.associated_item(projection.item_def_id).container_id(tcx) == def_id
- } else {
- false
- }
- };
-
- let predicates: Vec<_> = predicates_and_bounds
- .predicates
- .iter()
- .copied()
- .filter(|(pred, _)| match pred.kind().skip_binder() {
- ty::PredicateKind::Trait(tr) => !is_assoc_item_ty(tr.self_ty()),
- ty::PredicateKind::Projection(proj) => {
- !is_assoc_item_ty(proj.projection_ty.self_ty())
- }
- ty::PredicateKind::TypeOutlives(outlives) => !is_assoc_item_ty(outlives.0),
- _ => true,
- })
- .collect();
- if predicates.len() == predicates_and_bounds.predicates.len() {
- predicates_and_bounds
- } else {
- ty::GenericPredicates {
- parent: predicates_and_bounds.parent,
- predicates: tcx.arena.alloc_slice(&predicates),
- }
- }
- } else {
- if matches!(def_kind, DefKind::AnonConst) && tcx.lazy_normalization() {
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
- if tcx.hir().opt_const_param_default_param_hir_id(hir_id).is_some() {
- // In `generics_of` we set the generics' parent to be our parent's parent which means that
- // we lose out on the predicates of our actual parent if we dont return those predicates here.
- // (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
- //
- // struct Foo<T, const N: usize = { <T as Trait>::ASSOC }>(T) where T: Trait;
- // ^^^ ^^^^^^^^^^^^^^^^^^^^^^^ the def id we are calling
- // ^^^ explicit_predicates_of on
- // parent item we dont have set as the
- // parent of generics returned by `generics_of`
- //
- // In the above code we want the anon const to have predicates in its param env for `T: Trait`
- let item_def_id = tcx.hir().get_parent_item(hir_id);
- // In the above code example we would be calling `explicit_predicates_of(Foo)` here
- return tcx.explicit_predicates_of(item_def_id);
- }
- }
- gather_explicit_predicates_of(tcx, def_id)
- }
-}
-
-/// Converts a specific `GenericBound` from the AST into a set of
-/// predicates that apply to the self type. A vector is returned
-/// because this can be anywhere from zero predicates (`T: ?Sized` adds no
-/// predicates) to one (`T: Foo`) to many (`T: Bar<X = i32>` adds `T: Bar`
-/// and `<T as Bar>::X == i32`).
-fn predicates_from_bound<'tcx>(
- astconv: &dyn AstConv<'tcx>,
- param_ty: Ty<'tcx>,
- bound: &'tcx hir::GenericBound<'tcx>,
- bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
-) -> Vec<(ty::Predicate<'tcx>, Span)> {
- let mut bounds = Bounds::default();
- astconv.add_bounds(param_ty, [bound].into_iter(), &mut bounds, bound_vars);
- bounds.predicates(astconv.tcx(), param_ty).collect()
-}
-
-fn compute_sig_of_foreign_fn_decl<'tcx>(
- tcx: TyCtxt<'tcx>,
- def_id: DefId,
- decl: &'tcx hir::FnDecl<'tcx>,
- abi: abi::Abi,
-) -> ty::PolyFnSig<'tcx> {
- let unsafety = if abi == abi::Abi::RustIntrinsic {
- intrinsic_operation_unsafety(tcx.item_name(def_id))
- } else {
- hir::Unsafety::Unsafe
- };
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
- let fty = <dyn AstConv<'_>>::ty_of_fn(
- &ItemCtxt::new(tcx, def_id),
- hir_id,
- unsafety,
- abi,
- decl,
- None,
- None,
- );
-
- // Feature gate SIMD types in FFI, since I am not sure that the
- // ABIs are handled at all correctly. -huonw
- if abi != abi::Abi::RustIntrinsic
- && abi != abi::Abi::PlatformIntrinsic
- && !tcx.features().simd_ffi
- {
- let check = |ast_ty: &hir::Ty<'_>, ty: Ty<'_>| {
- if ty.is_simd() {
- let snip = tcx
- .sess
- .source_map()
- .span_to_snippet(ast_ty.span)
- .map_or_else(|_| String::new(), |s| format!(" `{}`", s));
- tcx.sess
- .struct_span_err(
- ast_ty.span,
- &format!(
- "use of SIMD type{} in FFI is highly experimental and \
- may result in invalid code",
- snip
- ),
- )
- .help("add `#![feature(simd_ffi)]` to the crate attributes to enable")
- .emit();
- }
- };
- for (input, ty) in iter::zip(decl.inputs, fty.inputs().skip_binder()) {
- check(input, *ty)
- }
- if let hir::FnRetTy::Return(ref ty) = decl.output {
- check(ty, fty.output().skip_binder())
- }
- }
-
- fty
-}
-
-fn is_foreign_item(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
- match tcx.hir().get_if_local(def_id) {
- Some(Node::ForeignItem(..)) => true,
- Some(_) => false,
- _ => bug!("is_foreign_item applied to non-local def-id {:?}", def_id),
- }
-}
-
-fn generator_kind(tcx: TyCtxt<'_>, def_id: DefId) -> Option<hir::GeneratorKind> {
- match tcx.hir().get_if_local(def_id) {
- Some(Node::Expr(&rustc_hir::Expr {
- kind: rustc_hir::ExprKind::Closure(&rustc_hir::Closure { body, .. }),
- ..
- })) => tcx.hir().body(body).generator_kind(),
- Some(_) => None,
- _ => bug!("generator_kind applied to non-local def-id {:?}", def_id),
- }
-}
-
-fn from_target_feature(
- tcx: TyCtxt<'_>,
- attr: &ast::Attribute,
- supported_target_features: &FxHashMap<String, Option<Symbol>>,
- target_features: &mut Vec<Symbol>,
-) {
- let Some(list) = attr.meta_item_list() else { return };
- let bad_item = |span| {
- let msg = "malformed `target_feature` attribute input";
- let code = "enable = \"..\"";
- tcx.sess
- .struct_span_err(span, msg)
- .span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders)
- .emit();
- };
- let rust_features = tcx.features();
- for item in list {
- // Only `enable = ...` is accepted in the meta-item list.
- if !item.has_name(sym::enable) {
- bad_item(item.span());
- continue;
- }
-
- // Must be of the form `enable = "..."` (a string).
- let Some(value) = item.value_str() else {
- bad_item(item.span());
- continue;
- };
-
- // We allow comma separation to enable multiple features.
- target_features.extend(value.as_str().split(',').filter_map(|feature| {
- let Some(feature_gate) = supported_target_features.get(feature) else {
- let msg =
- format!("the feature named `{}` is not valid for this target", feature);
- let mut err = tcx.sess.struct_span_err(item.span(), &msg);
- err.span_label(
- item.span(),
- format!("`{}` is not valid for this target", feature),
- );
- if let Some(stripped) = feature.strip_prefix('+') {
- let valid = supported_target_features.contains_key(stripped);
- if valid {
- err.help("consider removing the leading `+` in the feature name");
- }
- }
- err.emit();
- return None;
- };
-
- // Only allow features whose feature gates have been enabled.
- let allowed = match feature_gate.as_ref().copied() {
- Some(sym::arm_target_feature) => rust_features.arm_target_feature,
- Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature,
- Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature,
- Some(sym::mips_target_feature) => rust_features.mips_target_feature,
- Some(sym::riscv_target_feature) => rust_features.riscv_target_feature,
- Some(sym::avx512_target_feature) => rust_features.avx512_target_feature,
- Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature,
- Some(sym::tbm_target_feature) => rust_features.tbm_target_feature,
- Some(sym::wasm_target_feature) => rust_features.wasm_target_feature,
- Some(sym::cmpxchg16b_target_feature) => rust_features.cmpxchg16b_target_feature,
- Some(sym::movbe_target_feature) => rust_features.movbe_target_feature,
- Some(sym::rtm_target_feature) => rust_features.rtm_target_feature,
- Some(sym::f16c_target_feature) => rust_features.f16c_target_feature,
- Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature,
- Some(sym::bpf_target_feature) => rust_features.bpf_target_feature,
- Some(sym::aarch64_ver_target_feature) => rust_features.aarch64_ver_target_feature,
- Some(name) => bug!("unknown target feature gate {}", name),
- None => true,
- };
- if !allowed {
- feature_err(
- &tcx.sess.parse_sess,
- feature_gate.unwrap(),
- item.span(),
- &format!("the target feature `{}` is currently unstable", feature),
- )
- .emit();
- }
- Some(Symbol::intern(feature))
- }));
- }
-}
-
-fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
- use rustc_middle::mir::mono::Linkage::*;
-
- // Use the names from src/llvm/docs/LangRef.rst here. Most types are only
- // applicable to variable declarations and may not really make sense for
- // Rust code in the first place but allow them anyway and trust that the
- // user knows what they're doing. Who knows, unanticipated use cases may pop
- // up in the future.
- //
- // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
- // and don't have to be, LLVM treats them as no-ops.
- match name {
- "appending" => Appending,
- "available_externally" => AvailableExternally,
- "common" => Common,
- "extern_weak" => ExternalWeak,
- "external" => External,
- "internal" => Internal,
- "linkonce" => LinkOnceAny,
- "linkonce_odr" => LinkOnceODR,
- "private" => Private,
- "weak" => WeakAny,
- "weak_odr" => WeakODR,
- _ => tcx.sess.span_fatal(tcx.def_span(def_id), "invalid linkage specified"),
- }
-}
-
-fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
- if cfg!(debug_assertions) {
- let def_kind = tcx.def_kind(did);
- assert!(
- def_kind.has_codegen_attrs(),
- "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}",
- );
- }
-
- let did = did.expect_local();
- let attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(did));
- let mut codegen_fn_attrs = CodegenFnAttrs::new();
- if tcx.should_inherit_track_caller(did) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
- }
-
- // The panic_no_unwind function called by TerminatorKind::Abort will never
- // unwind. If the panic handler that it invokes unwind then it will simply
- // call the panic handler again.
- if Some(did.to_def_id()) == tcx.lang_items().panic_no_unwind() {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
- }
-
- let supported_target_features = tcx.supported_target_features(LOCAL_CRATE);
-
- let mut inline_span = None;
- let mut link_ordinal_span = None;
- let mut no_sanitize_span = None;
- for attr in attrs.iter() {
- if attr.has_name(sym::cold) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
- } else if attr.has_name(sym::rustc_allocator) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR;
- } else if attr.has_name(sym::ffi_returns_twice) {
- if tcx.is_foreign_item(did) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE;
- } else {
- // `#[ffi_returns_twice]` is only allowed `extern fn`s.
- struct_span_err!(
- tcx.sess,
- attr.span,
- E0724,
- "`#[ffi_returns_twice]` may only be used on foreign functions"
- )
- .emit();
- }
- } else if attr.has_name(sym::ffi_pure) {
- if tcx.is_foreign_item(did) {
- if attrs.iter().any(|a| a.has_name(sym::ffi_const)) {
- // `#[ffi_const]` functions cannot be `#[ffi_pure]`
- struct_span_err!(
- tcx.sess,
- attr.span,
- E0757,
- "`#[ffi_const]` function cannot be `#[ffi_pure]`"
- )
- .emit();
- } else {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE;
- }
- } else {
- // `#[ffi_pure]` is only allowed on foreign functions
- struct_span_err!(
- tcx.sess,
- attr.span,
- E0755,
- "`#[ffi_pure]` may only be used on foreign functions"
- )
- .emit();
- }
- } else if attr.has_name(sym::ffi_const) {
- if tcx.is_foreign_item(did) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST;
- } else {
- // `#[ffi_const]` is only allowed on foreign functions
- struct_span_err!(
- tcx.sess,
- attr.span,
- E0756,
- "`#[ffi_const]` may only be used on foreign functions"
- )
- .emit();
- }
- } else if attr.has_name(sym::rustc_allocator_nounwind) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
- } else if attr.has_name(sym::rustc_reallocator) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR;
- } else if attr.has_name(sym::rustc_deallocator) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR;
- } else if attr.has_name(sym::rustc_allocator_zeroed) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED;
- } else if attr.has_name(sym::naked) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
- } else if attr.has_name(sym::no_mangle) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
- } else if attr.has_name(sym::no_coverage) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
- } else if attr.has_name(sym::rustc_std_internal_symbol) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
- } else if attr.has_name(sym::used) {
- let inner = attr.meta_item_list();
- match inner.as_deref() {
- Some([item]) if item.has_name(sym::linker) => {
- if !tcx.features().used_with_arg {
- feature_err(
- &tcx.sess.parse_sess,
- sym::used_with_arg,
- attr.span,
- "`#[used(linker)]` is currently unstable",
- )
- .emit();
- }
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER;
- }
- Some([item]) if item.has_name(sym::compiler) => {
- if !tcx.features().used_with_arg {
- feature_err(
- &tcx.sess.parse_sess,
- sym::used_with_arg,
- attr.span,
- "`#[used(compiler)]` is currently unstable",
- )
- .emit();
- }
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
- }
- Some(_) => {
- tcx.sess
- .struct_span_err(
- attr.span,
- "expected `used`, `used(compiler)` or `used(linker)`",
- )
- .emit();
- }
- None => {
- // Unfortunately, unconditionally using `llvm.used` causes
- // issues in handling `.init_array` with the gold linker,
- // but using `llvm.compiler.used` caused a nontrival amount
- // of unintentional ecosystem breakage -- particularly on
- // Mach-O targets.
- //
- // As a result, we emit `llvm.compiler.used` only on ELF
- // targets. This is somewhat ad-hoc, but actually follows
- // our pre-LLVM 13 behavior (prior to the ecosystem
- // breakage), and seems to match `clang`'s behavior as well
- // (both before and after LLVM 13), possibly because they
- // have similar compatibility concerns to us. See
- // https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146
- // and following comments for some discussion of this, as
- // well as the comments in `rustc_codegen_llvm` where these
- // flags are handled.
- //
- // Anyway, to be clear: this is still up in the air
- // somewhat, and is subject to change in the future (which
- // is a good thing, because this would ideally be a bit
- // more firmed up).
- let is_like_elf = !(tcx.sess.target.is_like_osx
- || tcx.sess.target.is_like_windows
- || tcx.sess.target.is_like_wasm);
- codegen_fn_attrs.flags |= if is_like_elf {
- CodegenFnAttrFlags::USED
- } else {
- CodegenFnAttrFlags::USED_LINKER
- };
- }
- }
- } else if attr.has_name(sym::cmse_nonsecure_entry) {
- if !matches!(tcx.fn_sig(did).abi(), abi::Abi::C { .. }) {
- struct_span_err!(
- tcx.sess,
- attr.span,
- E0776,
- "`#[cmse_nonsecure_entry]` requires C ABI"
- )
- .emit();
- }
- if !tcx.sess.target.llvm_target.contains("thumbv8m") {
- struct_span_err!(tcx.sess, attr.span, E0775, "`#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M extension")
- .emit();
- }
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY;
- } else if attr.has_name(sym::thread_local) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL;
- } else if attr.has_name(sym::track_caller) {
- if !tcx.is_closure(did.to_def_id()) && tcx.fn_sig(did).abi() != abi::Abi::Rust {
- struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI")
- .emit();
- }
- if tcx.is_closure(did.to_def_id()) && !tcx.features().closure_track_caller {
- feature_err(
- &tcx.sess.parse_sess,
- sym::closure_track_caller,
- attr.span,
- "`#[track_caller]` on closures is currently unstable",
- )
- .emit();
- }
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
- } else if attr.has_name(sym::export_name) {
- if let Some(s) = attr.value_str() {
- if s.as_str().contains('\0') {
- // `#[export_name = ...]` will be converted to a null-terminated string,
- // so it may not contain any null characters.
- struct_span_err!(
- tcx.sess,
- attr.span,
- E0648,
- "`export_name` may not contain null characters"
- )
- .emit();
- }
- codegen_fn_attrs.export_name = Some(s);
- }
- } else if attr.has_name(sym::target_feature) {
- if !tcx.is_closure(did.to_def_id())
- && tcx.fn_sig(did).unsafety() == hir::Unsafety::Normal
- {
- if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
- // The `#[target_feature]` attribute is allowed on
- // WebAssembly targets on all functions, including safe
- // ones. Other targets require that `#[target_feature]` is
- // only applied to unsafe functions (pending the
- // `target_feature_11` feature) because on most targets
- // execution of instructions that are not supported is
- // considered undefined behavior. For WebAssembly which is a
- // 100% safe target at execution time it's not possible to
- // execute undefined instructions, and even if a future
- // feature was added in some form for this it would be a
- // deterministic trap. There is no undefined behavior when
- // executing WebAssembly so `#[target_feature]` is allowed
- // on safe functions (but again, only for WebAssembly)
- //
- // Note that this is also allowed if `actually_rustdoc` so
- // if a target is documenting some wasm-specific code then
- // it's not spuriously denied.
- } else if !tcx.features().target_feature_11 {
- let mut err = feature_err(
- &tcx.sess.parse_sess,
- sym::target_feature_11,
- attr.span,
- "`#[target_feature(..)]` can only be applied to `unsafe` functions",
- );
- err.span_label(tcx.def_span(did), "not an `unsafe` function");
- err.emit();
- } else {
- check_target_feature_trait_unsafe(tcx, did, attr.span);
- }
- }
- from_target_feature(
- tcx,
- attr,
- supported_target_features,
- &mut codegen_fn_attrs.target_features,
- );
- } else if attr.has_name(sym::linkage) {
- if let Some(val) = attr.value_str() {
- codegen_fn_attrs.linkage = Some(linkage_by_name(tcx, did, val.as_str()));
- }
- } else if attr.has_name(sym::link_section) {
- if let Some(val) = attr.value_str() {
- if val.as_str().bytes().any(|b| b == 0) {
- let msg = format!(
- "illegal null byte in link_section \
- value: `{}`",
- &val
- );
- tcx.sess.span_err(attr.span, &msg);
- } else {
- codegen_fn_attrs.link_section = Some(val);
- }
- }
- } else if attr.has_name(sym::link_name) {
- codegen_fn_attrs.link_name = attr.value_str();
- } else if attr.has_name(sym::link_ordinal) {
- link_ordinal_span = Some(attr.span);
- if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
- codegen_fn_attrs.link_ordinal = ordinal;
- }
- } else if attr.has_name(sym::no_sanitize) {
- no_sanitize_span = Some(attr.span);
- if let Some(list) = attr.meta_item_list() {
- for item in list.iter() {
- if item.has_name(sym::address) {
- codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS;
- } else if item.has_name(sym::cfi) {
- codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI;
- } else if item.has_name(sym::memory) {
- codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
- } else if item.has_name(sym::memtag) {
- codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG;
- } else if item.has_name(sym::shadow_call_stack) {
- codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK;
- } else if item.has_name(sym::thread) {
- codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD;
- } else if item.has_name(sym::hwaddress) {
- codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS;
- } else {
- tcx.sess
- .struct_span_err(item.span(), "invalid argument for `no_sanitize`")
- .note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
- .emit();
- }
- }
- }
- } else if attr.has_name(sym::instruction_set) {
- codegen_fn_attrs.instruction_set = match attr.meta_kind() {
- Some(MetaItemKind::List(ref items)) => match items.as_slice() {
- [NestedMetaItem::MetaItem(set)] => {
- let segments =
- set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
- match segments.as_slice() {
- [sym::arm, sym::a32] | [sym::arm, sym::t32] => {
- if !tcx.sess.target.has_thumb_interworking {
- struct_span_err!(
- tcx.sess.diagnostic(),
- attr.span,
- E0779,
- "target does not support `#[instruction_set]`"
- )
- .emit();
- None
- } else if segments[1] == sym::a32 {
- Some(InstructionSetAttr::ArmA32)
- } else if segments[1] == sym::t32 {
- Some(InstructionSetAttr::ArmT32)
- } else {
- unreachable!()
- }
- }
- _ => {
- struct_span_err!(
- tcx.sess.diagnostic(),
- attr.span,
- E0779,
- "invalid instruction set specified",
- )
- .emit();
- None
- }
- }
- }
- [] => {
- struct_span_err!(
- tcx.sess.diagnostic(),
- attr.span,
- E0778,
- "`#[instruction_set]` requires an argument"
- )
- .emit();
- None
- }
- _ => {
- struct_span_err!(
- tcx.sess.diagnostic(),
- attr.span,
- E0779,
- "cannot specify more than one instruction set"
- )
- .emit();
- None
- }
- },
- _ => {
- struct_span_err!(
- tcx.sess.diagnostic(),
- attr.span,
- E0778,
- "must specify an instruction set"
- )
- .emit();
- None
- }
- };
- } else if attr.has_name(sym::repr) {
- codegen_fn_attrs.alignment = match attr.meta_item_list() {
- Some(items) => match items.as_slice() {
- [item] => match item.name_value_literal() {
- Some((sym::align, literal)) => {
- let alignment = rustc_attr::parse_alignment(&literal.kind);
-
- match alignment {
- Ok(align) => Some(align),
- Err(msg) => {
- struct_span_err!(
- tcx.sess.diagnostic(),
- attr.span,
- E0589,
- "invalid `repr(align)` attribute: {}",
- msg
- )
- .emit();
-
- None
- }
- }
- }
- _ => None,
- },
- [] => None,
- _ => None,
- },
- None => None,
- };
- }
- }
-
- codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
- if !attr.has_name(sym::inline) {
- return ia;
- }
- match attr.meta_kind() {
- Some(MetaItemKind::Word) => InlineAttr::Hint,
- Some(MetaItemKind::List(ref items)) => {
- inline_span = Some(attr.span);
- if items.len() != 1 {
- struct_span_err!(
- tcx.sess.diagnostic(),
- attr.span,
- E0534,
- "expected one argument"
- )
- .emit();
- InlineAttr::None
- } else if list_contains_name(&items, sym::always) {
- InlineAttr::Always
- } else if list_contains_name(&items, sym::never) {
- InlineAttr::Never
- } else {
- struct_span_err!(
- tcx.sess.diagnostic(),
- items[0].span(),
- E0535,
- "invalid argument"
- )
- .emit();
-
- InlineAttr::None
- }
- }
- Some(MetaItemKind::NameValue(_)) => ia,
- None => ia,
- }
- });
-
- codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| {
- if !attr.has_name(sym::optimize) {
- return ia;
- }
- let err = |sp, s| struct_span_err!(tcx.sess.diagnostic(), sp, E0722, "{}", s).emit();
- match attr.meta_kind() {
- Some(MetaItemKind::Word) => {
- err(attr.span, "expected one argument");
- ia
- }
- Some(MetaItemKind::List(ref items)) => {
- inline_span = Some(attr.span);
- if items.len() != 1 {
- err(attr.span, "expected one argument");
- OptimizeAttr::None
- } else if list_contains_name(&items, sym::size) {
- OptimizeAttr::Size
- } else if list_contains_name(&items, sym::speed) {
- OptimizeAttr::Speed
- } else {
- err(items[0].span(), "invalid argument");
- OptimizeAttr::None
- }
- }
- Some(MetaItemKind::NameValue(_)) => ia,
- None => ia,
- }
- });
-
- // #73631: closures inherit `#[target_feature]` annotations
- if tcx.features().target_feature_11 && tcx.is_closure(did.to_def_id()) {
- let owner_id = tcx.parent(did.to_def_id());
- if tcx.def_kind(owner_id).has_codegen_attrs() {
- codegen_fn_attrs
- .target_features
- .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied());
- }
- }
-
- // If a function uses #[target_feature] it can't be inlined into general
- // purpose functions as they wouldn't have the right target features
- // enabled. For that reason we also forbid #[inline(always)] as it can't be
- // respected.
- if !codegen_fn_attrs.target_features.is_empty() {
- if codegen_fn_attrs.inline == InlineAttr::Always {
- if let Some(span) = inline_span {
- tcx.sess.span_err(
- span,
- "cannot use `#[inline(always)]` with \
- `#[target_feature]`",
- );
- }
- }
- }
-
- if !codegen_fn_attrs.no_sanitize.is_empty() {
- if codegen_fn_attrs.inline == InlineAttr::Always {
- if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) {
- let hir_id = tcx.hir().local_def_id_to_hir_id(did);
- tcx.struct_span_lint_hir(
- lint::builtin::INLINE_NO_SANITIZE,
- hir_id,
- no_sanitize_span,
- |lint| {
- lint.build("`no_sanitize` will have no effect after inlining")
- .span_note(inline_span, "inlining requested here")
- .emit();
- },
- )
- }
- }
- }
-
- // Weak lang items have the same semantics as "std internal" symbols in the
- // sense that they're preserved through all our LTO passes and only
- // strippable by the linker.
- //
- // Additionally weak lang items have predetermined symbol names.
- if tcx.is_weak_lang_item(did.to_def_id()) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
- }
- if let Some(name) = weak_lang_items::link_name(attrs) {
- codegen_fn_attrs.export_name = Some(name);
- codegen_fn_attrs.link_name = Some(name);
- }
- check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
-
- // Internal symbols to the standard library all have no_mangle semantics in
- // that they have defined symbol names present in the function name. This
- // also applies to weak symbols where they all have known symbol names.
- if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
- }
-
- // Any linkage to LLVM intrinsics for now forcibly marks them all as never
- // unwinds since LLVM sometimes can't handle codegen which `invoke`s
- // intrinsic functions.
- if let Some(name) = &codegen_fn_attrs.link_name {
- if name.as_str().starts_with("llvm.") {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
- }
- }
-
- codegen_fn_attrs
-}
-
-/// Computes the set of target features used in a function for the purposes of
-/// inline assembly.
-fn asm_target_features<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx FxHashSet<Symbol> {
- let mut target_features = tcx.sess.unstable_target_features.clone();
- if tcx.def_kind(did).has_codegen_attrs() {
- let attrs = tcx.codegen_fn_attrs(did);
- target_features.extend(&attrs.target_features);
- match attrs.instruction_set {
- None => {}
- Some(InstructionSetAttr::ArmA32) => {
- target_features.remove(&sym::thumb_mode);
- }
- Some(InstructionSetAttr::ArmT32) => {
- target_features.insert(sym::thumb_mode);
- }
- }
- }
-
- tcx.arena.alloc(target_features)
-}
-
-/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller
-/// applied to the method prototype.
-fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
- if let Some(impl_item) = tcx.opt_associated_item(def_id)
- && let ty::AssocItemContainer::ImplContainer = impl_item.container
- && let Some(trait_item) = impl_item.trait_item_def_id
- {
- return tcx
- .codegen_fn_attrs(trait_item)
- .flags
- .intersects(CodegenFnAttrFlags::TRACK_CALLER);
- }
-
- false
-}
-
-fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
- use rustc_ast::{Lit, LitIntType, LitKind};
- let meta_item_list = attr.meta_item_list();
- let meta_item_list: Option<&[ast::NestedMetaItem]> = meta_item_list.as_ref().map(Vec::as_ref);
- let sole_meta_list = match meta_item_list {
- Some([item]) => item.literal(),
- Some(_) => {
- tcx.sess
- .struct_span_err(attr.span, "incorrect number of arguments to `#[link_ordinal]`")
- .note("the attribute requires exactly one argument")
- .emit();
- return None;
- }
- _ => None,
- };
- if let Some(Lit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = sole_meta_list {
- // According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header,
- // the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
- // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information
- // to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
- //
- // FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for this:
- // both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies
- // a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library
- // for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import
- // library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I don't know yet
- // if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment
- // about LINK.EXE failing.)
- if *ordinal <= u16::MAX as u128 {
- Some(*ordinal as u16)
- } else {
- let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal);
- tcx.sess
- .struct_span_err(attr.span, &msg)
- .note("the value may not exceed `u16::MAX`")
- .emit();
- None
- }
- } else {
- tcx.sess
- .struct_span_err(attr.span, "illegal ordinal format in `link_ordinal`")
- .note("an unsuffixed integer value, e.g., `1`, is expected")
- .emit();
- None
- }
-}
-
-fn check_link_name_xor_ordinal(
- tcx: TyCtxt<'_>,
- codegen_fn_attrs: &CodegenFnAttrs,
- inline_span: Option<Span>,
-) {
- if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() {
- return;
- }
- let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
- if let Some(span) = inline_span {
- tcx.sess.span_err(span, msg);
- } else {
- tcx.sess.err(msg);
- }
-}
-
-/// Checks the function annotated with `#[target_feature]` is not a safe
-/// trait method implementation, reporting an error if it is.
-fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) {
- let hir_id = tcx.hir().local_def_id_to_hir_id(id);
- let node = tcx.hir().get(hir_id);
- if let Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node {
- let parent_id = tcx.hir().get_parent_item(hir_id);
- let parent_item = tcx.hir().expect_item(parent_id);
- if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = parent_item.kind {
- tcx.sess
- .struct_span_err(
- attr_span,
- "`#[target_feature(..)]` cannot be applied to safe trait method",
- )
- .span_label(attr_span, "cannot be applied to safe trait method")
- .span_label(tcx.def_span(id), "not an `unsafe` function")
- .emit();
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs
deleted file mode 100644
index 0438ac02e..000000000
--- a/compiler/rustc_typeck/src/errors.rs
+++ /dev/null
@@ -1,326 +0,0 @@
-//! Errors emitted by typeck.
-use rustc_errors::{error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed};
-use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
-use rustc_middle::ty::Ty;
-use rustc_session::{parse::ParseSess, SessionDiagnostic};
-use rustc_span::{symbol::Ident, Span, Symbol};
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::field_multiply_specified_in_initializer, code = "E0062")]
-pub struct FieldMultiplySpecifiedInInitializer {
- #[primary_span]
- #[label]
- pub span: Span,
- #[label(typeck::previous_use_label)]
- pub prev_span: Span,
- pub ident: Ident,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::unrecognized_atomic_operation, code = "E0092")]
-pub struct UnrecognizedAtomicOperation<'a> {
- #[primary_span]
- #[label]
- pub span: Span,
- pub op: &'a str,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::wrong_number_of_generic_arguments_to_intrinsic, code = "E0094")]
-pub struct WrongNumberOfGenericArgumentsToIntrinsic<'a> {
- #[primary_span]
- #[label]
- pub span: Span,
- pub found: usize,
- pub expected: usize,
- pub descr: &'a str,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::unrecognized_intrinsic_function, code = "E0093")]
-pub struct UnrecognizedIntrinsicFunction {
- #[primary_span]
- #[label]
- pub span: Span,
- pub name: Symbol,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::lifetimes_or_bounds_mismatch_on_trait, code = "E0195")]
-pub struct LifetimesOrBoundsMismatchOnTrait {
- #[primary_span]
- #[label]
- pub span: Span,
- #[label(typeck::generics_label)]
- pub generics_span: Option<Span>,
- pub item_kind: &'static str,
- pub ident: Ident,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::drop_impl_on_wrong_item, code = "E0120")]
-pub struct DropImplOnWrongItem {
- #[primary_span]
- #[label]
- pub span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::field_already_declared, code = "E0124")]
-pub struct FieldAlreadyDeclared {
- pub field_name: Ident,
- #[primary_span]
- #[label]
- pub span: Span,
- #[label(typeck::previous_decl_label)]
- pub prev_span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::copy_impl_on_type_with_dtor, code = "E0184")]
-pub struct CopyImplOnTypeWithDtor {
- #[primary_span]
- #[label]
- pub span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::multiple_relaxed_default_bounds, code = "E0203")]
-pub struct MultipleRelaxedDefaultBounds {
- #[primary_span]
- pub span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::copy_impl_on_non_adt, code = "E0206")]
-pub struct CopyImplOnNonAdt {
- #[primary_span]
- #[label]
- pub span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::trait_object_declared_with_no_traits, code = "E0224")]
-pub struct TraitObjectDeclaredWithNoTraits {
- #[primary_span]
- pub span: Span,
- #[label(typeck::alias_span)]
- pub trait_alias_span: Option<Span>,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::ambiguous_lifetime_bound, code = "E0227")]
-pub struct AmbiguousLifetimeBound {
- #[primary_span]
- pub span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::assoc_type_binding_not_allowed, code = "E0229")]
-pub struct AssocTypeBindingNotAllowed {
- #[primary_span]
- #[label]
- pub span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::functional_record_update_on_non_struct, code = "E0436")]
-pub struct FunctionalRecordUpdateOnNonStruct {
- #[primary_span]
- pub span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::typeof_reserved_keyword_used, code = "E0516")]
-pub struct TypeofReservedKeywordUsed<'tcx> {
- pub ty: Ty<'tcx>,
- #[primary_span]
- #[label]
- pub span: Span,
- #[suggestion_verbose(code = "{ty}")]
- pub opt_sugg: Option<(Span, Applicability)>,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::return_stmt_outside_of_fn_body, code = "E0572")]
-pub struct ReturnStmtOutsideOfFnBody {
- #[primary_span]
- pub span: Span,
- #[label(typeck::encl_body_label)]
- pub encl_body_span: Option<Span>,
- #[label(typeck::encl_fn_label)]
- pub encl_fn_span: Option<Span>,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::yield_expr_outside_of_generator, code = "E0627")]
-pub struct YieldExprOutsideOfGenerator {
- #[primary_span]
- pub span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::struct_expr_non_exhaustive, code = "E0639")]
-pub struct StructExprNonExhaustive {
- #[primary_span]
- pub span: Span,
- pub what: &'static str,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::method_call_on_unknown_type, code = "E0699")]
-pub struct MethodCallOnUnknownType {
- #[primary_span]
- pub span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::value_of_associated_struct_already_specified, code = "E0719")]
-pub struct ValueOfAssociatedStructAlreadySpecified {
- #[primary_span]
- #[label]
- pub span: Span,
- #[label(typeck::previous_bound_label)]
- pub prev_span: Span,
- pub item_name: Ident,
- pub def_path: String,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::address_of_temporary_taken, code = "E0745")]
-pub struct AddressOfTemporaryTaken {
- #[primary_span]
- #[label]
- pub span: Span,
-}
-
-#[derive(SessionSubdiagnostic)]
-pub enum AddReturnTypeSuggestion<'tcx> {
- #[suggestion(
- typeck::add_return_type_add,
- code = "-> {found} ",
- applicability = "machine-applicable"
- )]
- Add {
- #[primary_span]
- span: Span,
- found: Ty<'tcx>,
- },
- #[suggestion(
- typeck::add_return_type_missing_here,
- code = "-> _ ",
- applicability = "has-placeholders"
- )]
- MissingHere {
- #[primary_span]
- span: Span,
- },
-}
-
-#[derive(SessionSubdiagnostic)]
-pub enum ExpectedReturnTypeLabel<'tcx> {
- #[label(typeck::expected_default_return_type)]
- Unit {
- #[primary_span]
- span: Span,
- },
- #[label(typeck::expected_return_type)]
- Other {
- #[primary_span]
- span: Span,
- expected: Ty<'tcx>,
- },
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::unconstrained_opaque_type)]
-#[note]
-pub struct UnconstrainedOpaqueType {
- #[primary_span]
- pub span: Span,
- pub name: Symbol,
-}
-
-pub struct MissingTypeParams {
- pub span: Span,
- pub def_span: Span,
- pub missing_type_params: Vec<Symbol>,
- pub empty_generic_args: bool,
-}
-
-// Manual implementation of `SessionDiagnostic` to be able to call `span_to_snippet`.
-impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
- fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
- let mut err = sess.span_diagnostic.struct_span_err_with_code(
- self.span,
- rustc_errors::fluent::typeck::missing_type_params,
- error_code!(E0393),
- );
- err.set_arg("parameterCount", self.missing_type_params.len());
- err.set_arg(
- "parameters",
- self.missing_type_params
- .iter()
- .map(|n| format!("`{}`", n))
- .collect::<Vec<_>>()
- .join(", "),
- );
-
- err.span_label(self.def_span, rustc_errors::fluent::typeck::label);
-
- let mut suggested = false;
- if let (Ok(snippet), true) = (
- sess.source_map().span_to_snippet(self.span),
- // Don't suggest setting the type params if there are some already: the order is
- // tricky to get right and the user will already know what the syntax is.
- self.empty_generic_args,
- ) {
- if snippet.ends_with('>') {
- // The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion
- // we would have to preserve the right order. For now, as clearly the user is
- // aware of the syntax, we do nothing.
- } else {
- // The user wrote `Iterator`, so we don't have a type we can suggest, but at
- // least we can clue them to the correct syntax `Iterator<Type>`.
- err.span_suggestion(
- self.span,
- rustc_errors::fluent::typeck::suggestion,
- format!(
- "{}<{}>",
- snippet,
- self.missing_type_params
- .iter()
- .map(|n| n.to_string())
- .collect::<Vec<_>>()
- .join(", ")
- ),
- Applicability::HasPlaceholders,
- );
- suggested = true;
- }
- }
- if !suggested {
- err.span_label(self.span, rustc_errors::fluent::typeck::no_suggestion_label);
- }
-
- err.note(rustc_errors::fluent::typeck::note);
- err
- }
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::manual_implementation, code = "E0183")]
-#[help]
-pub struct ManualImplementation {
- #[primary_span]
- #[label]
- pub span: Span,
- pub trait_name: String,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(typeck::substs_on_overridden_impl)]
-pub struct SubstsOnOverriddenImpl {
- #[primary_span]
- pub span: Span,
-}
diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs
deleted file mode 100644
index f98ae46c5..000000000
--- a/compiler/rustc_typeck/src/lib.rs
+++ /dev/null
@@ -1,579 +0,0 @@
-/*!
-
-# typeck
-
-The type checker is responsible for:
-
-1. Determining the type of each expression.
-2. Resolving methods and traits.
-3. Guaranteeing that most type rules are met. ("Most?", you say, "why most?"
- Well, dear reader, read on.)
-
-The main entry point is [`check_crate()`]. Type checking operates in
-several major phases:
-
-1. The collect phase first passes over all items and determines their
- type, without examining their "innards".
-
-2. Variance inference then runs to compute the variance of each parameter.
-
-3. Coherence checks for overlapping or orphaned impls.
-
-4. Finally, the check phase then checks function bodies and so forth.
- Within the check phase, we check each function body one at a time
- (bodies of function expressions are checked as part of the
- containing function). Inference is used to supply types wherever
- they are unknown. The actual checking of a function itself has
- several phases (check, regionck, writeback), as discussed in the
- documentation for the [`check`] module.
-
-The type checker is defined into various submodules which are documented
-independently:
-
-- astconv: converts the AST representation of types
- into the `ty` representation.
-
-- collect: computes the types of each top-level item and enters them into
- the `tcx.types` table for later use.
-
-- coherence: enforces coherence rules, builds some tables.
-
-- variance: variance inference
-
-- outlives: outlives inference
-
-- check: walks over function bodies and type checks them, inferring types for
- local variables, type parameters, etc as necessary.
-
-- infer: finds the types to use for each type variable such that
- all subtyping and assignment constraints are met. In essence, the check
- module specifies the constraints, and the infer module solves them.
-
-## Note
-
-This API is completely unstable and subject to change.
-
-*/
-
-#![allow(rustc::potential_query_instability)]
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
-#![feature(box_patterns)]
-#![feature(control_flow_enum)]
-#![feature(drain_filter)]
-#![feature(hash_drain_filter)]
-#![feature(if_let_guard)]
-#![feature(is_sorted)]
-#![feature(iter_intersperse)]
-#![feature(label_break_value)]
-#![feature(let_chains)]
-#![feature(let_else)]
-#![feature(min_specialization)]
-#![feature(never_type)]
-#![feature(once_cell)]
-#![feature(slice_partition_dedup)]
-#![feature(try_blocks)]
-#![feature(is_some_with)]
-#![recursion_limit = "256"]
-
-#[macro_use]
-extern crate tracing;
-
-#[macro_use]
-extern crate rustc_middle;
-
-// These are used by Clippy.
-pub mod check;
-pub mod expr_use_visitor;
-
-mod astconv;
-mod bounds;
-mod check_unused;
-mod coherence;
-mod collect;
-mod constrained_generic_params;
-mod errors;
-pub mod hir_wf_check;
-mod impl_wf_check;
-mod mem_categorization;
-mod outlives;
-mod structured_errors;
-mod variance;
-
-use rustc_errors::{struct_span_err, ErrorGuaranteed};
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_hir::{Node, CRATE_HIR_ID};
-use rustc_infer::infer::{InferOk, TyCtxtInferExt};
-use rustc_infer::traits::TraitEngineExt as _;
-use rustc_middle::middle;
-use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_middle::util;
-use rustc_session::config::EntryFnType;
-use rustc_span::{symbol::sym, Span, DUMMY_SP};
-use rustc_target::spec::abi::Abi;
-use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
-use rustc_trait_selection::traits::{
- self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt as _,
-};
-
-use std::iter;
-
-use astconv::AstConv;
-use bounds::Bounds;
-
-fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) {
- match (decl.c_variadic, abi) {
- // The function has the correct calling convention, or isn't a "C-variadic" function.
- (false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl { .. }) => {}
- // The function is a "C-variadic" function with an incorrect calling convention.
- (true, _) => {
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0045,
- "C-variadic function must have C or cdecl calling convention"
- );
- err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
- }
- }
-}
-
-fn require_same_types<'tcx>(
- tcx: TyCtxt<'tcx>,
- cause: &ObligationCause<'tcx>,
- expected: Ty<'tcx>,
- actual: Ty<'tcx>,
-) -> bool {
- tcx.infer_ctxt().enter(|ref infcx| {
- let param_env = ty::ParamEnv::empty();
- let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
- match infcx.at(cause, param_env).eq(expected, actual) {
- Ok(InferOk { obligations, .. }) => {
- fulfill_cx.register_predicate_obligations(infcx, obligations);
- }
- Err(err) => {
- infcx.report_mismatched_types(cause, expected, actual, err).emit();
- return false;
- }
- }
-
- match fulfill_cx.select_all_or_error(infcx).as_slice() {
- [] => true,
- errors => {
- infcx.report_fulfillment_errors(errors, None, false);
- false
- }
- }
- })
-}
-
-fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
- let main_fnsig = tcx.fn_sig(main_def_id);
- let main_span = tcx.def_span(main_def_id);
-
- fn main_fn_diagnostics_hir_id(tcx: TyCtxt<'_>, def_id: DefId, sp: Span) -> hir::HirId {
- if let Some(local_def_id) = def_id.as_local() {
- let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id);
- let hir_type = tcx.type_of(local_def_id);
- if !matches!(hir_type.kind(), ty::FnDef(..)) {
- span_bug!(sp, "main has a non-function type: found `{}`", hir_type);
- }
- hir_id
- } else {
- CRATE_HIR_ID
- }
- }
-
- fn main_fn_generics_params_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
- if !def_id.is_local() {
- return None;
- }
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
- match tcx.hir().find(hir_id) {
- Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => {
- if !generics.params.is_empty() {
- Some(generics.span)
- } else {
- None
- }
- }
- _ => {
- span_bug!(tcx.def_span(def_id), "main has a non-function type");
- }
- }
- }
-
- fn main_fn_where_clauses_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
- if !def_id.is_local() {
- return None;
- }
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
- match tcx.hir().find(hir_id) {
- Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => {
- Some(generics.where_clause_span)
- }
- _ => {
- span_bug!(tcx.def_span(def_id), "main has a non-function type");
- }
- }
- }
-
- fn main_fn_asyncness_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
- if !def_id.is_local() {
- return None;
- }
- Some(tcx.def_span(def_id))
- }
-
- fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
- if !def_id.is_local() {
- return None;
- }
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
- match tcx.hir().find(hir_id) {
- Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(ref fn_sig, _, _), .. })) => {
- Some(fn_sig.decl.output.span())
- }
- _ => {
- span_bug!(tcx.def_span(def_id), "main has a non-function type");
- }
- }
- }
-
- let mut error = false;
- let main_diagnostics_hir_id = main_fn_diagnostics_hir_id(tcx, main_def_id, main_span);
- let main_fn_generics = tcx.generics_of(main_def_id);
- let main_fn_predicates = tcx.predicates_of(main_def_id);
- if main_fn_generics.count() != 0 || !main_fnsig.bound_vars().is_empty() {
- let generics_param_span = main_fn_generics_params_span(tcx, main_def_id);
- let msg = "`main` function is not allowed to have generic \
- parameters";
- let mut diag =
- struct_span_err!(tcx.sess, generics_param_span.unwrap_or(main_span), E0131, "{}", msg);
- if let Some(generics_param_span) = generics_param_span {
- let label = "`main` cannot have generic parameters";
- diag.span_label(generics_param_span, label);
- }
- diag.emit();
- error = true;
- } else if !main_fn_predicates.predicates.is_empty() {
- // generics may bring in implicit predicates, so we skip this check if generics is present.
- let generics_where_clauses_span = main_fn_where_clauses_span(tcx, main_def_id);
- let mut diag = struct_span_err!(
- tcx.sess,
- generics_where_clauses_span.unwrap_or(main_span),
- E0646,
- "`main` function is not allowed to have a `where` clause"
- );
- if let Some(generics_where_clauses_span) = generics_where_clauses_span {
- diag.span_label(generics_where_clauses_span, "`main` cannot have a `where` clause");
- }
- diag.emit();
- error = true;
- }
-
- let main_asyncness = tcx.asyncness(main_def_id);
- if let hir::IsAsync::Async = main_asyncness {
- let mut diag = struct_span_err!(
- tcx.sess,
- main_span,
- E0752,
- "`main` function is not allowed to be `async`"
- );
- let asyncness_span = main_fn_asyncness_span(tcx, main_def_id);
- if let Some(asyncness_span) = asyncness_span {
- diag.span_label(asyncness_span, "`main` function is not allowed to be `async`");
- }
- diag.emit();
- error = true;
- }
-
- for attr in tcx.get_attrs(main_def_id, sym::track_caller) {
- tcx.sess
- .struct_span_err(attr.span, "`main` function is not allowed to be `#[track_caller]`")
- .span_label(main_span, "`main` function is not allowed to be `#[track_caller]`")
- .emit();
- error = true;
- }
-
- if error {
- return;
- }
-
- let expected_return_type;
- if let Some(term_id) = tcx.lang_items().termination() {
- let return_ty = main_fnsig.output();
- let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span);
- if !return_ty.bound_vars().is_empty() {
- let msg = "`main` function return type is not allowed to have generic \
- parameters";
- struct_span_err!(tcx.sess, return_ty_span, E0131, "{}", msg).emit();
- error = true;
- }
- let return_ty = return_ty.skip_binder();
- tcx.infer_ctxt().enter(|infcx| {
- let cause = traits::ObligationCause::new(
- return_ty_span,
- main_diagnostics_hir_id,
- ObligationCauseCode::MainFunctionType,
- );
- let mut fulfillment_cx = traits::FulfillmentContext::new();
- // normalize any potential projections in the return type, then add
- // any possible obligations to the fulfillment context.
- // HACK(ThePuzzlemaker) this feels symptomatic of a problem within
- // checking trait fulfillment, not this here. I'm not sure why it
- // works in the example in `fn test()` given in #88609? This also
- // probably isn't the best way to do this.
- let InferOk { value: norm_return_ty, obligations } = infcx
- .partially_normalize_associated_types_in(
- cause.clone(),
- ty::ParamEnv::empty(),
- return_ty,
- );
- fulfillment_cx.register_predicate_obligations(&infcx, obligations);
- fulfillment_cx.register_bound(
- &infcx,
- ty::ParamEnv::empty(),
- norm_return_ty,
- term_id,
- cause,
- );
- let errors = fulfillment_cx.select_all_or_error(&infcx);
- if !errors.is_empty() {
- infcx.report_fulfillment_errors(&errors, None, false);
- error = true;
- }
- });
- // now we can take the return type of the given main function
- expected_return_type = main_fnsig.output();
- } else {
- // standard () main return type
- expected_return_type = ty::Binder::dummy(tcx.mk_unit());
- }
-
- if error {
- return;
- }
-
- let se_ty = tcx.mk_fn_ptr(expected_return_type.map_bound(|expected_return_type| {
- tcx.mk_fn_sig(iter::empty(), expected_return_type, false, hir::Unsafety::Normal, Abi::Rust)
- }));
-
- require_same_types(
- tcx,
- &ObligationCause::new(
- main_span,
- main_diagnostics_hir_id,
- ObligationCauseCode::MainFunctionType,
- ),
- se_ty,
- tcx.mk_fn_ptr(main_fnsig),
- );
-}
-fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
- let start_def_id = start_def_id.expect_local();
- let start_id = tcx.hir().local_def_id_to_hir_id(start_def_id);
- let start_span = tcx.def_span(start_def_id);
- let start_t = tcx.type_of(start_def_id);
- match start_t.kind() {
- ty::FnDef(..) => {
- if let Some(Node::Item(it)) = tcx.hir().find(start_id) {
- if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind {
- let mut error = false;
- if !generics.params.is_empty() {
- struct_span_err!(
- tcx.sess,
- generics.span,
- E0132,
- "start function is not allowed to have type parameters"
- )
- .span_label(generics.span, "start function cannot have type parameters")
- .emit();
- error = true;
- }
- if generics.has_where_clause_predicates {
- struct_span_err!(
- tcx.sess,
- generics.where_clause_span,
- E0647,
- "start function is not allowed to have a `where` clause"
- )
- .span_label(
- generics.where_clause_span,
- "start function cannot have a `where` clause",
- )
- .emit();
- error = true;
- }
- if let hir::IsAsync::Async = sig.header.asyncness {
- let span = tcx.def_span(it.def_id);
- struct_span_err!(
- tcx.sess,
- span,
- E0752,
- "`start` is not allowed to be `async`"
- )
- .span_label(span, "`start` is not allowed to be `async`")
- .emit();
- error = true;
- }
-
- let attrs = tcx.hir().attrs(start_id);
- for attr in attrs {
- if attr.has_name(sym::track_caller) {
- tcx.sess
- .struct_span_err(
- attr.span,
- "`start` is not allowed to be `#[track_caller]`",
- )
- .span_label(
- start_span,
- "`start` is not allowed to be `#[track_caller]`",
- )
- .emit();
- error = true;
- }
- }
-
- if error {
- return;
- }
- }
- }
-
- let se_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
- [tcx.types.isize, tcx.mk_imm_ptr(tcx.mk_imm_ptr(tcx.types.u8))].iter().cloned(),
- tcx.types.isize,
- false,
- hir::Unsafety::Normal,
- Abi::Rust,
- )));
-
- require_same_types(
- tcx,
- &ObligationCause::new(start_span, start_id, ObligationCauseCode::StartFunctionType),
- se_ty,
- tcx.mk_fn_ptr(tcx.fn_sig(start_def_id)),
- );
- }
- _ => {
- span_bug!(start_span, "start has a non-function type: found `{}`", start_t);
- }
- }
-}
-
-fn check_for_entry_fn(tcx: TyCtxt<'_>) {
- match tcx.entry_fn(()) {
- Some((def_id, EntryFnType::Main)) => check_main_fn_ty(tcx, def_id),
- Some((def_id, EntryFnType::Start)) => check_start_fn_ty(tcx, def_id),
- _ => {}
- }
-}
-
-pub fn provide(providers: &mut Providers) {
- collect::provide(providers);
- coherence::provide(providers);
- check::provide(providers);
- variance::provide(providers);
- outlives::provide(providers);
- impl_wf_check::provide(providers);
- hir_wf_check::provide(providers);
-}
-
-pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
- let _prof_timer = tcx.sess.timer("type_check_crate");
-
- // this ensures that later parts of type checking can assume that items
- // have valid types and not error
- // FIXME(matthewjasper) We shouldn't need to use `track_errors`.
- tcx.sess.track_errors(|| {
- tcx.sess.time("type_collecting", || {
- tcx.hir().for_each_module(|module| tcx.ensure().collect_mod_item_types(module))
- });
- })?;
-
- if tcx.features().rustc_attrs {
- tcx.sess.track_errors(|| {
- tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx));
- })?;
- }
-
- tcx.sess.track_errors(|| {
- tcx.sess.time("impl_wf_inference", || {
- tcx.hir().for_each_module(|module| tcx.ensure().check_mod_impl_wf(module))
- });
- })?;
-
- tcx.sess.track_errors(|| {
- tcx.sess.time("coherence_checking", || {
- for &trait_def_id in tcx.all_local_trait_impls(()).keys() {
- tcx.ensure().coherent_trait(trait_def_id);
- }
-
- // these queries are executed for side-effects (error reporting):
- tcx.ensure().crate_inherent_impls(());
- tcx.ensure().crate_inherent_impls_overlap_check(());
- });
- })?;
-
- if tcx.features().rustc_attrs {
- tcx.sess.track_errors(|| {
- tcx.sess.time("variance_testing", || variance::test::test_variance(tcx));
- })?;
- }
-
- tcx.sess.track_errors(|| {
- tcx.sess.time("wf_checking", || {
- tcx.hir().par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module))
- });
- })?;
-
- // NOTE: This is copy/pasted in librustdoc/core.rs and should be kept in sync.
- tcx.sess.time("item_types_checking", || {
- tcx.hir().for_each_module(|module| tcx.ensure().check_mod_item_types(module))
- });
-
- tcx.sess.time("item_bodies_checking", || tcx.typeck_item_bodies(()));
-
- check_unused::check_crate(tcx);
- check_for_entry_fn(tcx);
-
- if let Some(reported) = tcx.sess.has_errors() { Err(reported) } else { Ok(()) }
-}
-
-/// A quasi-deprecated helper used in rustdoc and clippy to get
-/// the type from a HIR node.
-pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> {
- // In case there are any projections, etc., find the "environment"
- // def-ID that will be used to determine the traits/predicates in
- // scope. This is derived from the enclosing item-like thing.
- let env_def_id = tcx.hir().get_parent_item(hir_ty.hir_id);
- let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id());
- <dyn AstConv<'_>>::ast_ty_to_ty(&item_cx, hir_ty)
-}
-
-pub fn hir_trait_to_predicates<'tcx>(
- tcx: TyCtxt<'tcx>,
- hir_trait: &hir::TraitRef<'_>,
- self_ty: Ty<'tcx>,
-) -> Bounds<'tcx> {
- // In case there are any projections, etc., find the "environment"
- // def-ID that will be used to determine the traits/predicates in
- // scope. This is derived from the enclosing item-like thing.
- let env_def_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id);
- let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id());
- let mut bounds = Bounds::default();
- let _ = <dyn AstConv<'_>>::instantiate_poly_trait_ref(
- &item_cx,
- hir_trait,
- DUMMY_SP,
- ty::BoundConstness::NotConst,
- self_ty,
- &mut bounds,
- true,
- );
-
- bounds
-}
diff --git a/compiler/rustc_typeck/src/outlives/mod.rs b/compiler/rustc_typeck/src/outlives/mod.rs
deleted file mode 100644
index 8fa65d51e..000000000
--- a/compiler/rustc_typeck/src/outlives/mod.rs
+++ /dev/null
@@ -1,130 +0,0 @@
-use hir::Node;
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::{self, CratePredicatesMap, ToPredicate, TyCtxt};
-use rustc_span::symbol::sym;
-use rustc_span::Span;
-
-mod explicit;
-mod implicit_infer;
-pub(crate) mod outlives_bounds;
-/// Code to write unit test for outlives.
-pub mod test;
-mod utils;
-
-pub fn provide(providers: &mut Providers) {
- *providers = Providers { inferred_outlives_of, inferred_outlives_crate, ..*providers };
-}
-
-fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate<'_>, Span)] {
- let id = tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local());
-
- if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) && tcx.lazy_normalization()
- {
- if tcx.hir().opt_const_param_default_param_hir_id(id).is_some() {
- // In `generics_of` we set the generics' parent to be our parent's parent which means that
- // we lose out on the predicates of our actual parent if we dont return those predicates here.
- // (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
- //
- // struct Foo<'a, 'b, const N: usize = { ... }>(&'a &'b ());
- // ^^^ ^^^^^^^ the def id we are calling
- // ^^^ inferred_outlives_of on
- // parent item we dont have set as the
- // parent of generics returned by `generics_of`
- //
- // In the above code we want the anon const to have predicates in its param env for `'b: 'a`
- let item_def_id = tcx.hir().get_parent_item(id);
- // In the above code example we would be calling `inferred_outlives_of(Foo)` here
- return tcx.inferred_outlives_of(item_def_id);
- }
- }
-
- match tcx.hir().get(id) {
- Node::Item(item) => match item.kind {
- hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..) => {
- let crate_map = tcx.inferred_outlives_crate(());
-
- let predicates = crate_map.predicates.get(&item_def_id).copied().unwrap_or(&[]);
-
- if tcx.has_attr(item_def_id, sym::rustc_outlives) {
- let mut pred: Vec<String> = predicates
- .iter()
- .map(|(out_pred, _)| match out_pred.kind().skip_binder() {
- ty::PredicateKind::RegionOutlives(p) => p.to_string(),
- ty::PredicateKind::TypeOutlives(p) => p.to_string(),
- err => bug!("unexpected predicate {:?}", err),
- })
- .collect();
- pred.sort();
-
- let span = tcx.def_span(item_def_id);
- let mut err = tcx.sess.struct_span_err(span, "rustc_outlives");
- for p in &pred {
- err.note(p);
- }
- err.emit();
- }
-
- debug!("inferred_outlives_of({:?}) = {:?}", item_def_id, predicates);
-
- predicates
- }
-
- _ => &[],
- },
-
- _ => &[],
- }
-}
-
-fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> {
- // Compute a map from each struct/enum/union S to the **explicit**
- // outlives predicates (`T: 'a`, `'a: 'b`) that the user wrote.
- // Typically there won't be many of these, except in older code where
- // they were mandatory. Nonetheless, we have to ensure that every such
- // predicate is satisfied, so they form a kind of base set of requirements
- // for the type.
-
- // Compute the inferred predicates
- let global_inferred_outlives = implicit_infer::infer_predicates(tcx);
-
- // Convert the inferred predicates into the "collected" form the
- // global data structure expects.
- //
- // FIXME -- consider correcting impedance mismatch in some way,
- // probably by updating the global data structure.
- let predicates = global_inferred_outlives
- .iter()
- .map(|(&def_id, set)| {
- let predicates = &*tcx.arena.alloc_from_iter(set.0.iter().filter_map(
- |(ty::OutlivesPredicate(kind1, region2), &span)| {
- match kind1.unpack() {
- GenericArgKind::Type(ty1) => Some((
- ty::Binder::dummy(ty::PredicateKind::TypeOutlives(
- ty::OutlivesPredicate(ty1, *region2),
- ))
- .to_predicate(tcx),
- span,
- )),
- GenericArgKind::Lifetime(region1) => Some((
- ty::Binder::dummy(ty::PredicateKind::RegionOutlives(
- ty::OutlivesPredicate(region1, *region2),
- ))
- .to_predicate(tcx),
- span,
- )),
- GenericArgKind::Const(_) => {
- // Generic consts don't impose any constraints.
- None
- }
- }
- },
- ));
- (def_id, predicates)
- })
- .collect();
-
- ty::CratePredicatesMap { predicates }
-}
diff --git a/compiler/rustc_typeck/src/outlives/outlives_bounds.rs b/compiler/rustc_typeck/src/outlives/outlives_bounds.rs
deleted file mode 100644
index 229a64650..000000000
--- a/compiler/rustc_typeck/src/outlives/outlives_bounds.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-use rustc_hir as hir;
-use rustc_middle::ty::{self, Ty};
-use rustc_trait_selection::infer::InferCtxt;
-use rustc_trait_selection::traits::query::type_op::{self, TypeOp, TypeOpOutput};
-use rustc_trait_selection::traits::query::NoSolution;
-use rustc_trait_selection::traits::{ObligationCause, TraitEngine, TraitEngineExt};
-
-pub use rustc_middle::traits::query::OutlivesBound;
-
-pub trait InferCtxtExt<'tcx> {
- fn implied_outlives_bounds(
- &self,
- param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
- ty: Ty<'tcx>,
- ) -> Vec<OutlivesBound<'tcx>>;
-}
-
-impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
- /// Implied bounds are region relationships that we deduce
- /// automatically. The idea is that (e.g.) a caller must check that a
- /// function's argument types are well-formed immediately before
- /// calling that fn, and hence the *callee* can assume that its
- /// argument types are well-formed. This may imply certain relationships
- /// between generic parameters. For example:
- /// ```
- /// fn foo<'a,T>(x: &'a T) {}
- /// ```
- /// can only be called with a `'a` and `T` such that `&'a T` is WF.
- /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
- ///
- /// # Parameters
- ///
- /// - `param_env`, the where-clauses in scope
- /// - `body_id`, the body-id to use when normalizing assoc types.
- /// Note that this may cause outlives obligations to be injected
- /// into the inference context with this body-id.
- /// - `ty`, the type that we are supposed to assume is WF.
- #[instrument(level = "debug", skip(self, param_env, body_id))]
- fn implied_outlives_bounds(
- &self,
- param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
- ty: Ty<'tcx>,
- ) -> Vec<OutlivesBound<'tcx>> {
- let span = self.tcx.hir().span(body_id);
- let result = param_env
- .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty })
- .fully_perform(self);
- let result = match result {
- Ok(r) => r,
- Err(NoSolution) => {
- self.tcx.sess.delay_span_bug(
- span,
- "implied_outlives_bounds failed to solve all obligations",
- );
- return vec![];
- }
- };
-
- let TypeOpOutput { output, constraints, .. } = result;
-
- if let Some(constraints) = constraints {
- // Instantiation may have produced new inference variables and constraints on those
- // variables. Process these constraints.
- let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self.tcx);
- let cause = ObligationCause::misc(span, body_id);
- for &constraint in &constraints.outlives {
- let obligation = self.query_outlives_constraint_to_obligation(
- constraint,
- cause.clone(),
- param_env,
- );
- fulfill_cx.register_predicate_obligation(self, obligation);
- }
- if !constraints.member_constraints.is_empty() {
- span_bug!(span, "{:#?}", constraints.member_constraints);
- }
- let errors = fulfill_cx.select_all_or_error(self);
- if !errors.is_empty() {
- self.tcx.sess.delay_span_bug(
- span,
- "implied_outlives_bounds failed to solve obligations from instantiation",
- );
- }
- };
-
- output
- }
-}
diff --git a/compiler/rustc_typeck/src/outlives/test.rs b/compiler/rustc_typeck/src/outlives/test.rs
deleted file mode 100644
index eb0e12034..000000000
--- a/compiler/rustc_typeck/src/outlives/test.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use rustc_errors::struct_span_err;
-use rustc_middle::ty::TyCtxt;
-use rustc_span::symbol::sym;
-
-pub fn test_inferred_outlives(tcx: TyCtxt<'_>) {
- for id in tcx.hir().items() {
- // For unit testing: check for a special "rustc_outlives"
- // attribute and report an error with various results if found.
- if tcx.has_attr(id.def_id.to_def_id(), sym::rustc_outlives) {
- let inferred_outlives_of = tcx.inferred_outlives_of(id.def_id);
- struct_span_err!(
- tcx.sess,
- tcx.def_span(id.def_id),
- E0640,
- "{:?}",
- inferred_outlives_of
- )
- .emit();
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/outlives/utils.rs b/compiler/rustc_typeck/src/outlives/utils.rs
deleted file mode 100644
index b718ca942..000000000
--- a/compiler/rustc_typeck/src/outlives/utils.rs
+++ /dev/null
@@ -1,175 +0,0 @@
-use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
-use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, Region, Ty, TyCtxt};
-use rustc_span::Span;
-use smallvec::smallvec;
-use std::collections::BTreeMap;
-
-/// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred
-/// must be added to the struct header.
-pub(crate) type RequiredPredicates<'tcx> =
- BTreeMap<ty::OutlivesPredicate<GenericArg<'tcx>, ty::Region<'tcx>>, Span>;
-
-/// Given a requirement `T: 'a` or `'b: 'a`, deduce the
-/// outlives_component and add it to `required_predicates`
-pub(crate) fn insert_outlives_predicate<'tcx>(
- tcx: TyCtxt<'tcx>,
- kind: GenericArg<'tcx>,
- outlived_region: Region<'tcx>,
- span: Span,
- required_predicates: &mut RequiredPredicates<'tcx>,
-) {
- // If the `'a` region is bound within the field type itself, we
- // don't want to propagate this constraint to the header.
- if !is_free_region(outlived_region) {
- return;
- }
-
- match kind.unpack() {
- GenericArgKind::Type(ty) => {
- // `T: 'outlived_region` for some type `T`
- // But T could be a lot of things:
- // e.g., if `T = &'b u32`, then `'b: 'outlived_region` is
- // what we want to add.
- //
- // Or if within `struct Foo<U>` you had `T = Vec<U>`, then
- // we would want to add `U: 'outlived_region`
- let mut components = smallvec![];
- push_outlives_components(tcx, ty, &mut components);
- for component in components {
- match component {
- Component::Region(r) => {
- // This would arise from something like:
- //
- // ```
- // struct Foo<'a, 'b> {
- // x: &'a &'b u32
- // }
- // ```
- //
- // Here `outlived_region = 'a` and `kind = &'b
- // u32`. Decomposing `&'b u32` into
- // components would yield `'b`, and we add the
- // where clause that `'b: 'a`.
- insert_outlives_predicate(
- tcx,
- r.into(),
- outlived_region,
- span,
- required_predicates,
- );
- }
-
- Component::Param(param_ty) => {
- // param_ty: ty::ParamTy
- // This would arise from something like:
- //
- // ```
- // struct Foo<'a, U> {
- // x: &'a Vec<U>
- // }
- // ```
- //
- // Here `outlived_region = 'a` and `kind =
- // Vec<U>`. Decomposing `Vec<U>` into
- // components would yield `U`, and we add the
- // where clause that `U: 'a`.
- let ty: Ty<'tcx> = param_ty.to_ty(tcx);
- required_predicates
- .entry(ty::OutlivesPredicate(ty.into(), outlived_region))
- .or_insert(span);
- }
-
- Component::Projection(proj_ty) => {
- // This would arise from something like:
- //
- // ```
- // struct Foo<'a, T: Iterator> {
- // x: &'a <T as Iterator>::Item
- // }
- // ```
- //
- // Here we want to add an explicit `where <T as Iterator>::Item: 'a`.
- let ty: Ty<'tcx> = tcx.mk_projection(proj_ty.item_def_id, proj_ty.substs);
- required_predicates
- .entry(ty::OutlivesPredicate(ty.into(), outlived_region))
- .or_insert(span);
- }
-
- Component::EscapingProjection(_) => {
- // As above, but the projection involves
- // late-bound regions. Therefore, the WF
- // requirement is not checked in type definition
- // but at fn call site, so ignore it.
- //
- // ```
- // struct Foo<'a, T: Iterator> {
- // x: for<'b> fn(<&'b T as Iterator>::Item)
- // // ^^^^^^^^^^^^^^^^^^^^^^^^^
- // }
- // ```
- //
- // Since `'b` is not in scope on `Foo`, can't
- // do anything here, ignore it.
- }
-
- Component::UnresolvedInferenceVariable(_) => bug!("not using infcx"),
- }
- }
- }
-
- GenericArgKind::Lifetime(r) => {
- if !is_free_region(r) {
- return;
- }
- required_predicates.entry(ty::OutlivesPredicate(kind, outlived_region)).or_insert(span);
- }
-
- GenericArgKind::Const(_) => {
- // Generic consts don't impose any constraints.
- }
- }
-}
-
-fn is_free_region(region: Region<'_>) -> bool {
- // First, screen for regions that might appear in a type header.
- match *region {
- // These correspond to `T: 'a` relationships:
- //
- // struct Foo<'a, T> {
- // field: &'a T, // this would generate a ReEarlyBound referencing `'a`
- // }
- //
- // We care about these, so fall through.
- ty::ReEarlyBound(_) => true,
-
- // These correspond to `T: 'static` relationships which can be
- // rather surprising.
- //
- // struct Foo<'a, T> {
- // field: &'static T, // this would generate a ReStatic
- // }
- ty::ReStatic => false,
-
- // Late-bound regions can appear in `fn` types:
- //
- // struct Foo<T> {
- // field: for<'b> fn(&'b T) // e.g., 'b here
- // }
- //
- // The type above might generate a `T: 'b` bound, but we can
- // ignore it. We can't put it on the struct header anyway.
- ty::ReLateBound(..) => false,
-
- // This can appear in `where Self: ` bounds (#64855):
- //
- // struct Bar<T>(<Self as Foo>::Type) where Self: ;
- // struct Baz<'a>(&'a Self) where Self: ;
- ty::ReEmpty(_) => false,
-
- // These regions don't appear in types from type declarations:
- ty::ReErased | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReFree(..) => {
- bug!("unexpected region in outlives inference: {:?}", region);
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/variance/test.rs b/compiler/rustc_typeck/src/variance/test.rs
deleted file mode 100644
index 2ba87db88..000000000
--- a/compiler/rustc_typeck/src/variance/test.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-use rustc_errors::struct_span_err;
-use rustc_middle::ty::TyCtxt;
-use rustc_span::symbol::sym;
-
-pub fn test_variance(tcx: TyCtxt<'_>) {
- // For unit testing: check for a special "rustc_variance"
- // attribute and report an error with various results if found.
- for id in tcx.hir().items() {
- if tcx.has_attr(id.def_id.to_def_id(), sym::rustc_variance) {
- let variances_of = tcx.variances_of(id.def_id);
- struct_span_err!(tcx.sess, tcx.def_span(id.def_id), E0208, "{:?}", variances_of).emit();
- }
- }
-}