clean up check_consts
checks
This commit is contained in:
parent
324ca878ce
commit
df1a4c630d
1 changed files with 40 additions and 147 deletions
|
@ -5,17 +5,15 @@ use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_index::bit_set::BitSet;
|
use rustc_index::bit_set::BitSet;
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
|
use rustc_infer::traits::ObligationCause;
|
||||||
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::traits::BuiltinImplSource;
|
use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt};
|
||||||
use rustc_middle::ty::GenericArgs;
|
use rustc_middle::ty::{Instance, InstanceDef, TypeVisitableExt};
|
||||||
use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt};
|
|
||||||
use rustc_middle::ty::{TraitRef, TypeVisitableExt};
|
|
||||||
use rustc_mir_dataflow::Analysis;
|
use rustc_mir_dataflow::Analysis;
|
||||||
use rustc_span::{sym, Span, Symbol};
|
use rustc_span::{sym, Span, Symbol};
|
||||||
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
|
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
|
||||||
use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext};
|
use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt};
|
||||||
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitor};
|
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitor};
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -752,143 +750,43 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||||
infcx.err_ctxt().report_fulfillment_errors(errors);
|
infcx.err_ctxt().report_fulfillment_errors(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut is_trait = false;
|
||||||
// Attempting to call a trait method?
|
// Attempting to call a trait method?
|
||||||
// FIXME(effects) do we need this?
|
if tcx.trait_of_item(callee).is_some() {
|
||||||
if let Some(trait_id) = tcx.trait_of_item(callee) {
|
|
||||||
trace!("attempting to call a trait method");
|
trace!("attempting to call a trait method");
|
||||||
if !self.tcx.features().const_trait_impl {
|
// trait method calls are only permitted when `effects` is enabled.
|
||||||
|
// we don't error, since that is handled by typeck. We try to resolve
|
||||||
|
// the trait into the concrete method, and uses that for const stability
|
||||||
|
// checks.
|
||||||
|
// FIXME(effects) we might consider moving const stability checks to typeck as well.
|
||||||
|
if tcx.features().effects {
|
||||||
|
is_trait = true;
|
||||||
|
|
||||||
|
if let Ok(Some(instance)) =
|
||||||
|
Instance::resolve(tcx, param_env, callee, fn_args)
|
||||||
|
&& let InstanceDef::Item(def) = instance.def
|
||||||
|
{
|
||||||
|
// Resolve a trait method call to its concrete implementation, which may be in a
|
||||||
|
// `const` trait impl. This is only used for the const stability check below, since
|
||||||
|
// we want to look at the concrete impl's stability.
|
||||||
|
fn_args = instance.args;
|
||||||
|
callee = def;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
self.check_op(ops::FnCallNonConst {
|
self.check_op(ops::FnCallNonConst {
|
||||||
caller,
|
caller,
|
||||||
callee,
|
callee,
|
||||||
args: fn_args,
|
args: fn_args,
|
||||||
span: *fn_span,
|
span: *fn_span,
|
||||||
call_source: *call_source,
|
call_source: *call_source,
|
||||||
feature: Some(sym::const_trait_impl),
|
feature: Some(if tcx.features().const_trait_impl {
|
||||||
|
sym::effects
|
||||||
|
} else {
|
||||||
|
sym::const_trait_impl
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let trait_ref = TraitRef::from_method(tcx, trait_id, fn_args);
|
|
||||||
let obligation =
|
|
||||||
Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);
|
|
||||||
|
|
||||||
let implsrc = {
|
|
||||||
let infcx = tcx.infer_ctxt().build();
|
|
||||||
let mut selcx = SelectionContext::new(&infcx);
|
|
||||||
selcx.select(&obligation)
|
|
||||||
};
|
|
||||||
|
|
||||||
match implsrc {
|
|
||||||
Ok(Some(ImplSource::Param(_))) if tcx.features().effects => {
|
|
||||||
debug!(
|
|
||||||
"const_trait_impl: provided {:?} via where-clause in {:?}",
|
|
||||||
trait_ref, param_env
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Closure: Fn{Once|Mut}
|
|
||||||
Ok(Some(ImplSource::Builtin(BuiltinImplSource::Misc, _)))
|
|
||||||
if trait_ref.self_ty().is_closure()
|
|
||||||
&& tcx.fn_trait_kind_from_def_id(trait_id).is_some() =>
|
|
||||||
{
|
|
||||||
let ty::Closure(closure_def_id, fn_args) = *trait_ref.self_ty().kind()
|
|
||||||
else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
if !tcx.is_const_fn_raw(closure_def_id) {
|
|
||||||
self.check_op(ops::FnCallNonConst {
|
|
||||||
caller,
|
|
||||||
callee,
|
|
||||||
args: fn_args,
|
|
||||||
span: *fn_span,
|
|
||||||
call_source: *call_source,
|
|
||||||
feature: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Some(ImplSource::UserDefined(data))) => {
|
|
||||||
let callee_name = tcx.item_name(callee);
|
|
||||||
|
|
||||||
if let hir::Constness::NotConst = tcx.constness(data.impl_def_id) {
|
|
||||||
self.check_op(ops::FnCallNonConst {
|
|
||||||
caller,
|
|
||||||
callee,
|
|
||||||
args: fn_args,
|
|
||||||
span: *fn_span,
|
|
||||||
call_source: *call_source,
|
|
||||||
feature: None,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(&did) = tcx
|
|
||||||
.associated_item_def_ids(data.impl_def_id)
|
|
||||||
.iter()
|
|
||||||
.find(|did| tcx.item_name(**did) == callee_name)
|
|
||||||
{
|
|
||||||
// using internal args is ok here, since this is only
|
|
||||||
// used for the `resolve` call below
|
|
||||||
fn_args = GenericArgs::identity_for_item(tcx, did);
|
|
||||||
callee = did;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ if !tcx.is_const_fn_raw(callee) => {
|
|
||||||
// At this point, it is only legal when the caller is in a trait
|
|
||||||
// marked with #[const_trait], and the callee is in the same trait.
|
|
||||||
let mut nonconst_call_permission = false;
|
|
||||||
if let Some(callee_trait) = tcx.trait_of_item(callee)
|
|
||||||
&& tcx.has_attr(callee_trait, sym::const_trait)
|
|
||||||
&& Some(callee_trait) == tcx.trait_of_item(caller.to_def_id())
|
|
||||||
// Can only call methods when it's `<Self as TheTrait>::f`.
|
|
||||||
&& tcx.types.self_param == fn_args.type_at(0)
|
|
||||||
{
|
|
||||||
nonconst_call_permission = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !nonconst_call_permission {
|
|
||||||
let obligation = Obligation::new(
|
|
||||||
tcx,
|
|
||||||
ObligationCause::dummy_with_span(*fn_span),
|
|
||||||
param_env,
|
|
||||||
trait_ref,
|
|
||||||
);
|
|
||||||
|
|
||||||
// improve diagnostics by showing what failed. Our requirements are stricter this time
|
|
||||||
// as we are going to error again anyways.
|
|
||||||
let infcx = tcx.infer_ctxt().build();
|
|
||||||
if let Err(e) = implsrc {
|
|
||||||
infcx.err_ctxt().report_selection_error(
|
|
||||||
obligation.clone(),
|
|
||||||
&obligation,
|
|
||||||
&e,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.check_op(ops::FnCallNonConst {
|
|
||||||
caller,
|
|
||||||
callee,
|
|
||||||
args: fn_args,
|
|
||||||
span: *fn_span,
|
|
||||||
call_source: *call_source,
|
|
||||||
feature: None,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve a trait method call to its concrete implementation, which may be in a
|
|
||||||
// `const` trait impl.
|
|
||||||
let instance = Instance::resolve(tcx, param_env, callee, fn_args);
|
|
||||||
debug!("Resolving ({:?}) -> {:?}", callee, instance);
|
|
||||||
if let Ok(Some(func)) = instance {
|
|
||||||
if let InstanceDef::Item(def) = func.def {
|
|
||||||
callee = def;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, we are calling a function, `callee`, whose `DefId` is known...
|
// At this point, we are calling a function, `callee`, whose `DefId` is known...
|
||||||
|
@ -921,21 +819,16 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tcx.is_const_fn_raw(callee) {
|
if !tcx.is_const_fn_raw(callee) && !is_trait {
|
||||||
if !tcx.is_const_default_method(callee) {
|
self.check_op(ops::FnCallNonConst {
|
||||||
// To get to here we must have already found a const impl for the
|
caller,
|
||||||
// trait, but for it to still be non-const can be that the impl is
|
callee,
|
||||||
// using default method bodies.
|
args: fn_args,
|
||||||
self.check_op(ops::FnCallNonConst {
|
span: *fn_span,
|
||||||
caller,
|
call_source: *call_source,
|
||||||
callee,
|
feature: None,
|
||||||
args: fn_args,
|
});
|
||||||
span: *fn_span,
|
return;
|
||||||
call_source: *call_source,
|
|
||||||
feature: None,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the `const fn` we are trying to call is not const-stable, ensure that we have
|
// If the `const fn` we are trying to call is not const-stable, ensure that we have
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue