Auto merge of #70107 - lcnr:issue68977, r=eddyb
WF-check all ty::Const's, not just array lengths. fixes #68977 This PR removes the special case for array length in `wf::compute` and checks the well formedness of all consts. Changes `PredicateKind::WellFormed` to take a `GenericArg` and updates `wf::obligations`.
This commit is contained in:
@@ -307,7 +307,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
|
|||||||
self.obligations.push(Obligation::new(
|
self.obligations.push(Obligation::new(
|
||||||
self.trace.cause.clone(),
|
self.trace.cause.clone(),
|
||||||
self.param_env,
|
self.param_env,
|
||||||
ty::PredicateKind::WellFormed(b_ty).to_predicate(self.infcx.tcx),
|
ty::PredicateKind::WellFormed(b_ty.into()).to_predicate(self.infcx.tcx),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use crate::mir::Body;
|
|||||||
use crate::mir::GeneratorLayout;
|
use crate::mir::GeneratorLayout;
|
||||||
use crate::traits::{self, Reveal};
|
use crate::traits::{self, Reveal};
|
||||||
use crate::ty;
|
use crate::ty;
|
||||||
use crate::ty::subst::{InternalSubsts, Subst, SubstsRef};
|
use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef};
|
||||||
use crate::ty::util::{Discr, IntTypeExt};
|
use crate::ty::util::{Discr, IntTypeExt};
|
||||||
use rustc_ast::ast;
|
use rustc_ast::ast;
|
||||||
use rustc_attr as attr;
|
use rustc_attr as attr;
|
||||||
@@ -1061,7 +1061,7 @@ pub enum PredicateKind<'tcx> {
|
|||||||
Projection(PolyProjectionPredicate<'tcx>),
|
Projection(PolyProjectionPredicate<'tcx>),
|
||||||
|
|
||||||
/// No syntax: `T` well-formed.
|
/// No syntax: `T` well-formed.
|
||||||
WellFormed(Ty<'tcx>),
|
WellFormed(GenericArg<'tcx>),
|
||||||
|
|
||||||
/// Trait must be object-safe.
|
/// Trait must be object-safe.
|
||||||
ObjectSafe(DefId),
|
ObjectSafe(DefId),
|
||||||
|
|||||||
@@ -2031,7 +2031,7 @@ define_print_and_forward_display! {
|
|||||||
ty::PredicateKind::RegionOutlives(predicate) => p!(print(predicate)),
|
ty::PredicateKind::RegionOutlives(predicate) => p!(print(predicate)),
|
||||||
ty::PredicateKind::TypeOutlives(predicate) => p!(print(predicate)),
|
ty::PredicateKind::TypeOutlives(predicate) => p!(print(predicate)),
|
||||||
ty::PredicateKind::Projection(predicate) => p!(print(predicate)),
|
ty::PredicateKind::Projection(predicate) => p!(print(predicate)),
|
||||||
ty::PredicateKind::WellFormed(ty) => p!(print(ty), write(" well-formed")),
|
ty::PredicateKind::WellFormed(arg) => p!(print(arg), write(" well-formed")),
|
||||||
&ty::PredicateKind::ObjectSafe(trait_def_id) => {
|
&ty::PredicateKind::ObjectSafe(trait_def_id) => {
|
||||||
p!(write("the trait `"),
|
p!(write("the trait `"),
|
||||||
print_def_path(trait_def_id, &[]),
|
print_def_path(trait_def_id, &[]),
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ impl fmt::Debug for ty::PredicateKind<'tcx> {
|
|||||||
ty::PredicateKind::RegionOutlives(ref pair) => pair.fmt(f),
|
ty::PredicateKind::RegionOutlives(ref pair) => pair.fmt(f),
|
||||||
ty::PredicateKind::TypeOutlives(ref pair) => pair.fmt(f),
|
ty::PredicateKind::TypeOutlives(ref pair) => pair.fmt(f),
|
||||||
ty::PredicateKind::Projection(ref pair) => pair.fmt(f),
|
ty::PredicateKind::Projection(ref pair) => pair.fmt(f),
|
||||||
ty::PredicateKind::WellFormed(ty) => write!(f, "WellFormed({:?})", ty),
|
ty::PredicateKind::WellFormed(data) => write!(f, "WellFormed({:?})", data),
|
||||||
ty::PredicateKind::ObjectSafe(trait_def_id) => {
|
ty::PredicateKind::ObjectSafe(trait_def_id) => {
|
||||||
write!(f, "ObjectSafe({:?})", trait_def_id)
|
write!(f, "ObjectSafe({:?})", trait_def_id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1016,7 +1016,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.prove_predicate(
|
self.prove_predicate(
|
||||||
ty::PredicateKind::WellFormed(inferred_ty).to_predicate(self.tcx()),
|
ty::PredicateKind::WellFormed(inferred_ty.into()).to_predicate(self.tcx()),
|
||||||
Locations::All(span),
|
Locations::All(span),
|
||||||
ConstraintCategory::TypeAnnotation,
|
ConstraintCategory::TypeAnnotation,
|
||||||
);
|
);
|
||||||
@@ -1268,7 +1268,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||||||
obligations.obligations.push(traits::Obligation::new(
|
obligations.obligations.push(traits::Obligation::new(
|
||||||
ObligationCause::dummy(),
|
ObligationCause::dummy(),
|
||||||
param_env,
|
param_env,
|
||||||
ty::PredicateKind::WellFormed(revealed_ty).to_predicate(infcx.tcx),
|
ty::PredicateKind::WellFormed(revealed_ty.into()).to_predicate(infcx.tcx),
|
||||||
));
|
));
|
||||||
obligations.add(
|
obligations.add(
|
||||||
infcx
|
infcx
|
||||||
@@ -1612,7 +1612,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||||||
self.check_call_dest(body, term, &sig, destination, term_location);
|
self.check_call_dest(body, term, &sig, destination, term_location);
|
||||||
|
|
||||||
self.prove_predicates(
|
self.prove_predicates(
|
||||||
sig.inputs_and_output.iter().map(|ty| ty::PredicateKind::WellFormed(ty)),
|
sig.inputs_and_output.iter().map(|ty| ty::PredicateKind::WellFormed(ty.into())),
|
||||||
term_location.to_locations(),
|
term_location.to_locations(),
|
||||||
ConstraintCategory::Boring,
|
ConstraintCategory::Boring,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use rustc_hir::Node;
|
|||||||
use rustc_middle::mir::interpret::ErrorHandled;
|
use rustc_middle::mir::interpret::ErrorHandled;
|
||||||
use rustc_middle::ty::error::ExpectedFound;
|
use rustc_middle::ty::error::ExpectedFound;
|
||||||
use rustc_middle::ty::fold::TypeFolder;
|
use rustc_middle::ty::fold::TypeFolder;
|
||||||
|
use rustc_middle::ty::subst::GenericArgKind;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, fast_reject, AdtKind, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt,
|
self, fast_reject, AdtKind, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt,
|
||||||
TypeFoldable, WithConstness,
|
TypeFoldable, WithConstness,
|
||||||
@@ -1531,13 +1532,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||||||
err
|
err
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::PredicateKind::WellFormed(ty) => {
|
ty::PredicateKind::WellFormed(arg) => {
|
||||||
// Same hacky approach as above to avoid deluging user
|
// Same hacky approach as above to avoid deluging user
|
||||||
// with error messages.
|
// with error messages.
|
||||||
if ty.references_error() || self.tcx.sess.has_errors() {
|
if arg.references_error() || self.tcx.sess.has_errors() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.need_type_info_err(body_id, span, ty, ErrorCode::E0282)
|
|
||||||
|
match arg.unpack() {
|
||||||
|
GenericArgKind::Lifetime(lt) => {
|
||||||
|
span_bug!(span, "unexpected well formed predicate: {:?}", lt)
|
||||||
|
}
|
||||||
|
GenericArgKind::Type(ty) => {
|
||||||
|
self.need_type_info_err(body_id, span, ty, ErrorCode::E0282)
|
||||||
|
}
|
||||||
|
GenericArgKind::Const(ct) => {
|
||||||
|
self.need_type_info_err_const(body_id, span, ct, ErrorCode::E0282)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::PredicateKind::Subtype(ref data) => {
|
ty::PredicateKind::Subtype(ref data) => {
|
||||||
|
|||||||
@@ -459,17 +459,17 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&ty::PredicateKind::WellFormed(ty) => {
|
&ty::PredicateKind::WellFormed(arg) => {
|
||||||
match wf::obligations(
|
match wf::obligations(
|
||||||
self.selcx.infcx(),
|
self.selcx.infcx(),
|
||||||
obligation.param_env,
|
obligation.param_env,
|
||||||
obligation.cause.body_id,
|
obligation.cause.body_id,
|
||||||
ty,
|
arg,
|
||||||
obligation.cause.span,
|
obligation.cause.span,
|
||||||
) {
|
) {
|
||||||
None => {
|
None => {
|
||||||
pending_obligation.stalled_on =
|
pending_obligation.stalled_on =
|
||||||
vec![TyOrConstInferVar::maybe_from_ty(ty).unwrap()];
|
vec![TyOrConstInferVar::maybe_from_generic_arg(arg).unwrap()];
|
||||||
ProcessResult::Unchanged
|
ProcessResult::Unchanged
|
||||||
}
|
}
|
||||||
Some(os) => ProcessResult::Changed(mk_pending(os)),
|
Some(os) => ProcessResult::Changed(mk_pending(os)),
|
||||||
|
|||||||
@@ -436,11 +436,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&ty::PredicateKind::WellFormed(ty) => match wf::obligations(
|
&ty::PredicateKind::WellFormed(arg) => match wf::obligations(
|
||||||
self.infcx,
|
self.infcx,
|
||||||
obligation.param_env,
|
obligation.param_env,
|
||||||
obligation.cause.body_id,
|
obligation.cause.body_id,
|
||||||
ty,
|
arg,
|
||||||
obligation.cause.span,
|
obligation.cause.span,
|
||||||
) {
|
) {
|
||||||
Some(mut obligations) => {
|
Some(mut obligations) => {
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ use crate::traits;
|
|||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::lang_items;
|
use rustc_hir::lang_items;
|
||||||
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
|
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
|
||||||
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
|
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// Returns the set of obligations needed to make `ty` well-formed.
|
/// Returns the set of obligations needed to make `arg` well-formed.
|
||||||
/// If `ty` contains unresolved inference variables, this may include
|
/// If `arg` contains unresolved inference variables, this may include
|
||||||
/// further WF obligations. However, if `ty` IS an unresolved
|
/// further WF obligations. However, if `arg` IS an unresolved
|
||||||
/// inference variable, returns `None`, because we are not able to
|
/// inference variable, returns `None`, because we are not able to
|
||||||
/// make any progress at all. This is to prevent "livelock" where we
|
/// make any progress at all. This is to prevent "livelock" where we
|
||||||
/// say "$0 is WF if $0 is WF".
|
/// say "$0 is WF if $0 is WF".
|
||||||
@@ -19,29 +19,51 @@ pub fn obligations<'a, 'tcx>(
|
|||||||
infcx: &InferCtxt<'a, 'tcx>,
|
infcx: &InferCtxt<'a, 'tcx>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
body_id: hir::HirId,
|
body_id: hir::HirId,
|
||||||
ty: Ty<'tcx>,
|
arg: GenericArg<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
|
) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
|
||||||
// Handle the "livelock" case (see comment above) by bailing out if necessary.
|
// Handle the "livelock" case (see comment above) by bailing out if necessary.
|
||||||
let ty = match ty.kind {
|
let arg = match arg.unpack() {
|
||||||
ty::Infer(ty::TyVar(_)) => {
|
GenericArgKind::Type(ty) => {
|
||||||
let resolved_ty = infcx.shallow_resolve(ty);
|
match ty.kind {
|
||||||
if resolved_ty == ty {
|
ty::Infer(ty::TyVar(_)) => {
|
||||||
// No progress, bail out to prevent "livelock".
|
let resolved_ty = infcx.shallow_resolve(ty);
|
||||||
return None;
|
if resolved_ty == ty {
|
||||||
}
|
// No progress, bail out to prevent "livelock".
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
resolved_ty
|
resolved_ty
|
||||||
|
}
|
||||||
|
_ => ty,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
_ => ty,
|
GenericArgKind::Const(ct) => {
|
||||||
|
match ct.val {
|
||||||
|
ty::ConstKind::Infer(infer) => {
|
||||||
|
let resolved = infcx.shallow_resolve(infer);
|
||||||
|
if resolved == infer {
|
||||||
|
// No progress.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
infcx.tcx.mk_const(ty::Const { val: ty::ConstKind::Infer(resolved), ty: ct.ty })
|
||||||
|
}
|
||||||
|
_ => ct,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
// There is nothing we have to do for lifetimes.
|
||||||
|
GenericArgKind::Lifetime(..) => return Some(Vec::new()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
|
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
|
||||||
wf.compute(ty);
|
wf.compute(arg);
|
||||||
debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out);
|
debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out);
|
||||||
|
|
||||||
let result = wf.normalize();
|
let result = wf.normalize();
|
||||||
debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result);
|
debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", arg, body_id, result);
|
||||||
Some(result)
|
Some(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,33 +100,33 @@ pub fn predicate_obligations<'a, 'tcx>(
|
|||||||
}
|
}
|
||||||
ty::PredicateKind::RegionOutlives(..) => {}
|
ty::PredicateKind::RegionOutlives(..) => {}
|
||||||
ty::PredicateKind::TypeOutlives(t) => {
|
ty::PredicateKind::TypeOutlives(t) => {
|
||||||
wf.compute(t.skip_binder().0);
|
wf.compute(t.skip_binder().0.into());
|
||||||
}
|
}
|
||||||
ty::PredicateKind::Projection(t) => {
|
ty::PredicateKind::Projection(t) => {
|
||||||
let t = t.skip_binder(); // (*)
|
let t = t.skip_binder(); // (*)
|
||||||
wf.compute_projection(t.projection_ty);
|
wf.compute_projection(t.projection_ty);
|
||||||
wf.compute(t.ty);
|
wf.compute(t.ty.into());
|
||||||
}
|
}
|
||||||
&ty::PredicateKind::WellFormed(t) => {
|
&ty::PredicateKind::WellFormed(arg) => {
|
||||||
wf.compute(t);
|
wf.compute(arg);
|
||||||
}
|
}
|
||||||
ty::PredicateKind::ObjectSafe(_) => {}
|
ty::PredicateKind::ObjectSafe(_) => {}
|
||||||
ty::PredicateKind::ClosureKind(..) => {}
|
ty::PredicateKind::ClosureKind(..) => {}
|
||||||
ty::PredicateKind::Subtype(data) => {
|
ty::PredicateKind::Subtype(data) => {
|
||||||
wf.compute(data.skip_binder().a); // (*)
|
wf.compute(data.skip_binder().a.into()); // (*)
|
||||||
wf.compute(data.skip_binder().b); // (*)
|
wf.compute(data.skip_binder().b.into()); // (*)
|
||||||
}
|
}
|
||||||
&ty::PredicateKind::ConstEvaluatable(def_id, substs) => {
|
&ty::PredicateKind::ConstEvaluatable(def_id, substs) => {
|
||||||
let obligations = wf.nominal_obligations(def_id, substs);
|
let obligations = wf.nominal_obligations(def_id, substs);
|
||||||
wf.out.extend(obligations);
|
wf.out.extend(obligations);
|
||||||
|
|
||||||
for ty in substs.types() {
|
for arg in substs.iter() {
|
||||||
wf.compute(ty);
|
wf.compute(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ty::PredicateKind::ConstEquate(c1, c2) => {
|
&ty::PredicateKind::ConstEquate(c1, c2) => {
|
||||||
wf.compute(c1.ty);
|
wf.compute(c1.into());
|
||||||
wf.compute(c2.ty);
|
wf.compute(c2.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +235,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
|||||||
self.infcx.tcx
|
self.infcx.tcx
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cause(&mut self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> {
|
fn cause(&self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> {
|
||||||
traits::ObligationCause::new(self.span, self.body_id, code)
|
traits::ObligationCause::new(self.span, self.body_id, code)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,15 +295,22 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
self.out.extend(trait_ref.substs.types().filter(|ty| !ty.has_escaping_bound_vars()).map(
|
self.out.extend(
|
||||||
|ty| {
|
trait_ref
|
||||||
traits::Obligation::new(
|
.substs
|
||||||
cause.clone(),
|
.iter()
|
||||||
param_env,
|
.filter(|arg| {
|
||||||
ty::PredicateKind::WellFormed(ty).to_predicate(tcx),
|
matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
|
||||||
)
|
})
|
||||||
},
|
.filter(|arg| !arg.has_escaping_bound_vars())
|
||||||
));
|
.map(|arg| {
|
||||||
|
traits::Obligation::new(
|
||||||
|
cause.clone(),
|
||||||
|
param_env,
|
||||||
|
ty::PredicateKind::WellFormed(arg).to_predicate(tcx),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes the obligations required for `trait_ref::Item` to be WF
|
/// Pushes the obligations required for `trait_ref::Item` to be WF
|
||||||
@@ -300,22 +329,6 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes the obligations required for an array length to be WF
|
|
||||||
/// into `self.out`.
|
|
||||||
fn compute_array_len(&mut self, constant: ty::Const<'tcx>) {
|
|
||||||
if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = constant.val {
|
|
||||||
assert!(promoted.is_none());
|
|
||||||
|
|
||||||
let obligations = self.nominal_obligations(def_id, substs);
|
|
||||||
self.out.extend(obligations);
|
|
||||||
|
|
||||||
let predicate =
|
|
||||||
ty::PredicateKind::ConstEvaluatable(def_id, substs).to_predicate(self.tcx());
|
|
||||||
let cause = self.cause(traits::MiscObligation);
|
|
||||||
self.out.push(traits::Obligation::new(cause, self.param_env, predicate));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) {
|
fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) {
|
||||||
if !subty.has_escaping_bound_vars() {
|
if !subty.has_escaping_bound_vars() {
|
||||||
let cause = self.cause(cause);
|
let cause = self.cause(cause);
|
||||||
@@ -332,8 +345,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`.
|
||||||
fn compute(&mut self, ty: Ty<'tcx>) {
|
fn compute(&mut self, arg: GenericArg<'tcx>) {
|
||||||
let mut walker = ty.walk();
|
let mut walker = arg.walk();
|
||||||
let param_env = self.param_env;
|
let param_env = self.param_env;
|
||||||
while let Some(arg) = walker.next() {
|
while let Some(arg) = walker.next() {
|
||||||
let ty = match arg.unpack() {
|
let ty = match arg.unpack() {
|
||||||
@@ -343,9 +356,53 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
|||||||
// obligations are handled by the parent (e.g. `ty::Ref`).
|
// obligations are handled by the parent (e.g. `ty::Ref`).
|
||||||
GenericArgKind::Lifetime(_) => continue,
|
GenericArgKind::Lifetime(_) => continue,
|
||||||
|
|
||||||
// FIXME(eddyb) this is wrong and needs to be replaced
|
GenericArgKind::Const(constant) => {
|
||||||
// (see https://github.com/rust-lang/rust/pull/70107).
|
match constant.val {
|
||||||
GenericArgKind::Const(_) => continue,
|
ty::ConstKind::Unevaluated(def_id, substs, promoted) => {
|
||||||
|
assert!(promoted.is_none());
|
||||||
|
|
||||||
|
let obligations = self.nominal_obligations(def_id, substs);
|
||||||
|
self.out.extend(obligations);
|
||||||
|
|
||||||
|
let predicate = ty::PredicateKind::ConstEvaluatable(def_id, substs)
|
||||||
|
.to_predicate(self.tcx());
|
||||||
|
let cause = self.cause(traits::MiscObligation);
|
||||||
|
self.out.push(traits::Obligation::new(
|
||||||
|
cause,
|
||||||
|
self.param_env,
|
||||||
|
predicate,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
ty::ConstKind::Infer(infer) => {
|
||||||
|
let resolved = self.infcx.shallow_resolve(infer);
|
||||||
|
// the `InferConst` changed, meaning that we made progress.
|
||||||
|
if resolved != infer {
|
||||||
|
let cause = self.cause(traits::MiscObligation);
|
||||||
|
|
||||||
|
let resolved_constant = self.infcx.tcx.mk_const(ty::Const {
|
||||||
|
val: ty::ConstKind::Infer(resolved),
|
||||||
|
..*constant
|
||||||
|
});
|
||||||
|
self.out.push(traits::Obligation::new(
|
||||||
|
cause,
|
||||||
|
self.param_env,
|
||||||
|
ty::PredicateKind::WellFormed(resolved_constant.into())
|
||||||
|
.to_predicate(self.tcx()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match ty.kind {
|
match ty.kind {
|
||||||
@@ -375,10 +432,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
|||||||
self.require_sized(subty, traits::SliceOrArrayElem);
|
self.require_sized(subty, traits::SliceOrArrayElem);
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Array(subty, len) => {
|
ty::Array(subty, _) => {
|
||||||
self.require_sized(subty, traits::SliceOrArrayElem);
|
self.require_sized(subty, traits::SliceOrArrayElem);
|
||||||
// FIXME(eddyb) handle `GenericArgKind::Const` above instead.
|
// Note that we handle the len is implicitly checked while walking `arg`.
|
||||||
self.compute_array_len(*len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Tuple(ref tys) => {
|
ty::Tuple(ref tys) => {
|
||||||
@@ -390,11 +446,11 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ty::RawPtr(_) => {
|
ty::RawPtr(_) => {
|
||||||
// simple cases that are WF if their type args are WF
|
// Simple cases that are WF if their type args are WF.
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Projection(data) => {
|
ty::Projection(data) => {
|
||||||
walker.skip_current_subtree(); // subtree handled by compute_projection
|
walker.skip_current_subtree(); // Subtree handled by compute_projection.
|
||||||
self.compute_projection(data);
|
self.compute_projection(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,7 +523,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
|||||||
walker.skip_current_subtree(); // subtree handled below
|
walker.skip_current_subtree(); // subtree handled below
|
||||||
for upvar_ty in substs.as_closure().upvar_tys() {
|
for upvar_ty in substs.as_closure().upvar_tys() {
|
||||||
// FIXME(eddyb) add the type to `walker` instead of recursing.
|
// FIXME(eddyb) add the type to `walker` instead of recursing.
|
||||||
self.compute(upvar_ty);
|
self.compute(upvar_ty.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,12 +591,12 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
|||||||
self.out.push(traits::Obligation::new(
|
self.out.push(traits::Obligation::new(
|
||||||
cause,
|
cause,
|
||||||
param_env,
|
param_env,
|
||||||
ty::PredicateKind::WellFormed(ty).to_predicate(self.tcx()),
|
ty::PredicateKind::WellFormed(ty.into()).to_predicate(self.tcx()),
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
// Yes, resolved, proceed with the result.
|
// Yes, resolved, proceed with the result.
|
||||||
// FIXME(eddyb) add the type to `walker` instead of recursing.
|
// FIXME(eddyb) add the type to `walker` instead of recursing.
|
||||||
self.compute(ty);
|
self.compute(ty.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ use rustc_middle::traits::{
|
|||||||
ChalkRustInterner as RustInterner,
|
ChalkRustInterner as RustInterner,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::fold::TypeFolder;
|
use rustc_middle::ty::fold::TypeFolder;
|
||||||
use rustc_middle::ty::subst::{GenericArg, SubstsRef};
|
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, Binder, BoundRegion, Region, RegionKind, Ty, TyCtxt, TyKind, TypeFoldable, TypeVisitor,
|
self, Binder, BoundRegion, Region, RegionKind, Ty, TyCtxt, TyKind, TypeFoldable, TypeVisitor,
|
||||||
};
|
};
|
||||||
@@ -77,7 +77,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'
|
|||||||
) -> chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'tcx>>> {
|
) -> chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'tcx>>> {
|
||||||
let clauses = self.environment.into_iter().filter_map(|clause| match clause {
|
let clauses = self.environment.into_iter().filter_map(|clause| match clause {
|
||||||
ChalkEnvironmentClause::Predicate(predicate) => {
|
ChalkEnvironmentClause::Predicate(predicate) => {
|
||||||
match &predicate.kind() {
|
match predicate.kind() {
|
||||||
ty::PredicateKind::Trait(predicate, _) => {
|
ty::PredicateKind::Trait(predicate, _) => {
|
||||||
let (predicate, binders, _named_regions) =
|
let (predicate, binders, _named_regions) =
|
||||||
collect_bound_vars(interner, interner.tcx, predicate);
|
collect_bound_vars(interner, interner.tcx, predicate);
|
||||||
@@ -165,24 +165,31 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predi
|
|||||||
chalk_ir::GoalData::All(chalk_ir::Goals::new(interner))
|
chalk_ir::GoalData::All(chalk_ir::Goals::new(interner))
|
||||||
}
|
}
|
||||||
ty::PredicateKind::Projection(predicate) => predicate.lower_into(interner),
|
ty::PredicateKind::Projection(predicate) => predicate.lower_into(interner),
|
||||||
ty::PredicateKind::WellFormed(ty) => match ty.kind {
|
ty::PredicateKind::WellFormed(arg) => match arg.unpack() {
|
||||||
// These types are always WF.
|
GenericArgKind::Type(ty) => match ty.kind {
|
||||||
ty::Str | ty::Placeholder(..) | ty::Error | ty::Never => {
|
// These types are always WF.
|
||||||
|
ty::Str | ty::Placeholder(..) | ty::Error | ty::Never => {
|
||||||
|
chalk_ir::GoalData::All(chalk_ir::Goals::new(interner))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(chalk): Well-formed only if ref lifetime outlives type
|
||||||
|
ty::Ref(..) => chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)),
|
||||||
|
|
||||||
|
ty::Param(..) => panic!("No Params expected."),
|
||||||
|
|
||||||
|
// FIXME(chalk) -- ultimately I think this is what we
|
||||||
|
// want to do, and we just have rules for how to prove
|
||||||
|
// `WellFormed` for everything above, instead of
|
||||||
|
// inlining a bit the rules of the proof here.
|
||||||
|
_ => chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::WellFormed(
|
||||||
|
chalk_ir::WellFormed::Ty(ty.lower_into(interner)),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
// FIXME(chalk): handle well formed consts
|
||||||
|
GenericArgKind::Const(..) => {
|
||||||
chalk_ir::GoalData::All(chalk_ir::Goals::new(interner))
|
chalk_ir::GoalData::All(chalk_ir::Goals::new(interner))
|
||||||
}
|
}
|
||||||
|
GenericArgKind::Lifetime(lt) => bug!("unexpect well formed predicate: {:?}", lt),
|
||||||
// FIXME(chalk): Well-formed only if ref lifetime outlives type
|
|
||||||
ty::Ref(..) => chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)),
|
|
||||||
|
|
||||||
ty::Param(..) => panic!("No Params expected."),
|
|
||||||
|
|
||||||
// FIXME(chalk) -- ultimately I think this is what we
|
|
||||||
// want to do, and we just have rules for how to prove
|
|
||||||
// `WellFormed` for everything above, instead of
|
|
||||||
// inlining a bit the rules of the proof here.
|
|
||||||
_ => chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::WellFormed(
|
|
||||||
chalk_ir::WellFormed::Ty(ty.lower_into(interner)),
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// FIXME(chalk): other predicates
|
// FIXME(chalk): other predicates
|
||||||
|
|||||||
@@ -47,14 +47,14 @@ fn compute_implied_outlives_bounds<'tcx>(
|
|||||||
// process it next. Currently (at least) these resulting
|
// process it next. Currently (at least) these resulting
|
||||||
// predicates are always guaranteed to be a subset of the original
|
// predicates are always guaranteed to be a subset of the original
|
||||||
// type, so we need not fear non-termination.
|
// type, so we need not fear non-termination.
|
||||||
let mut wf_types = vec![ty];
|
let mut wf_args = vec![ty.into()];
|
||||||
|
|
||||||
let mut implied_bounds = vec![];
|
let mut implied_bounds = vec![];
|
||||||
|
|
||||||
let mut fulfill_cx = FulfillmentContext::new();
|
let mut fulfill_cx = FulfillmentContext::new();
|
||||||
|
|
||||||
while let Some(ty) = wf_types.pop() {
|
while let Some(arg) = wf_args.pop() {
|
||||||
// Compute the obligations for `ty` to be well-formed. If `ty` is
|
// Compute the obligations for `arg` to be well-formed. If `arg` is
|
||||||
// an unresolved inference variable, just substituted an empty set
|
// an unresolved inference variable, just substituted an empty set
|
||||||
// -- because the return type here is going to be things we *add*
|
// -- because the return type here is going to be things we *add*
|
||||||
// to the environment, it's always ok for this set to be smaller
|
// to the environment, it's always ok for this set to be smaller
|
||||||
@@ -62,7 +62,7 @@ fn compute_implied_outlives_bounds<'tcx>(
|
|||||||
// unresolved inference variables here anyway, but there might be
|
// unresolved inference variables here anyway, but there might be
|
||||||
// during typeck under some circumstances.)
|
// during typeck under some circumstances.)
|
||||||
let obligations =
|
let obligations =
|
||||||
wf::obligations(infcx, param_env, hir::CRATE_HIR_ID, ty, DUMMY_SP).unwrap_or(vec![]);
|
wf::obligations(infcx, param_env, hir::CRATE_HIR_ID, arg, DUMMY_SP).unwrap_or(vec![]);
|
||||||
|
|
||||||
// N.B., all of these predicates *ought* to be easily proven
|
// N.B., all of these predicates *ought* to be easily proven
|
||||||
// true. In fact, their correctness is (mostly) implied by
|
// true. In fact, their correctness is (mostly) implied by
|
||||||
@@ -103,8 +103,8 @@ fn compute_implied_outlives_bounds<'tcx>(
|
|||||||
| ty::PredicateKind::ConstEvaluatable(..)
|
| ty::PredicateKind::ConstEvaluatable(..)
|
||||||
| ty::PredicateKind::ConstEquate(..) => vec![],
|
| ty::PredicateKind::ConstEquate(..) => vec![],
|
||||||
|
|
||||||
ty::PredicateKind::WellFormed(subty) => {
|
&ty::PredicateKind::WellFormed(arg) => {
|
||||||
wf_types.push(subty);
|
wf_args.push(arg);
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ impl AscribeUserTypeCx<'me, 'tcx> {
|
|||||||
self.relate(self_ty, Variance::Invariant, impl_self_ty)?;
|
self.relate(self_ty, Variance::Invariant, impl_self_ty)?;
|
||||||
|
|
||||||
self.prove_predicate(
|
self.prove_predicate(
|
||||||
ty::PredicateKind::WellFormed(impl_self_ty).to_predicate(self.tcx()),
|
ty::PredicateKind::WellFormed(impl_self_ty.into()).to_predicate(self.tcx()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ impl AscribeUserTypeCx<'me, 'tcx> {
|
|||||||
// them? This would only be relevant if some input
|
// them? This would only be relevant if some input
|
||||||
// type were ill-formed but did not appear in `ty`,
|
// type were ill-formed but did not appear in `ty`,
|
||||||
// which...could happen with normalization...
|
// which...could happen with normalization...
|
||||||
self.prove_predicate(ty::PredicateKind::WellFormed(ty).to_predicate(self.tcx()));
|
self.prove_predicate(ty::PredicateKind::WellFormed(ty.into()).to_predicate(self.tcx()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// we must check that return type of called functions is WF:
|
// we must check that return type of called functions is WF:
|
||||||
self.register_wf_obligation(output, call_expr.span, traits::MiscObligation);
|
self.register_wf_obligation(output.into(), call_expr.span, traits::MiscObligation);
|
||||||
|
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -413,7 +413,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
|||||||
// the function type must also be well-formed (this is not
|
// the function type must also be well-formed (this is not
|
||||||
// implied by the substs being well-formed because of inherent
|
// implied by the substs being well-formed because of inherent
|
||||||
// impls and late-bound regions - see issue #28609).
|
// impls and late-bound regions - see issue #28609).
|
||||||
self.register_wf_obligation(fty, self.span, traits::MiscObligation);
|
self.register_wf_obligation(fty.into(), self.span, traits::MiscObligation);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -401,7 +401,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
obligations.push(traits::Obligation::new(
|
obligations.push(traits::Obligation::new(
|
||||||
cause,
|
cause,
|
||||||
self.param_env,
|
self.param_env,
|
||||||
ty::PredicateKind::WellFormed(method_ty).to_predicate(tcx),
|
ty::PredicateKind::WellFormed(method_ty.into()).to_predicate(tcx),
|
||||||
));
|
));
|
||||||
|
|
||||||
let callee = MethodCallee { def_id, substs: trait_ref.substs, sig: fn_sig };
|
let callee = MethodCallee { def_id, substs: trait_ref.substs, sig: fn_sig };
|
||||||
|
|||||||
@@ -121,9 +121,8 @@ use rustc_middle::ty::adjustment::{
|
|||||||
};
|
};
|
||||||
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
|
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
|
||||||
use rustc_middle::ty::query::Providers;
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_middle::ty::subst::{
|
use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef};
|
||||||
GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts,
|
use rustc_middle::ty::subst::{GenericArgKind, UserSelfTy, UserSubsts};
|
||||||
};
|
|
||||||
use rustc_middle::ty::util::{Discr, IntTypeExt, Representability};
|
use rustc_middle::ty::util::{Discr, IntTypeExt, Representability};
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, AdtKind, CanonicalUserType, Const, GenericParamDefKind, RegionKind, ToPolyTraitRef,
|
self, AdtKind, CanonicalUserType, Const, GenericParamDefKind, RegionKind, ToPolyTraitRef,
|
||||||
@@ -3333,7 +3332,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
|
|
||||||
pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> {
|
pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> {
|
||||||
let t = AstConv::ast_ty_to_ty(self, ast_t);
|
let t = AstConv::ast_ty_to_ty(self, ast_t);
|
||||||
self.register_wf_obligation(t, ast_t.span, traits::MiscObligation);
|
self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation);
|
||||||
t
|
t
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3353,28 +3352,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> {
|
pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> {
|
||||||
let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id);
|
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);
|
let c = ty::Const::from_anon_const(self.tcx, const_def_id);
|
||||||
|
self.register_wf_obligation(
|
||||||
// HACK(eddyb) emulate what a `WellFormedConst` obligation would do.
|
c.into(),
|
||||||
// This code should be replaced with the proper WF handling ASAP.
|
self.tcx.hir().span(ast_c.hir_id),
|
||||||
if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = c.val {
|
ObligationCauseCode::MiscObligation,
|
||||||
assert!(promoted.is_none());
|
);
|
||||||
|
|
||||||
// HACK(eddyb) let's hope these are always empty.
|
|
||||||
// let obligations = self.nominal_obligations(def_id, substs);
|
|
||||||
// self.out.extend(obligations);
|
|
||||||
|
|
||||||
let cause = traits::ObligationCause::new(
|
|
||||||
self.tcx.def_span(const_def_id.to_def_id()),
|
|
||||||
self.body_id,
|
|
||||||
traits::MiscObligation,
|
|
||||||
);
|
|
||||||
self.register_predicate(traits::Obligation::new(
|
|
||||||
cause,
|
|
||||||
self.param_env,
|
|
||||||
ty::PredicateKind::ConstEvaluatable(def_id, substs).to_predicate(self.tcx),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3407,11 +3389,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers an obligation for checking later, during regionck, that the type `ty` must
|
/// Registers an obligation for checking later, during regionck, that `arg` is well-formed.
|
||||||
/// outlive the region `r`.
|
|
||||||
pub fn register_wf_obligation(
|
pub fn register_wf_obligation(
|
||||||
&self,
|
&self,
|
||||||
ty: Ty<'tcx>,
|
arg: subst::GenericArg<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
code: traits::ObligationCauseCode<'tcx>,
|
code: traits::ObligationCauseCode<'tcx>,
|
||||||
) {
|
) {
|
||||||
@@ -3420,16 +3401,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
self.register_predicate(traits::Obligation::new(
|
self.register_predicate(traits::Obligation::new(
|
||||||
cause,
|
cause,
|
||||||
self.param_env,
|
self.param_env,
|
||||||
ty::PredicateKind::WellFormed(ty).to_predicate(self.tcx),
|
ty::PredicateKind::WellFormed(arg).to_predicate(self.tcx),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers obligations that all types appearing in `substs` are well-formed.
|
/// Registers obligations that all `substs` are well-formed.
|
||||||
pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) {
|
pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) {
|
||||||
for ty in substs.types() {
|
for arg in substs.iter().filter(|arg| {
|
||||||
if !ty.references_error() {
|
matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..))
|
||||||
self.register_wf_obligation(ty, expr.span, traits::MiscObligation);
|
}) {
|
||||||
}
|
self.register_wf_obligation(arg, expr.span, traits::MiscObligation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3901,8 +3882,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
|
|
||||||
// All the input types from the fn signature must outlive the call
|
// All the input types from the fn signature must outlive the call
|
||||||
// so as to validate implied bounds.
|
// so as to validate implied bounds.
|
||||||
for (fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) {
|
for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) {
|
||||||
self.register_wf_obligation(fn_input_ty, arg_expr.span, traits::MiscObligation);
|
self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation);
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected_arg_count = fn_inputs.len();
|
let expected_arg_count = fn_inputs.len();
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ fn check_associated_item(
|
|||||||
ty::AssocKind::Const => {
|
ty::AssocKind::Const => {
|
||||||
let ty = fcx.tcx.type_of(item.def_id);
|
let ty = fcx.tcx.type_of(item.def_id);
|
||||||
let ty = fcx.normalize_associated_types_in(span, &ty);
|
let ty = fcx.normalize_associated_types_in(span, &ty);
|
||||||
fcx.register_wf_obligation(ty, span, code.clone());
|
fcx.register_wf_obligation(ty.into(), span, code.clone());
|
||||||
}
|
}
|
||||||
ty::AssocKind::Fn => {
|
ty::AssocKind::Fn => {
|
||||||
let sig = fcx.tcx.fn_sig(item.def_id);
|
let sig = fcx.tcx.fn_sig(item.def_id);
|
||||||
@@ -313,7 +313,7 @@ fn check_associated_item(
|
|||||||
if item.defaultness.has_value() {
|
if item.defaultness.has_value() {
|
||||||
let ty = fcx.tcx.type_of(item.def_id);
|
let ty = fcx.tcx.type_of(item.def_id);
|
||||||
let ty = fcx.normalize_associated_types_in(span, &ty);
|
let ty = fcx.normalize_associated_types_in(span, &ty);
|
||||||
fcx.register_wf_obligation(ty, span, code.clone());
|
fcx.register_wf_obligation(ty.into(), span, code.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ty::AssocKind::OpaqueTy => {
|
ty::AssocKind::OpaqueTy => {
|
||||||
@@ -406,7 +406,7 @@ fn check_type_defn<'tcx, F>(
|
|||||||
// All field types must be well-formed.
|
// All field types must be well-formed.
|
||||||
for field in &variant.fields {
|
for field in &variant.fields {
|
||||||
fcx.register_wf_obligation(
|
fcx.register_wf_obligation(
|
||||||
field.ty,
|
field.ty.into(),
|
||||||
field.span,
|
field.span,
|
||||||
ObligationCauseCode::MiscObligation,
|
ObligationCauseCode::MiscObligation,
|
||||||
)
|
)
|
||||||
@@ -601,7 +601,7 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: hir::HirId, ty_span: Span, allow_fo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fcx.register_wf_obligation(item_ty, ty_span, ObligationCauseCode::MiscObligation);
|
fcx.register_wf_obligation(item_ty.into(), ty_span, ObligationCauseCode::MiscObligation);
|
||||||
if forbid_unsized {
|
if forbid_unsized {
|
||||||
fcx.register_bound(
|
fcx.register_bound(
|
||||||
item_ty,
|
item_ty,
|
||||||
@@ -650,7 +650,7 @@ fn check_impl<'tcx>(
|
|||||||
let self_ty = fcx.tcx.type_of(item_def_id);
|
let self_ty = fcx.tcx.type_of(item_def_id);
|
||||||
let self_ty = fcx.normalize_associated_types_in(item.span, &self_ty);
|
let self_ty = fcx.normalize_associated_types_in(item.span, &self_ty);
|
||||||
fcx.register_wf_obligation(
|
fcx.register_wf_obligation(
|
||||||
self_ty,
|
self_ty.into(),
|
||||||
ast_self_ty.span,
|
ast_self_ty.span,
|
||||||
ObligationCauseCode::MiscObligation,
|
ObligationCauseCode::MiscObligation,
|
||||||
);
|
);
|
||||||
@@ -698,7 +698,7 @@ fn check_where_clauses<'tcx, 'fcx>(
|
|||||||
// be sure if it will error or not as user might always specify the other.
|
// be sure if it will error or not as user might always specify the other.
|
||||||
if !ty.needs_subst() {
|
if !ty.needs_subst() {
|
||||||
fcx.register_wf_obligation(
|
fcx.register_wf_obligation(
|
||||||
ty,
|
ty.into(),
|
||||||
fcx.tcx.def_span(param.def_id),
|
fcx.tcx.def_span(param.def_id),
|
||||||
ObligationCauseCode::MiscObligation,
|
ObligationCauseCode::MiscObligation,
|
||||||
);
|
);
|
||||||
@@ -841,13 +841,13 @@ fn check_fn_or_method<'fcx, 'tcx>(
|
|||||||
let sig = fcx.normalize_associated_types_in(span, &sig);
|
let sig = fcx.normalize_associated_types_in(span, &sig);
|
||||||
let sig = fcx.tcx.liberate_late_bound_regions(def_id, &sig);
|
let sig = fcx.tcx.liberate_late_bound_regions(def_id, &sig);
|
||||||
|
|
||||||
for (input_ty, span) in sig.inputs().iter().zip(hir_sig.decl.inputs.iter().map(|t| t.span)) {
|
for (&input_ty, span) in sig.inputs().iter().zip(hir_sig.decl.inputs.iter().map(|t| t.span)) {
|
||||||
fcx.register_wf_obligation(&input_ty, span, ObligationCauseCode::MiscObligation);
|
fcx.register_wf_obligation(input_ty.into(), span, ObligationCauseCode::MiscObligation);
|
||||||
}
|
}
|
||||||
implied_bounds.extend(sig.inputs());
|
implied_bounds.extend(sig.inputs());
|
||||||
|
|
||||||
fcx.register_wf_obligation(
|
fcx.register_wf_obligation(
|
||||||
sig.output(),
|
sig.output().into(),
|
||||||
hir_sig.decl.output.span(),
|
hir_sig.decl.output.span(),
|
||||||
ObligationCauseCode::ReturnType,
|
ObligationCauseCode::ReturnType,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -332,12 +332,12 @@ fn check_predicates<'tcx>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Include the well-formed predicates of the type parameters of the impl.
|
// Include the well-formed predicates of the type parameters of the impl.
|
||||||
for ty in tcx.impl_trait_ref(impl1_def_id).unwrap().substs.types() {
|
for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().substs {
|
||||||
if let Some(obligations) = wf::obligations(
|
if let Some(obligations) = wf::obligations(
|
||||||
infcx,
|
infcx,
|
||||||
tcx.param_env(impl1_def_id),
|
tcx.param_env(impl1_def_id),
|
||||||
tcx.hir().as_local_hir_id(impl1_def_id),
|
tcx.hir().as_local_hir_id(impl1_def_id),
|
||||||
ty,
|
arg,
|
||||||
span,
|
span,
|
||||||
) {
|
) {
|
||||||
impl2_predicates
|
impl2_predicates
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ error[E0282]: type annotations needed
|
|||||||
--> $DIR/cannot-infer-const-args.rs:9:5
|
--> $DIR/cannot-infer-const-args.rs:9:5
|
||||||
|
|
|
|
||||||
LL | foo();
|
LL | foo();
|
||||||
| ^^^ cannot infer type for fn item `fn() -> usize {foo::<{_: usize}>}`
|
| ^^^
|
||||||
|
|
|
||||||
|
= note: unable to infer the value of a const parameter
|
||||||
|
|
||||||
error: aborting due to previous error; 1 warning emitted
|
error: aborting due to previous error; 1 warning emitted
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// check-pass
|
|
||||||
|
|
||||||
#![feature(const_generics)]
|
#![feature(const_generics)]
|
||||||
//~^ WARN the feature `const_generics` is incomplete
|
//~^ WARN the feature `const_generics` is incomplete
|
||||||
|
|
||||||
@@ -7,6 +5,7 @@ struct Const<const N: usize>;
|
|||||||
|
|
||||||
impl<const C: usize> Const<{C}> {
|
impl<const C: usize> Const<{C}> {
|
||||||
fn successor() -> Const<{C + 1}> {
|
fn successor() -> Const<{C + 1}> {
|
||||||
|
//~^ ERROR constant expression depends on a generic parameter
|
||||||
Const
|
Const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
|
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
--> $DIR/issue-61747.rs:3:12
|
--> $DIR/issue-61747.rs:1:12
|
||||||
|
|
|
|
||||||
LL | #![feature(const_generics)]
|
LL | #![feature(const_generics)]
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
@@ -7,5 +7,13 @@ LL | #![feature(const_generics)]
|
|||||||
= note: `#[warn(incomplete_features)]` on by default
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
|
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
|
||||||
|
|
||||||
warning: 1 warning emitted
|
error: constant expression depends on a generic parameter
|
||||||
|
--> $DIR/issue-61747.rs:7:23
|
||||||
|
|
|
||||||
|
LL | fn successor() -> Const<{C + 1}> {
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this may fail depending on what value the parameter takes
|
||||||
|
|
||||||
|
error: aborting due to previous error; 1 warning emitted
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// check-pass
|
|
||||||
|
|
||||||
#![feature(const_generics)]
|
#![feature(const_generics)]
|
||||||
//~^ WARN the feature `const_generics` is incomplete
|
//~^ WARN the feature `const_generics` is incomplete
|
||||||
|
|
||||||
@@ -8,6 +6,7 @@ trait Foo {}
|
|||||||
impl<const N: usize> Foo for [(); N]
|
impl<const N: usize> Foo for [(); N]
|
||||||
where
|
where
|
||||||
Self:FooImpl<{N==0}>
|
Self:FooImpl<{N==0}>
|
||||||
|
//~^ERROR constant expression depends on a generic parameter
|
||||||
{}
|
{}
|
||||||
|
|
||||||
trait FooImpl<const IS_ZERO: bool>{}
|
trait FooImpl<const IS_ZERO: bool>{}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
|
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
--> $DIR/issue-61935.rs:3:12
|
--> $DIR/issue-61935.rs:1:12
|
||||||
|
|
|
|
||||||
LL | #![feature(const_generics)]
|
LL | #![feature(const_generics)]
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
@@ -7,5 +7,13 @@ LL | #![feature(const_generics)]
|
|||||||
= note: `#[warn(incomplete_features)]` on by default
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
|
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
|
||||||
|
|
||||||
warning: 1 warning emitted
|
error: constant expression depends on a generic parameter
|
||||||
|
--> $DIR/issue-61935.rs:8:14
|
||||||
|
|
|
||||||
|
LL | Self:FooImpl<{N==0}>
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this may fail depending on what value the parameter takes
|
||||||
|
|
||||||
|
error: aborting due to previous error; 1 warning emitted
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// build-pass
|
|
||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
#![feature(const_generics)]
|
#![feature(const_generics)]
|
||||||
|
|
||||||
pub struct Vector<T, const N: usize>([T; N]);
|
pub struct Vector<T, const N: usize>([T; N]);
|
||||||
|
|
||||||
pub type TruncatedVector<T, const N: usize> = Vector<T, { N - 1 }>;
|
pub type TruncatedVector<T, const N: usize> = Vector<T, { N - 1 }>;
|
||||||
@@ -9,6 +8,7 @@ pub type TruncatedVector<T, const N: usize> = Vector<T, { N - 1 }>;
|
|||||||
impl<T, const N: usize> Vector<T, { N }> {
|
impl<T, const N: usize> Vector<T, { N }> {
|
||||||
/// Drop the last component and return the vector with one fewer dimension.
|
/// Drop the last component and return the vector with one fewer dimension.
|
||||||
pub fn trunc(self) -> (TruncatedVector<T, { N }>, T) {
|
pub fn trunc(self) -> (TruncatedVector<T, { N }>, T) {
|
||||||
|
//~^ ERROR constant expression depends on a generic parameter
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/test/ui/const-generics/issues/issue-62220.stderr
Normal file
10
src/test/ui/const-generics/issues/issue-62220.stderr
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
error: constant expression depends on a generic parameter
|
||||||
|
--> $DIR/issue-62220.rs:10:27
|
||||||
|
|
|
||||||
|
LL | pub fn trunc(self) -> (TruncatedVector<T, { N }>, T) {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this may fail depending on what value the parameter takes
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#![allow(incomplete_features, dead_code, unconditional_recursion)]
|
#![allow(dead_code, unconditional_recursion)]
|
||||||
#![feature(const_generics)]
|
#![feature(const_generics)]
|
||||||
#![feature(lazy_normalization_consts)]
|
//~^ WARN the feature `const_generics` is incomplete
|
||||||
|
|
||||||
fn fact<const N: usize>() {
|
fn fact<const N: usize>() {
|
||||||
fact::<{ N - 1 }>();
|
fact::<{ N - 1 }>();
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/issue-66205.rs:2:12
|
||||||
|
|
|
||||||
|
LL | #![feature(const_generics)]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
|
||||||
|
|
||||||
error: constant expression depends on a generic parameter
|
error: constant expression depends on a generic parameter
|
||||||
--> $DIR/issue-66205.rs:6:12
|
--> $DIR/issue-66205.rs:6:12
|
||||||
|
|
|
|
||||||
@@ -6,5 +15,5 @@ LL | fact::<{ N - 1 }>();
|
|||||||
|
|
|
|
||||||
= note: this may fail depending on what value the parameter takes
|
= note: this may fail depending on what value the parameter takes
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error; 1 warning emitted
|
||||||
|
|
||||||
|
|||||||
40
src/test/ui/const-generics/issues/issue-68977.rs
Normal file
40
src/test/ui/const-generics/issues/issue-68977.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#![feature(const_generics)]
|
||||||
|
//~^ WARN the feature `const_generics` is incomplete
|
||||||
|
|
||||||
|
struct PhantomU8<const X: u8>;
|
||||||
|
|
||||||
|
trait FxpStorage {
|
||||||
|
type SInt; // Add arithmetic traits as needed.
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! fxp_storage_impls {
|
||||||
|
($($($n:literal)|+ => $sint:ty),* $(,)?) => {
|
||||||
|
$($(impl FxpStorage for PhantomU8<$n> {
|
||||||
|
type SInt = $sint;
|
||||||
|
})*)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fxp_storage_impls! {
|
||||||
|
1 => i8,
|
||||||
|
2 => i16,
|
||||||
|
3 | 4 => i32,
|
||||||
|
5 | 6 | 7 | 8 => i64,
|
||||||
|
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 => i128,
|
||||||
|
}
|
||||||
|
|
||||||
|
type FxpStorageHelper<const INT_BITS: u8, const FRAC_BITS: u8> =
|
||||||
|
PhantomU8<{(INT_BITS + FRAC_BITS + 7) / 8}>;
|
||||||
|
|
||||||
|
struct Fxp<const INT_BITS: u8, const FRAC_BITS: u8>
|
||||||
|
where
|
||||||
|
FxpStorageHelper<INT_BITS, FRAC_BITS>: FxpStorage,
|
||||||
|
//~^ ERROR constant expression depends on a generic parameter
|
||||||
|
{
|
||||||
|
storage: <FxpStorageHelper<INT_BITS, FRAC_BITS> as FxpStorage>::SInt,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Fxp::<1, 15> { storage: 0i16 };
|
||||||
|
Fxp::<2, 15> { storage: 0i32 };
|
||||||
|
}
|
||||||
19
src/test/ui/const-generics/issues/issue-68977.stderr
Normal file
19
src/test/ui/const-generics/issues/issue-68977.stderr
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/issue-68977.rs:1:12
|
||||||
|
|
|
||||||
|
LL | #![feature(const_generics)]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
|
||||||
|
|
||||||
|
error: constant expression depends on a generic parameter
|
||||||
|
--> $DIR/issue-68977.rs:31:44
|
||||||
|
|
|
||||||
|
LL | FxpStorageHelper<INT_BITS, FRAC_BITS>: FxpStorage,
|
||||||
|
| ^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this may fail depending on what value the parameter takes
|
||||||
|
|
||||||
|
error: aborting due to previous error; 1 warning emitted
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// run-pass
|
|
||||||
#![feature(const_generics)]
|
#![feature(const_generics)]
|
||||||
#![allow(incomplete_features)]
|
//~^ WARN the feature `const_generics` is incomplete
|
||||||
trait Foo {}
|
trait Foo {}
|
||||||
|
|
||||||
impl<const N: usize> Foo for [(); N] where Self: FooImpl<{ N == 0 }> {}
|
impl<const N: usize> Foo for [(); N] where Self: FooImpl<{ N == 0 }> {}
|
||||||
|
//~^ ERROR constant expression depends on a generic parameter
|
||||||
|
|
||||||
trait FooImpl<const IS_ZERO: bool> {}
|
trait FooImpl<const IS_ZERO: bool> {}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/issue-71922.rs:1:12
|
||||||
|
|
|
||||||
|
LL | #![feature(const_generics)]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
|
||||||
|
|
||||||
|
error: constant expression depends on a generic parameter
|
||||||
|
--> $DIR/issue-71922.rs:5:50
|
||||||
|
|
|
||||||
|
LL | impl<const N: usize> Foo for [(); N] where Self: FooImpl<{ N == 0 }> {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this may fail depending on what value the parameter takes
|
||||||
|
|
||||||
|
error: aborting due to previous error; 1 warning emitted
|
||||||
|
|
||||||
16
src/test/ui/const-generics/wf-misc.rs
Normal file
16
src/test/ui/const-generics/wf-misc.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#![feature(const_generics)]
|
||||||
|
//~^ WARN the feature `const_generics` is incomplete
|
||||||
|
|
||||||
|
pub fn arr_len<const N: usize>() {
|
||||||
|
let _: [u8; N + 1];
|
||||||
|
//~^ ERROR constant expression depends on a generic parameter
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Const<const N: usize>;
|
||||||
|
|
||||||
|
pub fn func_call<const N: usize>() {
|
||||||
|
let _: Const::<{N + 1}>;
|
||||||
|
//~^ ERROR constant expression depends on a generic parameter
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
27
src/test/ui/const-generics/wf-misc.stderr
Normal file
27
src/test/ui/const-generics/wf-misc.stderr
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/wf-misc.rs:1:12
|
||||||
|
|
|
||||||
|
LL | #![feature(const_generics)]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
|
||||||
|
|
||||||
|
error: constant expression depends on a generic parameter
|
||||||
|
--> $DIR/wf-misc.rs:5:12
|
||||||
|
|
|
||||||
|
LL | let _: [u8; N + 1];
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this may fail depending on what value the parameter takes
|
||||||
|
|
||||||
|
error: constant expression depends on a generic parameter
|
||||||
|
--> $DIR/wf-misc.rs:12:12
|
||||||
|
|
|
||||||
|
LL | let _: Const::<{N + 1}>;
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this may fail depending on what value the parameter takes
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors; 1 warning emitted
|
||||||
|
|
||||||
Reference in New Issue
Block a user