1
Fork 0

change TypeChecker to a MIR visitor

This commit is contained in:
lcnr 2025-03-11 16:08:38 +01:00
parent ba6c406854
commit 2f6aca8206
2 changed files with 396 additions and 430 deletions

View file

@ -174,7 +174,7 @@ pub(crate) fn type_check<'a, 'tcx>(
let mut verifier = TypeVerifier { typeck: &mut typeck, promoted, last_span: body.span }; let mut verifier = TypeVerifier { typeck: &mut typeck, promoted, last_span: body.span };
verifier.visit_body(body); verifier.visit_body(body);
typeck.typeck_mir(); typeck.visit_body(body);
typeck.equate_inputs_and_outputs(&normalized_inputs_and_output); typeck.equate_inputs_and_outputs(&normalized_inputs_and_output);
typeck.check_signature_annotation(); typeck.check_signature_annotation();
@ -543,7 +543,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
self.visit_body(promoted_body); self.visit_body(promoted_body);
self.typeck.typeck_mir(); self.typeck.visit_body(promoted_body);
self.typeck.body = parent_body; self.typeck.body = parent_body;
// Merge the outlives constraints back in, at the given location. // Merge the outlives constraints back in, at the given location.
@ -732,6 +732,10 @@ impl Locations {
} }
impl<'a, 'tcx> TypeChecker<'a, 'tcx> { impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
fn body(&self) -> &Body<'tcx> { fn body(&self) -> &Body<'tcx> {
self.body self.body
} }
@ -888,15 +892,38 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
Locations::All(span), Locations::All(span),
); );
} }
}
fn tcx(&self) -> TyCtxt<'tcx> { impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.infcx.tcx #[instrument(skip(self, body), level = "debug")]
fn visit_body(&mut self, body: &Body<'tcx>) {
debug_assert!(std::ptr::eq(self.body, body));
self.last_span = body.span;
debug!(?body.span);
for (local, local_decl) in body.local_decls.iter_enumerated() {
self.visit_local_decl(local, local_decl);
}
for (block, block_data) in body.basic_blocks.iter_enumerated() {
let mut location = Location { block, statement_index: 0 };
for stmt in &block_data.statements {
if !stmt.source_info.span.is_dummy() {
self.last_span = stmt.source_info.span;
}
self.visit_statement(stmt, location);
location.statement_index += 1;
}
self.visit_terminator(block_data.terminator(), location);
self.check_iscleanup(block_data);
}
} }
#[instrument(skip(self), level = "debug")] #[instrument(skip(self), level = "debug")]
fn check_stmt(&mut self, stmt: &Statement<'tcx>, location: Location) { fn visit_statement(&mut self, stmt: &Statement<'tcx>, location: Location) {
self.super_statement(stmt, location);
let tcx = self.tcx(); let tcx = self.tcx();
debug!("stmt kind: {:?}", stmt.kind);
match &stmt.kind { match &stmt.kind {
StatementKind::Assign(box (place, rv)) => { StatementKind::Assign(box (place, rv)) => {
// Assignments to temporaries are not "interesting"; // Assignments to temporaries are not "interesting";
@ -976,7 +1003,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
} }
} }
self.check_rvalue(rv, location);
if !self.unsized_feature_enabled() { if !self.unsized_feature_enabled() {
let trait_ref = ty::TraitRef::new( let trait_ref = ty::TraitRef::new(
tcx, tcx,
@ -1011,14 +1037,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
); );
} }
} }
StatementKind::Intrinsic(box kind) => match kind { StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(..))
NonDivergingIntrinsic::Assume(op) => self.check_operand(op, location), | StatementKind::FakeRead(..)
NonDivergingIntrinsic::CopyNonOverlapping(..) => span_bug!(
stmt.source_info.span,
"Unexpected NonDivergingIntrinsic::CopyNonOverlapping, should only appear after lowering_intrinsics",
),
},
StatementKind::FakeRead(..)
| StatementKind::StorageLive(..) | StatementKind::StorageLive(..)
| StatementKind::StorageDead(..) | StatementKind::StorageDead(..)
| StatementKind::Retag { .. } | StatementKind::Retag { .. }
@ -1027,14 +1047,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
| StatementKind::PlaceMention(..) | StatementKind::PlaceMention(..)
| StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::BackwardIncompatibleDropHint { .. }
| StatementKind::Nop => {} | StatementKind::Nop => {}
StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => { StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(..))
| StatementKind::Deinit(..)
| StatementKind::SetDiscriminant { .. } => {
bug!("Statement not allowed in this MIR phase") bug!("Statement not allowed in this MIR phase")
} }
} }
} }
#[instrument(skip(self, term_location), level = "debug")] #[instrument(skip(self), level = "debug")]
fn check_terminator(&mut self, term: &Terminator<'tcx>, term_location: Location) { fn visit_terminator(&mut self, term: &Terminator<'tcx>, term_location: Location) {
self.super_terminator(term, term_location);
let tcx = self.tcx(); let tcx = self.tcx();
debug!("terminator kind: {:?}", term.kind); debug!("terminator kind: {:?}", term.kind);
match &term.kind { match &term.kind {
@ -1052,8 +1075,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
} }
TerminatorKind::SwitchInt { discr, .. } => { TerminatorKind::SwitchInt { discr, .. } => {
self.check_operand(discr, term_location);
let switch_ty = discr.ty(self.body, tcx); let switch_ty = discr.ty(self.body, tcx);
if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() { if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() {
span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty); span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty);
@ -1068,11 +1089,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
_ => unreachable!(), _ => unreachable!(),
}; };
self.check_operand(func, term_location);
for arg in args {
self.check_operand(&arg.node, term_location);
}
let func_ty = func.ty(self.body, tcx); let func_ty = func.ty(self.body, tcx);
debug!("func_ty.kind: {:?}", func_ty.kind()); debug!("func_ty.kind: {:?}", func_ty.kind());
@ -1159,8 +1175,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.check_call_inputs(term, func, &sig, args, term_location, call_source); self.check_call_inputs(term, func, &sig, args, term_location, call_source);
} }
TerminatorKind::Assert { cond, msg, .. } => { TerminatorKind::Assert { cond, msg, .. } => {
self.check_operand(cond, term_location);
let cond_ty = cond.ty(self.body, tcx); let cond_ty = cond.ty(self.body, tcx);
if cond_ty != tcx.types.bool { if cond_ty != tcx.types.bool {
span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty); span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty);
@ -1176,8 +1190,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
} }
} }
TerminatorKind::Yield { value, resume_arg, .. } => { TerminatorKind::Yield { value, resume_arg, .. } => {
self.check_operand(value, term_location);
match self.body.yield_ty() { match self.body.yield_ty() {
None => span_mirbug!(self, term, "yield in non-coroutine"), None => span_mirbug!(self, term, "yield in non-coroutine"),
Some(ty) => { Some(ty) => {
@ -1225,240 +1237,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
} }
} }
fn check_call_dest( fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) {
&mut self, self.super_local_decl(local, local_decl);
term: &Terminator<'tcx>,
sig: &ty::FnSig<'tcx>,
destination: Place<'tcx>,
target: Option<BasicBlock>,
term_location: Location,
) {
let tcx = self.tcx();
match target {
Some(_) => {
let dest_ty = destination.ty(self.body, tcx).ty;
let dest_ty = self.normalize(dest_ty, term_location);
let category = match destination.as_local() {
Some(RETURN_PLACE) => {
if let DefiningTy::Const(def_id, _) | DefiningTy::InlineConst(def_id, _) =
self.universal_regions.defining_ty
{
if tcx.is_static(def_id) {
ConstraintCategory::UseAsStatic
} else {
ConstraintCategory::UseAsConst
}
} else {
ConstraintCategory::Return(ReturnConstraint::Normal)
}
}
Some(l) if !self.body.local_decls[l].is_user_variable() => {
ConstraintCategory::Boring
}
// The return type of a call is interesting for diagnostics.
_ => ConstraintCategory::Assignment,
};
let locations = term_location.to_locations();
if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) {
span_mirbug!(
self,
term,
"call dest mismatch ({:?} <- {:?}): {:?}",
dest_ty,
sig.output(),
terr
);
}
// When `unsized_fn_params` and `unsized_locals` are both not enabled,
// this check is done at `check_local`.
if self.unsized_feature_enabled() {
let span = term.source_info.span;
self.ensure_place_sized(dest_ty, span);
}
}
None => {
// The signature in this call can reference region variables,
// so erase them before calling a query.
let output_ty = self.tcx().erase_regions(sig.output());
if !output_ty.is_privately_uninhabited(
self.tcx(),
self.infcx.typing_env(self.infcx.param_env),
) {
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
}
}
}
}
#[instrument(level = "debug", skip(self, term, func, term_location, call_source))]
fn check_call_inputs(
&mut self,
term: &Terminator<'tcx>,
func: &Operand<'tcx>,
sig: &ty::FnSig<'tcx>,
args: &[Spanned<Operand<'tcx>>],
term_location: Location,
call_source: CallSource,
) {
if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.c_variadic) {
span_mirbug!(self, term, "call to {:?} with wrong # of args", sig);
}
let func_ty = func.ty(self.body, self.infcx.tcx);
if let ty::FnDef(def_id, _) = *func_ty.kind() {
// Some of the SIMD intrinsics are special: they need a particular argument to be a
// constant. (Eventually this should use const-generics, but those are not up for the
// task yet: https://github.com/rust-lang/rust/issues/85229.)
if let Some(name @ (sym::simd_shuffle | sym::simd_insert | sym::simd_extract)) =
self.tcx().intrinsic(def_id).map(|i| i.name)
{
let idx = match name {
sym::simd_shuffle => 2,
_ => 1,
};
if !matches!(args[idx], Spanned { node: Operand::Constant(_), .. }) {
self.tcx().dcx().emit_err(SimdIntrinsicArgConst {
span: term.source_info.span,
arg: idx + 1,
intrinsic: name.to_string(),
});
}
}
}
debug!(?func_ty);
for (n, (fn_arg, op_arg)) in iter::zip(sig.inputs(), args).enumerate() {
let op_arg_ty = op_arg.node.ty(self.body, self.tcx());
let op_arg_ty = self.normalize(op_arg_ty, term_location);
let category = if call_source.from_hir_call() {
ConstraintCategory::CallArgument(Some(self.infcx.tcx.erase_regions(func_ty)))
} else {
ConstraintCategory::Boring
};
if let Err(terr) =
self.sub_types(op_arg_ty, *fn_arg, term_location.to_locations(), category)
{
span_mirbug!(
self,
term,
"bad arg #{:?} ({:?} <- {:?}): {:?}",
n,
fn_arg,
op_arg_ty,
terr
);
}
}
}
fn check_iscleanup(&mut self, block_data: &BasicBlockData<'tcx>) {
let is_cleanup = block_data.is_cleanup;
self.last_span = block_data.terminator().source_info.span;
match block_data.terminator().kind {
TerminatorKind::Goto { target } => {
self.assert_iscleanup(block_data, target, is_cleanup)
}
TerminatorKind::SwitchInt { ref targets, .. } => {
for target in targets.all_targets() {
self.assert_iscleanup(block_data, *target, is_cleanup);
}
}
TerminatorKind::UnwindResume => {
if !is_cleanup {
span_mirbug!(self, block_data, "resume on non-cleanup block!")
}
}
TerminatorKind::UnwindTerminate(_) => {
if !is_cleanup {
span_mirbug!(self, block_data, "terminate on non-cleanup block!")
}
}
TerminatorKind::Return => {
if is_cleanup {
span_mirbug!(self, block_data, "return on cleanup block")
}
}
TerminatorKind::TailCall { .. } => {
if is_cleanup {
span_mirbug!(self, block_data, "tailcall on cleanup block")
}
}
TerminatorKind::CoroutineDrop { .. } => {
if is_cleanup {
span_mirbug!(self, block_data, "coroutine_drop in cleanup block")
}
}
TerminatorKind::Yield { resume, drop, .. } => {
if is_cleanup {
span_mirbug!(self, block_data, "yield in cleanup block")
}
self.assert_iscleanup(block_data, resume, is_cleanup);
if let Some(drop) = drop {
self.assert_iscleanup(block_data, drop, is_cleanup);
}
}
TerminatorKind::Unreachable => {}
TerminatorKind::Drop { target, unwind, .. }
| TerminatorKind::Assert { target, unwind, .. } => {
self.assert_iscleanup(block_data, target, is_cleanup);
self.assert_iscleanup_unwind(block_data, unwind, is_cleanup);
}
TerminatorKind::Call { ref target, unwind, .. } => {
if let &Some(target) = target {
self.assert_iscleanup(block_data, target, is_cleanup);
}
self.assert_iscleanup_unwind(block_data, unwind, is_cleanup);
}
TerminatorKind::FalseEdge { real_target, imaginary_target } => {
self.assert_iscleanup(block_data, real_target, is_cleanup);
self.assert_iscleanup(block_data, imaginary_target, is_cleanup);
}
TerminatorKind::FalseUnwind { real_target, unwind } => {
self.assert_iscleanup(block_data, real_target, is_cleanup);
self.assert_iscleanup_unwind(block_data, unwind, is_cleanup);
}
TerminatorKind::InlineAsm { ref targets, unwind, .. } => {
for &target in targets {
self.assert_iscleanup(block_data, target, is_cleanup);
}
self.assert_iscleanup_unwind(block_data, unwind, is_cleanup);
}
}
}
fn assert_iscleanup(&mut self, ctxt: &dyn fmt::Debug, bb: BasicBlock, iscleanuppad: bool) {
if self.body[bb].is_cleanup != iscleanuppad {
span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}", bb, iscleanuppad);
}
}
fn assert_iscleanup_unwind(
&mut self,
ctxt: &dyn fmt::Debug,
unwind: UnwindAction,
is_cleanup: bool,
) {
match unwind {
UnwindAction::Cleanup(unwind) => {
if is_cleanup {
span_mirbug!(self, ctxt, "unwind on cleanup block")
}
self.assert_iscleanup(ctxt, unwind, true);
}
UnwindAction::Continue => {
if is_cleanup {
span_mirbug!(self, ctxt, "unwind on cleanup block")
}
}
UnwindAction::Unreachable | UnwindAction::Terminate(_) => (),
}
}
fn check_local(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) {
match self.body.local_kind(local) { match self.body.local_kind(local) {
LocalKind::ReturnPointer | LocalKind::Arg => { LocalKind::ReturnPointer | LocalKind::Arg => {
// return values of normal functions are required to be // return values of normal functions are required to be
@ -1481,129 +1261,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
} }
} }
fn ensure_place_sized(&mut self, ty: Ty<'tcx>, span: Span) {
let tcx = self.tcx();
// Erase the regions from `ty` to get a global type. The
// `Sized` bound in no way depends on precise regions, so this
// shouldn't affect `is_sized`.
let erased_ty = tcx.erase_regions(ty);
// FIXME(#132279): Using `Ty::is_sized` causes us to incorrectly handle opaques here.
if !erased_ty.is_sized(tcx, self.infcx.typing_env(self.infcx.param_env)) {
// in current MIR construction, all non-control-flow rvalue
// expressions evaluate through `as_temp` or `into` a return
// slot or local, so to find all unsized rvalues it is enough
// to check all temps, return slots and locals.
if self.reported_errors.replace((ty, span)).is_none() {
// While this is located in `nll::typeck` this error is not
// an NLL error, it's a required check to prevent creation
// of unsized rvalues in a call expression.
self.tcx().dcx().emit_err(MoveUnsized { ty, span });
}
}
}
fn aggregate_field_ty(
&mut self,
ak: &AggregateKind<'tcx>,
field_index: FieldIdx,
location: Location,
) -> Result<Ty<'tcx>, FieldAccessError> {
let tcx = self.tcx();
match *ak {
AggregateKind::Adt(adt_did, variant_index, args, _, active_field_index) => {
let def = tcx.adt_def(adt_did);
let variant = &def.variant(variant_index);
let adj_field_index = active_field_index.unwrap_or(field_index);
if let Some(field) = variant.fields.get(adj_field_index) {
Ok(self.normalize(field.ty(tcx, args), location))
} else {
Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
}
}
AggregateKind::Closure(_, args) => {
match args.as_closure().upvar_tys().get(field_index.as_usize()) {
Some(ty) => Ok(*ty),
None => Err(FieldAccessError::OutOfRange {
field_count: args.as_closure().upvar_tys().len(),
}),
}
}
AggregateKind::Coroutine(_, args) => {
// It doesn't make sense to look at a field beyond the prefix;
// these require a variant index, and are not initialized in
// aggregate rvalues.
match args.as_coroutine().prefix_tys().get(field_index.as_usize()) {
Some(ty) => Ok(*ty),
None => Err(FieldAccessError::OutOfRange {
field_count: args.as_coroutine().prefix_tys().len(),
}),
}
}
AggregateKind::CoroutineClosure(_, args) => {
match args.as_coroutine_closure().upvar_tys().get(field_index.as_usize()) {
Some(ty) => Ok(*ty),
None => Err(FieldAccessError::OutOfRange {
field_count: args.as_coroutine_closure().upvar_tys().len(),
}),
}
}
AggregateKind::Array(ty) => Ok(ty),
AggregateKind::Tuple | AggregateKind::RawPtr(..) => {
unreachable!("This should have been covered in check_rvalues");
}
}
}
fn check_operand(&mut self, op: &Operand<'tcx>, location: Location) {
debug!(?op, ?location, "check_operand");
if let Operand::Constant(constant) = op {
let maybe_uneval = match constant.const_ {
Const::Val(..) | Const::Ty(_, _) => None,
Const::Unevaluated(uv, _) => Some(uv),
};
if let Some(uv) = maybe_uneval {
if uv.promoted.is_none() {
let tcx = self.tcx();
let def_id = uv.def;
if tcx.def_kind(def_id) == DefKind::InlineConst {
let def_id = def_id.expect_local();
let predicates = self.prove_closure_bounds(
tcx,
def_id,
uv.args,
location.to_locations(),
);
self.normalize_and_prove_instantiated_predicates(
def_id.to_def_id(),
predicates,
location.to_locations(),
);
}
}
}
}
}
#[instrument(skip(self), level = "debug")] #[instrument(skip(self), level = "debug")]
fn check_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
self.super_rvalue(rvalue, location);
let tcx = self.tcx(); let tcx = self.tcx();
let span = self.body.source_info(location).span; let span = self.body.source_info(location).span;
match rvalue { match rvalue {
Rvalue::Aggregate(ak, ops) => { Rvalue::Aggregate(ak, ops) => self.check_aggregate_rvalue(rvalue, ak, ops, location),
for op in ops {
self.check_operand(op, location);
}
self.check_aggregate_rvalue(rvalue, ak, ops, location)
}
Rvalue::Repeat(operand, len) => { Rvalue::Repeat(operand, len) => {
self.check_operand(operand, location);
let array_ty = rvalue.ty(self.body.local_decls(), tcx); let array_ty = rvalue.ty(self.body.local_decls(), tcx);
self.prove_predicate( self.prove_predicate(
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(array_ty.into())), ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(array_ty.into())),
@ -1656,9 +1322,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
&Rvalue::NullaryOp(NullOp::ContractChecks, _) => {} &Rvalue::NullaryOp(NullOp::ContractChecks, _) => {}
&Rvalue::NullaryOp(NullOp::UbChecks, _) => {} &Rvalue::NullaryOp(NullOp::UbChecks, _) => {}
Rvalue::ShallowInitBox(operand, ty) => { Rvalue::ShallowInitBox(_operand, ty) => {
self.check_operand(operand, location);
let trait_ref = ty::TraitRef::new( let trait_ref = ty::TraitRef::new(
tcx, tcx,
tcx.require_lang_item(LangItem::Sized, Some(span)), tcx.require_lang_item(LangItem::Sized, Some(span)),
@ -1673,8 +1337,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
} }
Rvalue::Cast(cast_kind, op, ty) => { Rvalue::Cast(cast_kind, op, ty) => {
self.check_operand(op, location);
match *cast_kind { match *cast_kind {
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, coercion_source) => { CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, coercion_source) => {
let is_implicit_coercion = coercion_source == CoercionSource::Implicit; let is_implicit_coercion = coercion_source == CoercionSource::Implicit;
@ -2191,9 +1853,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge,
box (left, right), box (left, right),
) => { ) => {
self.check_operand(left, location);
self.check_operand(right, location);
let ty_left = left.ty(self.body, tcx); let ty_left = left.ty(self.body, tcx);
match ty_left.kind() { match ty_left.kind() {
// Types with regions are comparable if they have a common super-type. // Types with regions are comparable if they have a common super-type.
@ -2242,23 +1901,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
} }
} }
Rvalue::Use(operand) | Rvalue::UnaryOp(_, operand) => {
self.check_operand(operand, location);
}
Rvalue::CopyForDeref(place) => {
let op = &Operand::Copy(*place);
self.check_operand(op, location);
}
Rvalue::BinaryOp(_, box (left, right)) => {
self.check_operand(left, location);
self.check_operand(right, location);
}
Rvalue::WrapUnsafeBinder(op, ty) => { Rvalue::WrapUnsafeBinder(op, ty) => {
self.check_operand(op, location);
let operand_ty = op.ty(self.body, self.tcx()); let operand_ty = op.ty(self.body, self.tcx());
let ty::UnsafeBinder(binder_ty) = *ty.kind() else { let ty::UnsafeBinder(binder_ty) = *ty.kind() else {
unreachable!(); unreachable!();
}; };
@ -2276,7 +1920,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
.unwrap(); .unwrap();
} }
Rvalue::RawPtr(..) Rvalue::Use(_)
| Rvalue::UnaryOp(_, _)
| Rvalue::CopyForDeref(_)
| Rvalue::BinaryOp(..)
| Rvalue::RawPtr(..)
| Rvalue::ThreadLocalRef(..) | Rvalue::ThreadLocalRef(..)
| Rvalue::Len(..) | Rvalue::Len(..)
| Rvalue::Discriminant(..) | Rvalue::Discriminant(..)
@ -2284,6 +1932,348 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
} }
} }
#[instrument(level = "debug", skip(self))]
fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) {
self.super_operand(op, location);
if let Operand::Constant(constant) = op {
let maybe_uneval = match constant.const_ {
Const::Val(..) | Const::Ty(_, _) => None,
Const::Unevaluated(uv, _) => Some(uv),
};
if let Some(uv) = maybe_uneval {
if uv.promoted.is_none() {
let tcx = self.tcx();
let def_id = uv.def;
if tcx.def_kind(def_id) == DefKind::InlineConst {
let def_id = def_id.expect_local();
let predicates = self.prove_closure_bounds(
tcx,
def_id,
uv.args,
location.to_locations(),
);
self.normalize_and_prove_instantiated_predicates(
def_id.to_def_id(),
predicates,
location.to_locations(),
);
}
}
}
}
}
}
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
fn check_call_dest(
&mut self,
term: &Terminator<'tcx>,
sig: &ty::FnSig<'tcx>,
destination: Place<'tcx>,
target: Option<BasicBlock>,
term_location: Location,
) {
let tcx = self.tcx();
match target {
Some(_) => {
let dest_ty = destination.ty(self.body, tcx).ty;
let dest_ty = self.normalize(dest_ty, term_location);
let category = match destination.as_local() {
Some(RETURN_PLACE) => {
if let DefiningTy::Const(def_id, _) | DefiningTy::InlineConst(def_id, _) =
self.universal_regions.defining_ty
{
if tcx.is_static(def_id) {
ConstraintCategory::UseAsStatic
} else {
ConstraintCategory::UseAsConst
}
} else {
ConstraintCategory::Return(ReturnConstraint::Normal)
}
}
Some(l) if !self.body.local_decls[l].is_user_variable() => {
ConstraintCategory::Boring
}
// The return type of a call is interesting for diagnostics.
_ => ConstraintCategory::Assignment,
};
let locations = term_location.to_locations();
if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations, category) {
span_mirbug!(
self,
term,
"call dest mismatch ({:?} <- {:?}): {:?}",
dest_ty,
sig.output(),
terr
);
}
// When `unsized_fn_params` and `unsized_locals` are both not enabled,
// this check is done at `check_local`.
if self.unsized_feature_enabled() {
let span = term.source_info.span;
self.ensure_place_sized(dest_ty, span);
}
}
None => {
// The signature in this call can reference region variables,
// so erase them before calling a query.
let output_ty = self.tcx().erase_regions(sig.output());
if !output_ty.is_privately_uninhabited(
self.tcx(),
self.infcx.typing_env(self.infcx.param_env),
) {
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
}
}
}
}
#[instrument(level = "debug", skip(self, term, func, term_location, call_source))]
fn check_call_inputs(
&mut self,
term: &Terminator<'tcx>,
func: &Operand<'tcx>,
sig: &ty::FnSig<'tcx>,
args: &[Spanned<Operand<'tcx>>],
term_location: Location,
call_source: CallSource,
) {
if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.c_variadic) {
span_mirbug!(self, term, "call to {:?} with wrong # of args", sig);
}
let func_ty = func.ty(self.body, self.infcx.tcx);
if let ty::FnDef(def_id, _) = *func_ty.kind() {
// Some of the SIMD intrinsics are special: they need a particular argument to be a
// constant. (Eventually this should use const-generics, but those are not up for the
// task yet: https://github.com/rust-lang/rust/issues/85229.)
if let Some(name @ (sym::simd_shuffle | sym::simd_insert | sym::simd_extract)) =
self.tcx().intrinsic(def_id).map(|i| i.name)
{
let idx = match name {
sym::simd_shuffle => 2,
_ => 1,
};
if !matches!(args[idx], Spanned { node: Operand::Constant(_), .. }) {
self.tcx().dcx().emit_err(SimdIntrinsicArgConst {
span: term.source_info.span,
arg: idx + 1,
intrinsic: name.to_string(),
});
}
}
}
debug!(?func_ty);
for (n, (fn_arg, op_arg)) in iter::zip(sig.inputs(), args).enumerate() {
let op_arg_ty = op_arg.node.ty(self.body, self.tcx());
let op_arg_ty = self.normalize(op_arg_ty, term_location);
let category = if call_source.from_hir_call() {
ConstraintCategory::CallArgument(Some(self.infcx.tcx.erase_regions(func_ty)))
} else {
ConstraintCategory::Boring
};
if let Err(terr) =
self.sub_types(op_arg_ty, *fn_arg, term_location.to_locations(), category)
{
span_mirbug!(
self,
term,
"bad arg #{:?} ({:?} <- {:?}): {:?}",
n,
fn_arg,
op_arg_ty,
terr
);
}
}
}
fn check_iscleanup(&mut self, block_data: &BasicBlockData<'tcx>) {
let is_cleanup = block_data.is_cleanup;
self.last_span = block_data.terminator().source_info.span;
match block_data.terminator().kind {
TerminatorKind::Goto { target } => {
self.assert_iscleanup(block_data, target, is_cleanup)
}
TerminatorKind::SwitchInt { ref targets, .. } => {
for target in targets.all_targets() {
self.assert_iscleanup(block_data, *target, is_cleanup);
}
}
TerminatorKind::UnwindResume => {
if !is_cleanup {
span_mirbug!(self, block_data, "resume on non-cleanup block!")
}
}
TerminatorKind::UnwindTerminate(_) => {
if !is_cleanup {
span_mirbug!(self, block_data, "terminate on non-cleanup block!")
}
}
TerminatorKind::Return => {
if is_cleanup {
span_mirbug!(self, block_data, "return on cleanup block")
}
}
TerminatorKind::TailCall { .. } => {
if is_cleanup {
span_mirbug!(self, block_data, "tailcall on cleanup block")
}
}
TerminatorKind::CoroutineDrop { .. } => {
if is_cleanup {
span_mirbug!(self, block_data, "coroutine_drop in cleanup block")
}
}
TerminatorKind::Yield { resume, drop, .. } => {
if is_cleanup {
span_mirbug!(self, block_data, "yield in cleanup block")
}
self.assert_iscleanup(block_data, resume, is_cleanup);
if let Some(drop) = drop {
self.assert_iscleanup(block_data, drop, is_cleanup);
}
}
TerminatorKind::Unreachable => {}
TerminatorKind::Drop { target, unwind, .. }
| TerminatorKind::Assert { target, unwind, .. } => {
self.assert_iscleanup(block_data, target, is_cleanup);
self.assert_iscleanup_unwind(block_data, unwind, is_cleanup);
}
TerminatorKind::Call { ref target, unwind, .. } => {
if let &Some(target) = target {
self.assert_iscleanup(block_data, target, is_cleanup);
}
self.assert_iscleanup_unwind(block_data, unwind, is_cleanup);
}
TerminatorKind::FalseEdge { real_target, imaginary_target } => {
self.assert_iscleanup(block_data, real_target, is_cleanup);
self.assert_iscleanup(block_data, imaginary_target, is_cleanup);
}
TerminatorKind::FalseUnwind { real_target, unwind } => {
self.assert_iscleanup(block_data, real_target, is_cleanup);
self.assert_iscleanup_unwind(block_data, unwind, is_cleanup);
}
TerminatorKind::InlineAsm { ref targets, unwind, .. } => {
for &target in targets {
self.assert_iscleanup(block_data, target, is_cleanup);
}
self.assert_iscleanup_unwind(block_data, unwind, is_cleanup);
}
}
}
fn assert_iscleanup(&mut self, ctxt: &dyn fmt::Debug, bb: BasicBlock, iscleanuppad: bool) {
if self.body[bb].is_cleanup != iscleanuppad {
span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}", bb, iscleanuppad);
}
}
fn assert_iscleanup_unwind(
&mut self,
ctxt: &dyn fmt::Debug,
unwind: UnwindAction,
is_cleanup: bool,
) {
match unwind {
UnwindAction::Cleanup(unwind) => {
if is_cleanup {
span_mirbug!(self, ctxt, "unwind on cleanup block")
}
self.assert_iscleanup(ctxt, unwind, true);
}
UnwindAction::Continue => {
if is_cleanup {
span_mirbug!(self, ctxt, "unwind on cleanup block")
}
}
UnwindAction::Unreachable | UnwindAction::Terminate(_) => (),
}
}
fn ensure_place_sized(&mut self, ty: Ty<'tcx>, span: Span) {
let tcx = self.tcx();
// Erase the regions from `ty` to get a global type. The
// `Sized` bound in no way depends on precise regions, so this
// shouldn't affect `is_sized`.
let erased_ty = tcx.erase_regions(ty);
// FIXME(#132279): Using `Ty::is_sized` causes us to incorrectly handle opaques here.
if !erased_ty.is_sized(tcx, self.infcx.typing_env(self.infcx.param_env)) {
// in current MIR construction, all non-control-flow rvalue
// expressions evaluate through `as_temp` or `into` a return
// slot or local, so to find all unsized rvalues it is enough
// to check all temps, return slots and locals.
if self.reported_errors.replace((ty, span)).is_none() {
// While this is located in `nll::typeck` this error is not
// an NLL error, it's a required check to prevent creation
// of unsized rvalues in a call expression.
self.tcx().dcx().emit_err(MoveUnsized { ty, span });
}
}
}
fn aggregate_field_ty(
&mut self,
ak: &AggregateKind<'tcx>,
field_index: FieldIdx,
location: Location,
) -> Result<Ty<'tcx>, FieldAccessError> {
let tcx = self.tcx();
match *ak {
AggregateKind::Adt(adt_did, variant_index, args, _, active_field_index) => {
let def = tcx.adt_def(adt_did);
let variant = &def.variant(variant_index);
let adj_field_index = active_field_index.unwrap_or(field_index);
if let Some(field) = variant.fields.get(adj_field_index) {
Ok(self.normalize(field.ty(tcx, args), location))
} else {
Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
}
}
AggregateKind::Closure(_, args) => {
match args.as_closure().upvar_tys().get(field_index.as_usize()) {
Some(ty) => Ok(*ty),
None => Err(FieldAccessError::OutOfRange {
field_count: args.as_closure().upvar_tys().len(),
}),
}
}
AggregateKind::Coroutine(_, args) => {
// It doesn't make sense to look at a field beyond the prefix;
// these require a variant index, and are not initialized in
// aggregate rvalues.
match args.as_coroutine().prefix_tys().get(field_index.as_usize()) {
Some(ty) => Ok(*ty),
None => Err(FieldAccessError::OutOfRange {
field_count: args.as_coroutine().prefix_tys().len(),
}),
}
}
AggregateKind::CoroutineClosure(_, args) => {
match args.as_coroutine_closure().upvar_tys().get(field_index.as_usize()) {
Some(ty) => Ok(*ty),
None => Err(FieldAccessError::OutOfRange {
field_count: args.as_coroutine_closure().upvar_tys().len(),
}),
}
}
AggregateKind::Array(ty) => Ok(ty),
AggregateKind::Tuple | AggregateKind::RawPtr(..) => {
unreachable!("This should have been covered in check_rvalues");
}
}
}
/// If this rvalue supports a user-given type annotation, then /// If this rvalue supports a user-given type annotation, then
/// extract and return it. This represents the final type of the /// extract and return it. This represents the final type of the
/// rvalue and will be unified with the inferred type. /// rvalue and will be unified with the inferred type.
@ -2623,30 +2613,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
tcx.predicates_of(def_id).instantiate(tcx, args) tcx.predicates_of(def_id).instantiate(tcx, args)
} }
#[instrument(skip(self), level = "debug")]
fn typeck_mir(&mut self) {
self.last_span = self.body.span;
debug!(?self.body.span);
for (local, local_decl) in self.body.local_decls.iter_enumerated() {
self.check_local(local, local_decl);
}
for (block, block_data) in self.body.basic_blocks.iter_enumerated() {
let mut location = Location { block, statement_index: 0 };
for stmt in &block_data.statements {
if !stmt.source_info.span.is_dummy() {
self.last_span = stmt.source_info.span;
}
self.check_stmt(stmt, location);
location.statement_index += 1;
}
self.check_terminator(block_data.terminator(), location);
self.check_iscleanup(block_data);
}
}
} }
trait NormalizeLocation: fmt::Debug + Copy { trait NormalizeLocation: fmt::Debug + Copy {

View file

@ -7,12 +7,12 @@ LL | let _ = x as &dyn Bar<'static, 'a>; // Error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'a` must outlive `'static` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'a` must outlive `'static`
error: lifetime may not live long enough error: lifetime may not live long enough
--> $DIR/type-checking-test-4.rs:22:18 --> $DIR/type-checking-test-4.rs:22:13
| |
LL | fn test_wrong2<'a>(x: &dyn Foo<'static>, y: &'a u32) { LL | fn test_wrong2<'a>(x: &dyn Foo<'static>, y: &'a u32) {
| -- lifetime `'a` defined here | -- lifetime `'a` defined here
LL | let _ = x as &dyn Bar<'a, 'static>; // Error LL | let _ = x as &dyn Bar<'a, 'static>; // Error
| ^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'a` must outlive `'static`
error: lifetime may not live long enough error: lifetime may not live long enough
--> $DIR/type-checking-test-4.rs:28:5 --> $DIR/type-checking-test-4.rs:28:5