Auto merge of #122150 - ShoyuVanilla:replace-typewalker, r=lcnr
Replace `TypeWalker` usage with `TypeVisitor` in `wf.rs` Resolves #121693
This commit is contained in:
commit
b054da8155
3 changed files with 311 additions and 301 deletions
|
@ -2,7 +2,9 @@ use crate::infer::InferCtxt;
|
||||||
use crate::traits;
|
use crate::traits;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::lang_items::LangItem;
|
use rustc_hir::lang_items::LangItem;
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
|
use rustc_middle::ty::{
|
||||||
|
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||||
|
};
|
||||||
use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef};
|
use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef};
|
||||||
use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
|
use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
|
||||||
use rustc_span::{Span, DUMMY_SP};
|
use rustc_span::{Span, DUMMY_SP};
|
||||||
|
@ -535,305 +537,8 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
||||||
/// Pushes all the predicates needed to validate that `ty` is WF into `out`.
|
/// Pushes all the predicates needed to validate that `ty` is WF into `out`.
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
fn compute(&mut self, arg: GenericArg<'tcx>) {
|
fn compute(&mut self, arg: GenericArg<'tcx>) {
|
||||||
let mut walker = arg.walk();
|
arg.visit_with(self);
|
||||||
let param_env = self.param_env;
|
debug!(?self.out);
|
||||||
let depth = self.recursion_depth;
|
|
||||||
while let Some(arg) = walker.next() {
|
|
||||||
debug!(?arg, ?self.out);
|
|
||||||
let ty = match arg.unpack() {
|
|
||||||
GenericArgKind::Type(ty) => ty,
|
|
||||||
|
|
||||||
// No WF constraints for lifetimes being present, any outlives
|
|
||||||
// obligations are handled by the parent (e.g. `ty::Ref`).
|
|
||||||
GenericArgKind::Lifetime(_) => continue,
|
|
||||||
|
|
||||||
GenericArgKind::Const(ct) => {
|
|
||||||
match ct.kind() {
|
|
||||||
ty::ConstKind::Unevaluated(uv) => {
|
|
||||||
if !ct.has_escaping_bound_vars() {
|
|
||||||
let obligations = self.nominal_obligations(uv.def, uv.args);
|
|
||||||
self.out.extend(obligations);
|
|
||||||
|
|
||||||
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
|
|
||||||
ty::ClauseKind::ConstEvaluatable(ct),
|
|
||||||
));
|
|
||||||
let cause = self.cause(traits::WellFormed(None));
|
|
||||||
self.out.push(traits::Obligation::with_depth(
|
|
||||||
self.tcx(),
|
|
||||||
cause,
|
|
||||||
self.recursion_depth,
|
|
||||||
self.param_env,
|
|
||||||
predicate,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ty::ConstKind::Infer(_) => {
|
|
||||||
let cause = self.cause(traits::WellFormed(None));
|
|
||||||
|
|
||||||
self.out.push(traits::Obligation::with_depth(
|
|
||||||
self.tcx(),
|
|
||||||
cause,
|
|
||||||
self.recursion_depth,
|
|
||||||
self.param_env,
|
|
||||||
ty::Binder::dummy(ty::PredicateKind::Clause(
|
|
||||||
ty::ClauseKind::WellFormed(ct.into()),
|
|
||||||
)),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
ty::ConstKind::Expr(_) => {
|
|
||||||
// FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the
|
|
||||||
// trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary
|
|
||||||
// as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated`
|
|
||||||
// which means that the `DefId` would have been typeck'd elsewhere. However in
|
|
||||||
// the future we may allow directly lowering to `ConstKind::Expr` in which case
|
|
||||||
// we would not be proving bounds we should.
|
|
||||||
|
|
||||||
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
|
|
||||||
ty::ClauseKind::ConstEvaluatable(ct),
|
|
||||||
));
|
|
||||||
let cause = self.cause(traits::WellFormed(None));
|
|
||||||
self.out.push(traits::Obligation::with_depth(
|
|
||||||
self.tcx(),
|
|
||||||
cause,
|
|
||||||
self.recursion_depth,
|
|
||||||
self.param_env,
|
|
||||||
predicate,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::ConstKind::Error(_)
|
|
||||||
| ty::ConstKind::Param(_)
|
|
||||||
| ty::ConstKind::Bound(..)
|
|
||||||
| ty::ConstKind::Placeholder(..) => {
|
|
||||||
// These variants are trivially WF, so nothing to do here.
|
|
||||||
}
|
|
||||||
ty::ConstKind::Value(..) => {
|
|
||||||
// FIXME: Enforce that values are structurally-matchable.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!("wf bounds for ty={:?} ty.kind={:#?}", ty, ty.kind());
|
|
||||||
|
|
||||||
match *ty.kind() {
|
|
||||||
ty::Bool
|
|
||||||
| ty::Char
|
|
||||||
| ty::Int(..)
|
|
||||||
| ty::Uint(..)
|
|
||||||
| ty::Float(..)
|
|
||||||
| ty::Error(_)
|
|
||||||
| ty::Str
|
|
||||||
| ty::CoroutineWitness(..)
|
|
||||||
| ty::Never
|
|
||||||
| ty::Param(_)
|
|
||||||
| ty::Bound(..)
|
|
||||||
| ty::Placeholder(..)
|
|
||||||
| ty::Foreign(..) => {
|
|
||||||
// WfScalar, WfParameter, etc
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can only infer to `ty::Int(_) | ty::Uint(_)`.
|
|
||||||
ty::Infer(ty::IntVar(_)) => {}
|
|
||||||
|
|
||||||
// Can only infer to `ty::Float(_)`.
|
|
||||||
ty::Infer(ty::FloatVar(_)) => {}
|
|
||||||
|
|
||||||
ty::Slice(subty) => {
|
|
||||||
self.require_sized(subty, traits::SliceOrArrayElem);
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Array(subty, _) => {
|
|
||||||
self.require_sized(subty, traits::SliceOrArrayElem);
|
|
||||||
// Note that we handle the len is implicitly checked while walking `arg`.
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Tuple(tys) => {
|
|
||||||
if let Some((_last, rest)) = tys.split_last() {
|
|
||||||
for &elem in rest {
|
|
||||||
self.require_sized(elem, traits::TupleElem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::RawPtr(_) => {
|
|
||||||
// Simple cases that are WF if their type args are WF.
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Alias(ty::Projection, data) => {
|
|
||||||
walker.skip_current_subtree(); // Subtree handled by compute_projection.
|
|
||||||
self.compute_projection(data);
|
|
||||||
}
|
|
||||||
ty::Alias(ty::Inherent, data) => {
|
|
||||||
walker.skip_current_subtree(); // Subtree handled by compute_inherent_projection.
|
|
||||||
self.compute_inherent_projection(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Adt(def, args) => {
|
|
||||||
// WfNominalType
|
|
||||||
let obligations = self.nominal_obligations(def.did(), args);
|
|
||||||
self.out.extend(obligations);
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::FnDef(did, args) => {
|
|
||||||
let obligations = self.nominal_obligations(did, args);
|
|
||||||
self.out.extend(obligations);
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Ref(r, rty, _) => {
|
|
||||||
// WfReference
|
|
||||||
if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
|
|
||||||
let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
|
|
||||||
self.out.push(traits::Obligation::with_depth(
|
|
||||||
self.tcx(),
|
|
||||||
cause,
|
|
||||||
depth,
|
|
||||||
param_env,
|
|
||||||
ty::Binder::dummy(ty::PredicateKind::Clause(
|
|
||||||
ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(rty, r)),
|
|
||||||
)),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Coroutine(did, args, ..) => {
|
|
||||||
// Walk ALL the types in the coroutine: this will
|
|
||||||
// include the upvar types as well as the yield
|
|
||||||
// type. Note that this is mildly distinct from
|
|
||||||
// the closure case, where we have to be careful
|
|
||||||
// about the signature of the closure. We don't
|
|
||||||
// have the problem of implied bounds here since
|
|
||||||
// coroutines don't take arguments.
|
|
||||||
let obligations = self.nominal_obligations(did, args);
|
|
||||||
self.out.extend(obligations);
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Closure(did, args) => {
|
|
||||||
// Only check the upvar types for WF, not the rest
|
|
||||||
// of the types within. This is needed because we
|
|
||||||
// capture the signature and it may not be WF
|
|
||||||
// without the implied bounds. Consider a closure
|
|
||||||
// like `|x: &'a T|` -- it may be that `T: 'a` is
|
|
||||||
// not known to hold in the creator's context (and
|
|
||||||
// indeed the closure may not be invoked by its
|
|
||||||
// creator, but rather turned to someone who *can*
|
|
||||||
// verify that).
|
|
||||||
//
|
|
||||||
// The special treatment of closures here really
|
|
||||||
// ought not to be necessary either; the problem
|
|
||||||
// is related to #25860 -- there is no way for us
|
|
||||||
// to express a fn type complete with the implied
|
|
||||||
// bounds that it is assuming. I think in reality
|
|
||||||
// the WF rules around fn are a bit messed up, and
|
|
||||||
// that is the rot problem: `fn(&'a T)` should
|
|
||||||
// probably always be WF, because it should be
|
|
||||||
// shorthand for something like `where(T: 'a) {
|
|
||||||
// fn(&'a T) }`, as discussed in #25860.
|
|
||||||
walker.skip_current_subtree(); // subtree handled below
|
|
||||||
// FIXME(eddyb) add the type to `walker` instead of recursing.
|
|
||||||
self.compute(args.as_closure().tupled_upvars_ty().into());
|
|
||||||
// Note that we cannot skip the generic types
|
|
||||||
// types. Normally, within the fn
|
|
||||||
// body where they are created, the generics will
|
|
||||||
// always be WF, and outside of that fn body we
|
|
||||||
// are not directly inspecting closure types
|
|
||||||
// anyway, except via auto trait matching (which
|
|
||||||
// only inspects the upvar types).
|
|
||||||
// But when a closure is part of a type-alias-impl-trait
|
|
||||||
// then the function that created the defining site may
|
|
||||||
// have had more bounds available than the type alias
|
|
||||||
// specifies. This may cause us to have a closure in the
|
|
||||||
// hidden type that is not actually well formed and
|
|
||||||
// can cause compiler crashes when the user abuses unsafe
|
|
||||||
// code to procure such a closure.
|
|
||||||
// See tests/ui/type-alias-impl-trait/wf_check_closures.rs
|
|
||||||
let obligations = self.nominal_obligations(did, args);
|
|
||||||
self.out.extend(obligations);
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::CoroutineClosure(did, args) => {
|
|
||||||
// See the above comments. The same apply to coroutine-closures.
|
|
||||||
walker.skip_current_subtree();
|
|
||||||
self.compute(args.as_coroutine_closure().tupled_upvars_ty().into());
|
|
||||||
let obligations = self.nominal_obligations(did, args);
|
|
||||||
self.out.extend(obligations);
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::FnPtr(_) => {
|
|
||||||
// let the loop iterate into the argument/return
|
|
||||||
// types appearing in the fn signature
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
|
||||||
// All of the requirements on type parameters
|
|
||||||
// have already been checked for `impl Trait` in
|
|
||||||
// return position. We do need to check type-alias-impl-trait though.
|
|
||||||
if self.tcx().is_type_alias_impl_trait(def_id) {
|
|
||||||
let obligations = self.nominal_obligations(def_id, args);
|
|
||||||
self.out.extend(obligations);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Alias(ty::Weak, ty::AliasTy { def_id, args, .. }) => {
|
|
||||||
let obligations = self.nominal_obligations(def_id, args);
|
|
||||||
self.out.extend(obligations);
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Dynamic(data, r, _) => {
|
|
||||||
// WfObject
|
|
||||||
//
|
|
||||||
// Here, we defer WF checking due to higher-ranked
|
|
||||||
// regions. This is perhaps not ideal.
|
|
||||||
self.from_object_ty(ty, data, r);
|
|
||||||
|
|
||||||
// FIXME(#27579) RFC also considers adding trait
|
|
||||||
// obligations that don't refer to Self and
|
|
||||||
// checking those
|
|
||||||
|
|
||||||
let defer_to_coercion = self.tcx().features().object_safe_for_dispatch;
|
|
||||||
|
|
||||||
if !defer_to_coercion {
|
|
||||||
if let Some(principal) = data.principal_def_id() {
|
|
||||||
self.out.push(traits::Obligation::with_depth(
|
|
||||||
self.tcx(),
|
|
||||||
self.cause(traits::WellFormed(None)),
|
|
||||||
depth,
|
|
||||||
param_env,
|
|
||||||
ty::Binder::dummy(ty::PredicateKind::ObjectSafe(principal)),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inference variables are the complicated case, since we don't
|
|
||||||
// know what type they are. We do two things:
|
|
||||||
//
|
|
||||||
// 1. Check if they have been resolved, and if so proceed with
|
|
||||||
// THAT type.
|
|
||||||
// 2. If not, we've at least simplified things (e.g., we went
|
|
||||||
// from `Vec<$0>: WF` to `$0: WF`), so we can
|
|
||||||
// register a pending obligation and keep
|
|
||||||
// moving. (Goal is that an "inductive hypothesis"
|
|
||||||
// is satisfied to ensure termination.)
|
|
||||||
// See also the comment on `fn obligations`, describing "livelock"
|
|
||||||
// prevention, which happens before this can be reached.
|
|
||||||
ty::Infer(_) => {
|
|
||||||
let cause = self.cause(traits::WellFormed(None));
|
|
||||||
self.out.push(traits::Obligation::with_depth(
|
|
||||||
self.tcx(),
|
|
||||||
cause,
|
|
||||||
self.recursion_depth,
|
|
||||||
param_env,
|
|
||||||
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
|
|
||||||
ty.into(),
|
|
||||||
))),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!(?self.out);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
|
@ -933,6 +638,302 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
|
||||||
|
type Result = ();
|
||||||
|
|
||||||
|
fn visit_ty(&mut self, t: <TyCtxt<'tcx> as ty::Interner>::Ty) -> Self::Result {
|
||||||
|
debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind());
|
||||||
|
|
||||||
|
match *t.kind() {
|
||||||
|
ty::Bool
|
||||||
|
| ty::Char
|
||||||
|
| ty::Int(..)
|
||||||
|
| ty::Uint(..)
|
||||||
|
| ty::Float(..)
|
||||||
|
| ty::Error(_)
|
||||||
|
| ty::Str
|
||||||
|
| ty::CoroutineWitness(..)
|
||||||
|
| ty::Never
|
||||||
|
| ty::Param(_)
|
||||||
|
| ty::Bound(..)
|
||||||
|
| ty::Placeholder(..)
|
||||||
|
| ty::Foreign(..) => {
|
||||||
|
// WfScalar, WfParameter, etc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can only infer to `ty::Int(_) | ty::Uint(_)`.
|
||||||
|
ty::Infer(ty::IntVar(_)) => {}
|
||||||
|
|
||||||
|
// Can only infer to `ty::Float(_)`.
|
||||||
|
ty::Infer(ty::FloatVar(_)) => {}
|
||||||
|
|
||||||
|
ty::Slice(subty) => {
|
||||||
|
self.require_sized(subty, traits::SliceOrArrayElem);
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Array(subty, _) => {
|
||||||
|
self.require_sized(subty, traits::SliceOrArrayElem);
|
||||||
|
// Note that we handle the len is implicitly checked while walking `arg`.
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Tuple(tys) => {
|
||||||
|
if let Some((_last, rest)) = tys.split_last() {
|
||||||
|
for &elem in rest {
|
||||||
|
self.require_sized(elem, traits::TupleElem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::RawPtr(_) => {
|
||||||
|
// Simple cases that are WF if their type args are WF.
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Alias(ty::Projection, data) => {
|
||||||
|
self.compute_projection(data);
|
||||||
|
return; // Subtree handled by compute_projection.
|
||||||
|
}
|
||||||
|
ty::Alias(ty::Inherent, data) => {
|
||||||
|
self.compute_inherent_projection(data);
|
||||||
|
return; // Subtree handled by compute_inherent_projection.
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Adt(def, args) => {
|
||||||
|
// WfNominalType
|
||||||
|
let obligations = self.nominal_obligations(def.did(), args);
|
||||||
|
self.out.extend(obligations);
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::FnDef(did, args) => {
|
||||||
|
let obligations = self.nominal_obligations(did, args);
|
||||||
|
self.out.extend(obligations);
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Ref(r, rty, _) => {
|
||||||
|
// WfReference
|
||||||
|
if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
|
||||||
|
let cause = self.cause(traits::ReferenceOutlivesReferent(t));
|
||||||
|
self.out.push(traits::Obligation::with_depth(
|
||||||
|
self.tcx(),
|
||||||
|
cause,
|
||||||
|
self.recursion_depth,
|
||||||
|
self.param_env,
|
||||||
|
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(
|
||||||
|
ty::OutlivesPredicate(rty, r),
|
||||||
|
))),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Coroutine(did, args, ..) => {
|
||||||
|
// Walk ALL the types in the coroutine: this will
|
||||||
|
// include the upvar types as well as the yield
|
||||||
|
// type. Note that this is mildly distinct from
|
||||||
|
// the closure case, where we have to be careful
|
||||||
|
// about the signature of the closure. We don't
|
||||||
|
// have the problem of implied bounds here since
|
||||||
|
// coroutines don't take arguments.
|
||||||
|
let obligations = self.nominal_obligations(did, args);
|
||||||
|
self.out.extend(obligations);
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Closure(did, args) => {
|
||||||
|
// Note that we cannot skip the generic types
|
||||||
|
// types. Normally, within the fn
|
||||||
|
// body where they are created, the generics will
|
||||||
|
// always be WF, and outside of that fn body we
|
||||||
|
// are not directly inspecting closure types
|
||||||
|
// anyway, except via auto trait matching (which
|
||||||
|
// only inspects the upvar types).
|
||||||
|
// But when a closure is part of a type-alias-impl-trait
|
||||||
|
// then the function that created the defining site may
|
||||||
|
// have had more bounds available than the type alias
|
||||||
|
// specifies. This may cause us to have a closure in the
|
||||||
|
// hidden type that is not actually well formed and
|
||||||
|
// can cause compiler crashes when the user abuses unsafe
|
||||||
|
// code to procure such a closure.
|
||||||
|
// See tests/ui/type-alias-impl-trait/wf_check_closures.rs
|
||||||
|
let obligations = self.nominal_obligations(did, args);
|
||||||
|
self.out.extend(obligations);
|
||||||
|
// Only check the upvar types for WF, not the rest
|
||||||
|
// of the types within. This is needed because we
|
||||||
|
// capture the signature and it may not be WF
|
||||||
|
// without the implied bounds. Consider a closure
|
||||||
|
// like `|x: &'a T|` -- it may be that `T: 'a` is
|
||||||
|
// not known to hold in the creator's context (and
|
||||||
|
// indeed the closure may not be invoked by its
|
||||||
|
// creator, but rather turned to someone who *can*
|
||||||
|
// verify that).
|
||||||
|
//
|
||||||
|
// The special treatment of closures here really
|
||||||
|
// ought not to be necessary either; the problem
|
||||||
|
// is related to #25860 -- there is no way for us
|
||||||
|
// to express a fn type complete with the implied
|
||||||
|
// bounds that it is assuming. I think in reality
|
||||||
|
// the WF rules around fn are a bit messed up, and
|
||||||
|
// that is the rot problem: `fn(&'a T)` should
|
||||||
|
// probably always be WF, because it should be
|
||||||
|
// shorthand for something like `where(T: 'a) {
|
||||||
|
// fn(&'a T) }`, as discussed in #25860.
|
||||||
|
let upvars = args.as_closure().tupled_upvars_ty();
|
||||||
|
return upvars.visit_with(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::CoroutineClosure(did, args) => {
|
||||||
|
// See the above comments. The same apply to coroutine-closures.
|
||||||
|
let obligations = self.nominal_obligations(did, args);
|
||||||
|
self.out.extend(obligations);
|
||||||
|
let upvars = args.as_coroutine_closure().tupled_upvars_ty();
|
||||||
|
return upvars.visit_with(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::FnPtr(_) => {
|
||||||
|
// Let the visitor iterate into the argument/return
|
||||||
|
// types appearing in the fn signature.
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
||||||
|
// All of the requirements on type parameters
|
||||||
|
// have already been checked for `impl Trait` in
|
||||||
|
// return position. We do need to check type-alias-impl-trait though.
|
||||||
|
if self.tcx().is_type_alias_impl_trait(def_id) {
|
||||||
|
let obligations = self.nominal_obligations(def_id, args);
|
||||||
|
self.out.extend(obligations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Alias(ty::Weak, ty::AliasTy { def_id, args, .. }) => {
|
||||||
|
let obligations = self.nominal_obligations(def_id, args);
|
||||||
|
self.out.extend(obligations);
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Dynamic(data, r, _) => {
|
||||||
|
// WfObject
|
||||||
|
//
|
||||||
|
// Here, we defer WF checking due to higher-ranked
|
||||||
|
// regions. This is perhaps not ideal.
|
||||||
|
self.from_object_ty(t, data, r);
|
||||||
|
|
||||||
|
// FIXME(#27579) RFC also considers adding trait
|
||||||
|
// obligations that don't refer to Self and
|
||||||
|
// checking those
|
||||||
|
|
||||||
|
let defer_to_coercion = self.tcx().features().object_safe_for_dispatch;
|
||||||
|
|
||||||
|
if !defer_to_coercion {
|
||||||
|
if let Some(principal) = data.principal_def_id() {
|
||||||
|
self.out.push(traits::Obligation::with_depth(
|
||||||
|
self.tcx(),
|
||||||
|
self.cause(traits::WellFormed(None)),
|
||||||
|
self.recursion_depth,
|
||||||
|
self.param_env,
|
||||||
|
ty::Binder::dummy(ty::PredicateKind::ObjectSafe(principal)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inference variables are the complicated case, since we don't
|
||||||
|
// know what type they are. We do two things:
|
||||||
|
//
|
||||||
|
// 1. Check if they have been resolved, and if so proceed with
|
||||||
|
// THAT type.
|
||||||
|
// 2. If not, we've at least simplified things (e.g., we went
|
||||||
|
// from `Vec<$0>: WF` to `$0: WF`), so we can
|
||||||
|
// register a pending obligation and keep
|
||||||
|
// moving. (Goal is that an "inductive hypothesis"
|
||||||
|
// is satisfied to ensure termination.)
|
||||||
|
// See also the comment on `fn obligations`, describing "livelock"
|
||||||
|
// prevention, which happens before this can be reached.
|
||||||
|
ty::Infer(_) => {
|
||||||
|
let cause = self.cause(traits::WellFormed(None));
|
||||||
|
self.out.push(traits::Obligation::with_depth(
|
||||||
|
self.tcx(),
|
||||||
|
cause,
|
||||||
|
self.recursion_depth,
|
||||||
|
self.param_env,
|
||||||
|
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
|
||||||
|
t.into(),
|
||||||
|
))),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.super_visit_with(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_const(&mut self, c: <TyCtxt<'tcx> as ty::Interner>::Const) -> Self::Result {
|
||||||
|
match c.kind() {
|
||||||
|
ty::ConstKind::Unevaluated(uv) => {
|
||||||
|
if !c.has_escaping_bound_vars() {
|
||||||
|
let obligations = self.nominal_obligations(uv.def, uv.args);
|
||||||
|
self.out.extend(obligations);
|
||||||
|
|
||||||
|
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
|
||||||
|
ty::ClauseKind::ConstEvaluatable(c),
|
||||||
|
));
|
||||||
|
let cause = self.cause(traits::WellFormed(None));
|
||||||
|
self.out.push(traits::Obligation::with_depth(
|
||||||
|
self.tcx(),
|
||||||
|
cause,
|
||||||
|
self.recursion_depth,
|
||||||
|
self.param_env,
|
||||||
|
predicate,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::ConstKind::Infer(_) => {
|
||||||
|
let cause = self.cause(traits::WellFormed(None));
|
||||||
|
|
||||||
|
self.out.push(traits::Obligation::with_depth(
|
||||||
|
self.tcx(),
|
||||||
|
cause,
|
||||||
|
self.recursion_depth,
|
||||||
|
self.param_env,
|
||||||
|
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
|
||||||
|
c.into(),
|
||||||
|
))),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
ty::ConstKind::Expr(_) => {
|
||||||
|
// FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the
|
||||||
|
// trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary
|
||||||
|
// as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated`
|
||||||
|
// which means that the `DefId` would have been typeck'd elsewhere. However in
|
||||||
|
// the future we may allow directly lowering to `ConstKind::Expr` in which case
|
||||||
|
// we would not be proving bounds we should.
|
||||||
|
|
||||||
|
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
|
||||||
|
ty::ClauseKind::ConstEvaluatable(c),
|
||||||
|
));
|
||||||
|
let cause = self.cause(traits::WellFormed(None));
|
||||||
|
self.out.push(traits::Obligation::with_depth(
|
||||||
|
self.tcx(),
|
||||||
|
cause,
|
||||||
|
self.recursion_depth,
|
||||||
|
self.param_env,
|
||||||
|
predicate,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::ConstKind::Error(_)
|
||||||
|
| ty::ConstKind::Param(_)
|
||||||
|
| ty::ConstKind::Bound(..)
|
||||||
|
| ty::ConstKind::Placeholder(..) => {
|
||||||
|
// These variants are trivially WF, so nothing to do here.
|
||||||
|
}
|
||||||
|
ty::ConstKind::Value(..) => {
|
||||||
|
// FIXME: Enforce that values are structurally-matchable.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.super_visit_with(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_predicate(&mut self, _p: <TyCtxt<'tcx> as ty::Interner>::Predicate) -> Self::Result {
|
||||||
|
bug!("predicate should not be checked for well-formedness");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Given an object type like `SomeTrait + Send`, computes the lifetime
|
/// Given an object type like `SomeTrait + Send`, computes the lifetime
|
||||||
/// bounds that must hold on the elided self type. These are derived
|
/// bounds that must hold on the elided self type. These are derived
|
||||||
/// from the declarations of `SomeTrait`, `Send`, and friends -- if
|
/// from the declarations of `SomeTrait`, `Send`, and friends -- if
|
||||||
|
|
|
@ -20,6 +20,7 @@ where
|
||||||
//~^^^ ERROR: function takes 1 generic argument but 2 generic arguments were supplied
|
//~^^^ ERROR: function takes 1 generic argument but 2 generic arguments were supplied
|
||||||
//~^^^^ ERROR: unconstrained generic constant
|
//~^^^^ ERROR: unconstrained generic constant
|
||||||
//~^^^^^ ERROR: unconstrained generic constant `{const expr}`
|
//~^^^^^ ERROR: unconstrained generic constant `{const expr}`
|
||||||
|
//~^^^^^^ ERROR: unconstrained generic constant `{const expr}`
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -58,7 +58,15 @@ error: unconstrained generic constant `{const expr}`
|
||||||
LL | foo::<_, L>([(); L + 1 + L]);
|
LL | foo::<_, L>([(); L + 1 + L]);
|
||||||
| ^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
error: aborting due to 5 previous errors
|
error: unconstrained generic constant `{const expr}`
|
||||||
|
--> $DIR/issue_114151.rs:17:5
|
||||||
|
|
|
||||||
|
LL | foo::<_, L>([(); L + 1 + L]);
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
error: aborting due to 6 previous errors
|
||||||
|
|
||||||
Some errors have detailed explanations: E0107, E0308.
|
Some errors have detailed explanations: E0107, E0308.
|
||||||
For more information about an error, try `rustc --explain E0107`.
|
For more information about an error, try `rustc --explain E0107`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue