1
Fork 0

Auto merge of #109442 - Nilstrieb:rollup-seb5xsa, r=Nilstrieb

Rollup of 10 pull requests

Successful merges:

 - #106434 (Document `Iterator::sum/product` for Option/Result)
 - #108326 (Implement read_buf for a few more types)
 - #108842 (Enforce non-lifetime-binders in supertrait preds are not object safe)
 - #108896 (new solver: make all goal evaluation able to be automatically rerun )
 - #109124 (Add `dist.compression-profile` option to control compression speed)
 - #109240 (Walk un-shifted nested `impl Trait` in trait when setting up default trait method assumptions)
 - #109385 (fix typo)
 - #109386 (add myself to mailmap)
 - #109390 (Custom MIR: Support aggregate expressions)
 - #109408 (not *all* retags might be explicit in Runtime MIR)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-03-21 12:06:26 +00:00
commit a01b4cc9f3
55 changed files with 1228 additions and 556 deletions

View file

@ -29,6 +29,8 @@ Alexander Ronald Altman <alexanderaltman@me.com>
Alexandre Martin <martin.alex32@hotmail.fr>
Alexis Beingessner <a.beingessner@gmail.com>
Alfie John <alfie@alfie.wtf> Alfie John <alfiej@fastmail.fm>
Alona Enraght-Moony <code@alona.page> <nixon.emoony@gmail.com>
Alona Enraght-Moony <code@alona.page> <nixon@caminus.local>
Amos Onn <amosonn@gmail.com>
Ana-Maria Mihalache <mihalacheana.maria@yahoo.com>
Anatoly Ikorsky <aikorsky@gmail.com>
@ -415,7 +417,6 @@ Nicolas Abram <abramlujan@gmail.com>
Nicole Mazzuca <npmazzuca@gmail.com>
Nif Ward <nif.ward@gmail.com>
Nika Layzell <nika@thelayzells.com> <michael@thelayzells.com>
Nixon Enraght-Moony <nixon.emoony@gmail.com>
NODA Kai <nodakai@gmail.com>
oliver <16816606+o752d@users.noreply.github.com>
Oliver Middleton <olliemail27@gmail.com> <ollie27@users.noreply.github.com>

View file

@ -605,7 +605,7 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
found: Option<ty::OpaqueHiddenType<'tcx>>,
/// In the presence of dead code, typeck may figure out a hidden type
/// while borrowck will now. We collect these cases here and check at
/// while borrowck will not. We collect these cases here and check at
/// the end that we actually found a type that matches (modulo regions).
typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>,
}

View file

@ -78,7 +78,8 @@ pub enum MirPhase {
/// MIR, this is UB.
/// - Retags: If `-Zmir-emit-retag` is enabled, analysis MIR has "implicit" retags in the same way
/// that Rust itself has them. Where exactly these are is generally subject to change, and so we
/// don't document this here. Runtime MIR has all retags explicit.
/// don't document this here. Runtime MIR has most retags explicit (though implicit retags
/// can still occur at `Rvalue::{Ref,AddrOf}`).
/// - Generator bodies: In analysis MIR, locals may actually be behind a pointer that user code has
/// access to. This occurs in generator bodies. Such locals do not behave like other locals,
/// because they eg may be aliased in surprising ways. Runtime MIR has no such special locals -
@ -1165,7 +1166,7 @@ pub enum AggregateKind<'tcx> {
Tuple,
/// The second field is the variant index. It's equal to 0 for struct
/// and union expressions. The fourth field is
/// and union expressions. The last field is the
/// active field number and is present only for union expressions
/// -- e.g., for a union expression `SomeUnion { c: .. }`, the
/// active field index would identity the field `c`

View file

@ -897,6 +897,9 @@ pub enum ObjectSafetyViolation {
/// (e.g., `trait Foo : Bar<Self>`).
SupertraitSelf(SmallVec<[Span; 1]>),
// Supertrait has a non-lifetime `for<T>` binder.
SupertraitNonLifetimeBinder(SmallVec<[Span; 1]>),
/// Method has something illegal.
Method(Symbol, MethodViolationCode, Span),
@ -919,6 +922,9 @@ impl ObjectSafetyViolation {
.into()
}
}
ObjectSafetyViolation::SupertraitNonLifetimeBinder(_) => {
format!("where clause cannot reference non-lifetime `for<...>` variables").into()
}
ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => {
format!("associated function `{}` has no `self` parameter", name).into()
}
@ -969,7 +975,9 @@ impl ObjectSafetyViolation {
pub fn solution(&self, err: &mut Diagnostic) {
match self {
ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) => {}
ObjectSafetyViolation::SizedSelf(_)
| ObjectSafetyViolation::SupertraitSelf(_)
| ObjectSafetyViolation::SupertraitNonLifetimeBinder(..) => {}
ObjectSafetyViolation::Method(
name,
MethodViolationCode::StaticMethod(Some((add_self_sugg, make_sized_sugg))),
@ -1023,7 +1031,8 @@ impl ObjectSafetyViolation {
// diagnostics use a `note` instead of a `span_label`.
match self {
ObjectSafetyViolation::SupertraitSelf(spans)
| ObjectSafetyViolation::SizedSelf(spans) => spans.clone(),
| ObjectSafetyViolation::SizedSelf(spans)
| ObjectSafetyViolation::SupertraitNonLifetimeBinder(spans) => spans.clone(),
ObjectSafetyViolation::AssocConst(_, span)
| ObjectSafetyViolation::GAT(_, span)
| ObjectSafetyViolation::Method(_, _, span)

View file

@ -51,9 +51,7 @@ where
// Region folder
impl<'tcx> TyCtxt<'tcx> {
/// Folds the escaping and free regions in `value` using `f`, and
/// sets `skipped_regions` to true if any late-bound region was found
/// and skipped.
/// Folds the escaping and free regions in `value` using `f`.
pub fn fold_regions<T>(
self,
value: T,
@ -64,17 +62,6 @@ impl<'tcx> TyCtxt<'tcx> {
{
value.fold_with(&mut RegionFolder::new(self, &mut f))
}
pub fn super_fold_regions<T>(
self,
value: T,
mut f: impl FnMut(ty::Region<'tcx>, ty::DebruijnIndex) -> ty::Region<'tcx>,
) -> T
where
T: TypeSuperFoldable<TyCtxt<'tcx>>,
{
value.super_fold_with(&mut RegionFolder::new(self, &mut f))
}
}
/// Folds over the substructure of a type, visiting its component

View file

@ -166,6 +166,28 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
let cast_kind = mir_cast_kind(source_ty, expr.ty);
Ok(Rvalue::Cast(cast_kind, source, expr.ty))
},
ExprKind::Tuple { fields } => Ok(
Rvalue::Aggregate(
Box::new(AggregateKind::Tuple),
fields.iter().map(|e| self.parse_operand(*e)).collect::<Result<_, _>>()?
)
),
ExprKind::Array { fields } => {
let elem_ty = expr.ty.builtin_index().expect("ty must be an array");
Ok(Rvalue::Aggregate(
Box::new(AggregateKind::Array(elem_ty)),
fields.iter().map(|e| self.parse_operand(*e)).collect::<Result<_, _>>()?
))
},
ExprKind::Adt(box AdtExpr{ adt_def, variant_index, substs, fields, .. }) => {
let is_union = adt_def.is_union();
let active_field_index = is_union.then(|| fields[0].name.index());
Ok(Rvalue::Aggregate(
Box::new(AggregateKind::Adt(adt_def.did(), *variant_index, substs, None, active_field_index)),
fields.iter().map(|f| self.parse_operand(f.expr)).collect::<Result<_, _>>()?
))
},
_ => self.parse_operand(expr_id).map(Rvalue::Use),
)
}

View file

@ -224,7 +224,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
if goal.predicate.self_ty().is_ty_var() {
return vec![Candidate {
source: CandidateSource::BuiltinImpl,
result: self.make_canonical_response(Certainty::AMBIGUOUS).unwrap(),
result: self
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
.unwrap(),
}];
}
@ -261,8 +263,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else {
return
};
self.probe(|this| {
let normalized_ty = this.next_ty_infer();
self.probe(|ecx| {
let normalized_ty = ecx.next_ty_infer();
let normalizes_to_goal = goal.with(
tcx,
ty::Binder::dummy(ty::ProjectionPredicate {
@ -270,28 +273,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
term: normalized_ty.into(),
}),
);
let normalization_certainty = match this.evaluate_goal(normalizes_to_goal) {
Ok((_, certainty)) => certainty,
Err(NoSolution) => return,
};
let normalized_ty = this.resolve_vars_if_possible(normalized_ty);
ecx.add_goal(normalizes_to_goal);
if let Ok(_) = ecx.try_evaluate_added_goals() {
let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
// NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
// This doesn't work as long as we use `CandidateSource` in winnowing.
let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
let normalized_candidates = this.assemble_and_evaluate_candidates(goal);
for mut normalized_candidate in normalized_candidates {
normalized_candidate.result =
normalized_candidate.result.unchecked_map(|mut response| {
// FIXME: This currently hides overflow in the normalization step of the self type
// which is probably wrong. Maybe `unify_and` should actually keep overflow as
// we treat it as non-fatal anyways.
response.certainty = response.certainty.unify_and(normalization_certainty);
response
});
candidates.push(normalized_candidate);
candidates.extend(ecx.assemble_and_evaluate_candidates(goal));
}
})
});
}
fn assemble_impl_candidates<G: GoalKind<'tcx>>(
@ -516,7 +507,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
} else {
Certainty::AMBIGUOUS
};
return self.make_canonical_response(certainty);
return self.evaluate_added_goals_and_make_canonical_response(certainty);
}
}
@ -538,14 +529,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
}
fn discard_reservation_impl(&self, mut candidate: Candidate<'tcx>) -> Candidate<'tcx> {
fn discard_reservation_impl(&mut self, mut candidate: Candidate<'tcx>) -> Candidate<'tcx> {
if let CandidateSource::Impl(def_id) = candidate.source {
if let ty::ImplPolarity::Reservation = self.tcx().impl_polarity(def_id) {
debug!("Selected reservation impl");
// We assemble all candidates inside of a probe so by
// making a new canonical response here our result will
// have no constraints.
candidate.result = self.make_canonical_response(Certainty::AMBIGUOUS).unwrap();
candidate.result = self
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
.unwrap();
}
}

View file

@ -48,7 +48,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
/// - `external_constraints`: additional constraints which aren't expressable
/// using simple unification of inference variables.
#[instrument(level = "debug", skip(self))]
pub(super) fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> {
pub(super) fn evaluate_added_goals_and_make_canonical_response(
&mut self,
certainty: Certainty,
) -> QueryResult<'tcx> {
let goals_certainty = self.try_evaluate_added_goals()?;
let certainty = certainty.unify_and(goals_certainty);
let external_constraints = self.compute_external_query_constraints()?;
let response = Response { var_values: self.var_values, external_constraints, certainty };
@ -209,7 +215,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// FIXME: To deal with #105787 I also expect us to emit nested obligations here at
// some point. We can figure out how to deal with this once we actually have
// an ICE.
let nested_goals = self.eq(param_env, orig, response)?;
let nested_goals = self.eq_and_get_goals(param_env, orig, response)?;
assert!(nested_goals.is_empty(), "{nested_goals:?}");
}

View file

@ -2,8 +2,11 @@ use rustc_hir::def_id::DefId;
use rustc_infer::infer::at::ToTrace;
use rustc_infer::infer::canonical::CanonicalVarValues;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime};
use rustc_infer::infer::{
DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, TyCtxtInferExt,
};
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryResult};
use rustc_infer::traits::ObligationCause;
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::ty::{
@ -13,8 +16,8 @@ use rustc_middle::ty::{
use rustc_span::DUMMY_SP;
use std::ops::ControlFlow;
use super::search_graph::SearchGraph;
use super::Goal;
use super::search_graph::{self, OverflowHandler};
use super::{search_graph::SearchGraph, Goal};
pub struct EvalCtxt<'a, 'tcx> {
// FIXME: should be private.
@ -33,14 +36,305 @@ pub struct EvalCtxt<'a, 'tcx> {
pub(super) search_graph: &'a mut SearchGraph<'tcx>,
/// This field is used by a debug assertion in [`EvalCtxt::evaluate_goal`],
/// see the comment in that method for more details.
pub in_projection_eq_hack: bool,
pub(super) nested_goals: NestedGoals<'tcx>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(super) enum IsNormalizesToHack {
Yes,
No,
}
#[derive(Debug, Clone)]
pub(super) struct NestedGoals<'tcx> {
pub(super) normalizes_to_hack_goal: Option<Goal<'tcx, ty::ProjectionPredicate<'tcx>>>,
pub(super) goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
}
impl NestedGoals<'_> {
pub(super) fn new() -> Self {
Self { normalizes_to_hack_goal: None, goals: Vec::new() }
}
pub(super) fn is_empty(&self) -> bool {
self.normalizes_to_hack_goal.is_none() && self.goals.is_empty()
}
}
pub trait InferCtxtEvalExt<'tcx> {
/// Evaluates a goal from **outside** of the trait solver.
///
/// Using this while inside of the solver is wrong as it uses a new
/// search graph which would break cycle detection.
fn evaluate_root_goal(
&self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
) -> Result<(bool, Certainty), NoSolution>;
}
impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
#[instrument(level = "debug", skip(self))]
fn evaluate_root_goal(
&self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
) -> Result<(bool, Certainty), NoSolution> {
let mut search_graph = search_graph::SearchGraph::new(self.tcx);
let mut ecx = EvalCtxt {
search_graph: &mut search_graph,
infcx: self,
// Only relevant when canonicalizing the response.
max_input_universe: ty::UniverseIndex::ROOT,
var_values: CanonicalVarValues::dummy(),
nested_goals: NestedGoals::new(),
};
let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal);
assert!(
ecx.nested_goals.is_empty(),
"root `EvalCtxt` should not have any goals added to it"
);
assert!(search_graph.is_empty());
result
}
}
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
/// The entry point of the solver.
///
/// This function deals with (coinductive) cycles, overflow, and caching
/// and then calls [`EvalCtxt::compute_goal`] which contains the actual
/// logic of the solver.
///
/// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal]
/// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're
/// outside of it.
#[instrument(level = "debug", skip(tcx, search_graph), ret)]
fn evaluate_canonical_goal(
tcx: TyCtxt<'tcx>,
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
canonical_goal: CanonicalGoal<'tcx>,
) -> QueryResult<'tcx> {
// Deal with overflow, caching, and coinduction.
//
// The actual solver logic happens in `ecx.compute_goal`.
search_graph.with_new_goal(tcx, canonical_goal, |search_graph| {
let (ref infcx, goal, var_values) =
tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
let mut ecx = EvalCtxt {
infcx,
var_values,
max_input_universe: canonical_goal.max_universe,
search_graph,
nested_goals: NestedGoals::new(),
};
ecx.compute_goal(goal)
})
}
/// Recursively evaluates `goal`, returning whether any inference vars have
/// been constrained and the certainty of the result.
fn evaluate_goal(
&mut self,
is_normalizes_to_hack: IsNormalizesToHack,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
) -> Result<(bool, Certainty), NoSolution> {
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
let canonical_response =
EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
let has_changed = !canonical_response.value.var_values.is_identity();
let certainty = self.instantiate_and_apply_query_response(
goal.param_env,
orig_values,
canonical_response,
)?;
// Check that rerunning this query with its inference constraints applied
// doesn't result in new inference constraints and has the same result.
//
// If we have projection goals like `<T as Trait>::Assoc == u32` we recursively
// call `exists<U> <T as Trait>::Assoc == U` to enable better caching. This goal
// could constrain `U` to `u32` which would cause this check to result in a
// solver cycle.
if cfg!(debug_assertions)
&& has_changed
&& is_normalizes_to_hack == IsNormalizesToHack::No
&& !self.search_graph.in_cycle()
{
debug!("rerunning goal to check result is stable");
let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
let canonical_response =
EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
if !canonical_response.value.var_values.is_identity() {
bug!("unstable result: {goal:?} {canonical_goal:?} {canonical_response:?}");
}
assert_eq!(certainty, canonical_response.value.certainty);
}
Ok((has_changed, certainty))
}
fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
let Goal { param_env, predicate } = goal;
let kind = predicate.kind();
if let Some(kind) = kind.no_bound_vars() {
match kind {
ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
self.compute_trait_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
self.compute_projection_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => {
self.compute_type_outlives_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
self.compute_region_outlives_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) })
}
ty::PredicateKind::Subtype(predicate) => {
self.compute_subtype_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Coerce(predicate) => {
self.compute_coerce_goal(Goal { param_env, predicate })
}
ty::PredicateKind::ClosureKind(def_id, substs, kind) => self
.compute_closure_kind_goal(Goal {
param_env,
predicate: (def_id, substs, kind),
}),
ty::PredicateKind::ObjectSafe(trait_def_id) => {
self.compute_object_safe_goal(trait_def_id)
}
ty::PredicateKind::WellFormed(arg) => {
self.compute_well_formed_goal(Goal { param_env, predicate: arg })
}
ty::PredicateKind::Ambiguous => {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
// FIXME: implement these predicates :)
ty::PredicateKind::ConstEvaluatable(_) | ty::PredicateKind::ConstEquate(_, _) => {
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
ty::PredicateKind::TypeWellFormedFromEnv(..) => {
bug!("TypeWellFormedFromEnv is only used for Chalk")
}
ty::PredicateKind::AliasEq(lhs, rhs) => {
self.compute_alias_eq_goal(Goal { param_env, predicate: (lhs, rhs) })
}
}
} else {
let kind = self.infcx.instantiate_binder_with_placeholders(kind);
let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
self.add_goal(goal);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
// Recursively evaluates all the goals added to this `EvalCtxt` to completion, returning
// the certainty of all the goals.
#[instrument(level = "debug", skip(self))]
pub(super) fn try_evaluate_added_goals(&mut self) -> Result<Certainty, NoSolution> {
let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new());
let mut new_goals = NestedGoals::new();
let response = self.repeat_while_none(
|_| Ok(Certainty::Maybe(MaybeCause::Overflow)),
|this| {
let mut has_changed = Err(Certainty::Yes);
if let Some(goal) = goals.normalizes_to_hack_goal.take() {
let (_, certainty) = match this.evaluate_goal(
IsNormalizesToHack::Yes,
goal.with(this.tcx(), ty::Binder::dummy(goal.predicate)),
) {
Ok(r) => r,
Err(NoSolution) => return Some(Err(NoSolution)),
};
if goal.predicate.projection_ty
!= this.resolve_vars_if_possible(goal.predicate.projection_ty)
{
has_changed = Ok(())
}
match certainty {
Certainty::Yes => {}
Certainty::Maybe(_) => {
let goal = this.resolve_vars_if_possible(goal);
// The rhs of this `normalizes-to` must always be an unconstrained infer var as it is
// the hack used by `normalizes-to` to ensure that every `normalizes-to` behaves the same
// regardless of the rhs.
//
// However it is important not to unconditionally replace the rhs with a new infer var
// as otherwise we may replace the original unconstrained infer var with a new infer var
// and never propagate any constraints on the new var back to the original var.
let term = this
.term_is_fully_unconstrained(goal)
.then_some(goal.predicate.term)
.unwrap_or_else(|| {
this.next_term_infer_of_kind(goal.predicate.term)
});
let projection_pred = ty::ProjectionPredicate {
term,
projection_ty: goal.predicate.projection_ty,
};
new_goals.normalizes_to_hack_goal =
Some(goal.with(this.tcx(), projection_pred));
has_changed = has_changed.map_err(|c| c.unify_and(certainty));
}
}
}
for nested_goal in goals.goals.drain(..) {
let (changed, certainty) =
match this.evaluate_goal(IsNormalizesToHack::No, nested_goal) {
Ok(result) => result,
Err(NoSolution) => return Some(Err(NoSolution)),
};
if changed {
has_changed = Ok(());
}
match certainty {
Certainty::Yes => {}
Certainty::Maybe(_) => {
new_goals.goals.push(nested_goal);
has_changed = has_changed.map_err(|c| c.unify_and(certainty));
}
}
}
core::mem::swap(&mut new_goals, &mut goals);
match has_changed {
Ok(()) => None,
Err(certainty) => Some(Ok(certainty)),
}
},
);
self.nested_goals = goals;
response
}
}
impl<'tcx> EvalCtxt<'_, 'tcx> {
pub(super) fn probe<T>(&mut self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T {
self.infcx.probe(|_| f(self))
let mut ecx = EvalCtxt {
infcx: self.infcx,
var_values: self.var_values,
max_input_universe: self.max_input_universe,
search_graph: self.search_graph,
nested_goals: self.nested_goals.clone(),
};
self.infcx.probe(|_| f(&mut ecx))
}
pub(super) fn tcx(&self) -> TyCtxt<'tcx> {
@ -61,6 +355,15 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
)
}
/// Returns a ty infer or a const infer depending on whether `kind` is a `Ty` or `Const`.
/// If `kind` is an integer inference variable this will still return a ty infer var.
pub(super) fn next_term_infer_of_kind(&self, kind: ty::Term<'tcx>) -> ty::Term<'tcx> {
match kind.unpack() {
ty::TermKind::Ty(_) => self.next_ty_infer().into(),
ty::TermKind::Const(ct) => self.next_const_infer(ct.ty()).into(),
}
}
/// Is the projection predicate is of the form `exists<T> <Ty as Trait>::Assoc = T`.
///
/// This is the case if the `term` is an inference variable in the innermost universe
@ -137,6 +440,30 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
#[instrument(level = "debug", skip(self, param_env), ret)]
pub(super) fn eq<T: ToTrace<'tcx>>(
&mut self,
param_env: ty::ParamEnv<'tcx>,
lhs: T,
rhs: T,
) -> Result<(), NoSolution> {
self.infcx
.at(&ObligationCause::dummy(), param_env)
.eq(DefineOpaqueTypes::No, lhs, rhs)
.map(|InferOk { value: (), obligations }| {
self.add_goals(obligations.into_iter().map(|o| o.into()));
})
.map_err(|e| {
debug!(?e, "failed to equate");
NoSolution
})
}
/// Equates two values returning the nested goals without adding them
/// to the nested goals of the `EvalCtxt`.
///
/// If possible, try using `eq` instead which automatically handles nested
/// goals correctly.
#[instrument(level = "debug", skip(self, param_env), ret)]
pub(super) fn eq_and_get_goals<T: ToTrace<'tcx>>(
&self,
param_env: ty::ParamEnv<'tcx>,
lhs: T,

View file

@ -15,23 +15,19 @@
// FIXME: uses of `infcx.at` need to enable deferred projection equality once that's implemented.
use std::mem;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt};
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
use rustc_infer::traits::query::NoSolution;
use rustc_middle::traits::solve::{
CanonicalGoal, CanonicalResponse, Certainty, ExternalConstraints, ExternalConstraintsData,
Goal, MaybeCause, QueryResult, Response,
Goal, QueryResult, Response,
};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
};
use rustc_span::DUMMY_SP;
use crate::solve::search_graph::OverflowHandler;
use crate::traits::ObligationCause;
mod assembly;
@ -42,7 +38,7 @@ mod project_goals;
mod search_graph;
mod trait_goals;
pub use eval_ctxt::EvalCtxt;
pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt};
pub use fulfill::FulfillmentCtxt;
trait CanonicalResponseExt {
@ -57,180 +53,18 @@ impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> {
}
}
pub trait InferCtxtEvalExt<'tcx> {
/// Evaluates a goal from **outside** of the trait solver.
///
/// Using this while inside of the solver is wrong as it uses a new
/// search graph which would break cycle detection.
fn evaluate_root_goal(
&self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
) -> Result<(bool, Certainty), NoSolution>;
}
impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
fn evaluate_root_goal(
&self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
) -> Result<(bool, Certainty), NoSolution> {
let mut search_graph = search_graph::SearchGraph::new(self.tcx);
let result = EvalCtxt {
search_graph: &mut search_graph,
infcx: self,
// Only relevant when canonicalizing the response.
max_input_universe: ty::UniverseIndex::ROOT,
var_values: CanonicalVarValues::dummy(),
in_projection_eq_hack: false,
}
.evaluate_goal(goal);
assert!(search_graph.is_empty());
result
}
}
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
/// The entry point of the solver.
///
/// This function deals with (coinductive) cycles, overflow, and caching
/// and then calls [`EvalCtxt::compute_goal`] which contains the actual
/// logic of the solver.
///
/// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal]
/// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're
/// outside of it.
#[instrument(level = "debug", skip(tcx, search_graph), ret)]
fn evaluate_canonical_goal(
tcx: TyCtxt<'tcx>,
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
canonical_goal: CanonicalGoal<'tcx>,
) -> QueryResult<'tcx> {
// Deal with overflow, caching, and coinduction.
//
// The actual solver logic happens in `ecx.compute_goal`.
search_graph.with_new_goal(tcx, canonical_goal, |search_graph| {
let (ref infcx, goal, var_values) =
tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
let mut ecx = EvalCtxt {
infcx,
var_values,
max_input_universe: canonical_goal.max_universe,
search_graph,
in_projection_eq_hack: false,
};
ecx.compute_goal(goal)
})
}
/// Recursively evaluates `goal`, returning whether any inference vars have
/// been constrained and the certainty of the result.
fn evaluate_goal(
&mut self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
) -> Result<(bool, Certainty), NoSolution> {
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
let canonical_response =
EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
let has_changed = !canonical_response.value.var_values.is_identity();
let certainty = self.instantiate_and_apply_query_response(
goal.param_env,
orig_values,
canonical_response,
)?;
// Check that rerunning this query with its inference constraints applied
// doesn't result in new inference constraints and has the same result.
//
// If we have projection goals like `<T as Trait>::Assoc == u32` we recursively
// call `exists<U> <T as Trait>::Assoc == U` to enable better caching. This goal
// could constrain `U` to `u32` which would cause this check to result in a
// solver cycle.
if cfg!(debug_assertions)
&& has_changed
&& !self.in_projection_eq_hack
&& !self.search_graph.in_cycle()
&& false
{
let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
let canonical_response =
EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
if !canonical_response.value.var_values.is_identity() {
bug!("unstable result: {goal:?} {canonical_goal:?} {canonical_response:?}");
}
assert_eq!(certainty, canonical_response.value.certainty);
}
Ok((has_changed, certainty))
}
fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
let Goal { param_env, predicate } = goal;
let kind = predicate.kind();
if let Some(kind) = kind.no_bound_vars() {
match kind {
ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
self.compute_trait_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
self.compute_projection_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => {
self.compute_type_outlives_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
self.compute_region_outlives_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) })
}
ty::PredicateKind::Subtype(predicate) => {
self.compute_subtype_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Coerce(predicate) => {
self.compute_coerce_goal(Goal { param_env, predicate })
}
ty::PredicateKind::ClosureKind(def_id, substs, kind) => self
.compute_closure_kind_goal(Goal {
param_env,
predicate: (def_id, substs, kind),
}),
ty::PredicateKind::ObjectSafe(trait_def_id) => {
self.compute_object_safe_goal(trait_def_id)
}
ty::PredicateKind::WellFormed(arg) => {
self.compute_well_formed_goal(Goal { param_env, predicate: arg })
}
ty::PredicateKind::Ambiguous => self.make_canonical_response(Certainty::AMBIGUOUS),
// FIXME: implement these predicates :)
ty::PredicateKind::ConstEvaluatable(_) | ty::PredicateKind::ConstEquate(_, _) => {
self.make_canonical_response(Certainty::Yes)
}
ty::PredicateKind::TypeWellFormedFromEnv(..) => {
bug!("TypeWellFormedFromEnv is only used for Chalk")
}
ty::PredicateKind::AliasEq(lhs, rhs) => {
self.compute_alias_eq_goal(Goal { param_env, predicate: (lhs, rhs) })
}
}
} else {
let kind = self.infcx.instantiate_binder_with_placeholders(kind);
let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
let (_, certainty) = self.evaluate_goal(goal)?;
self.make_canonical_response(certainty)
}
}
#[instrument(level = "debug", skip(self))]
fn compute_type_outlives_goal(
&mut self,
goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>,
) -> QueryResult<'tcx> {
let ty::OutlivesPredicate(ty, lt) = goal.predicate;
self.infcx.register_region_obligation_with_cause(ty, lt, &ObligationCause::dummy());
self.make_canonical_response(Certainty::Yes)
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
#[instrument(level = "debug", skip(self))]
fn compute_region_outlives_goal(
&mut self,
goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>,
@ -239,9 +73,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
&ObligationCause::dummy(),
ty::Binder::dummy(goal.predicate),
);
self.make_canonical_response(Certainty::Yes)
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
#[instrument(level = "debug", skip(self))]
fn compute_coerce_goal(
&mut self,
goal: Goal<'tcx, CoercePredicate<'tcx>>,
@ -256,6 +91,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
})
}
#[instrument(level = "debug", skip(self))]
fn compute_subtype_goal(
&mut self,
goal: Goal<'tcx, SubtypePredicate<'tcx>>,
@ -263,18 +99,18 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() {
// FIXME: Do we want to register a subtype relation between these vars?
// That won't actually reflect in the query response, so it seems moot.
self.make_canonical_response(Certainty::AMBIGUOUS)
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
} else {
let InferOk { value: (), obligations } = self
.infcx
.at(&ObligationCause::dummy(), goal.param_env)
.sub(DefineOpaqueTypes::No, goal.predicate.a, goal.predicate.b)?;
self.evaluate_all_and_make_canonical_response(
obligations.into_iter().map(|pred| pred.into()).collect(),
)
self.add_goals(obligations.into_iter().map(|pred| pred.into()));
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
#[instrument(level = "debug", skip(self))]
fn compute_closure_kind_goal(
&mut self,
goal: Goal<'tcx, (DefId, ty::SubstsRef<'tcx>, ty::ClosureKind)>,
@ -283,23 +119,25 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
let found_kind = substs.as_closure().kind_ty().to_opt_closure_kind();
let Some(found_kind) = found_kind else {
return self.make_canonical_response(Certainty::AMBIGUOUS);
return self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
};
if found_kind.extends(expected_kind) {
self.make_canonical_response(Certainty::Yes)
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
Err(NoSolution)
}
}
#[instrument(level = "debug", skip(self))]
fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> {
if self.tcx().check_is_object_safe(trait_def_id) {
self.make_canonical_response(Certainty::Yes)
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
Err(NoSolution)
}
}
#[instrument(level = "debug", skip(self))]
fn compute_well_formed_goal(
&mut self,
goal: Goal<'tcx, ty::GenericArg<'tcx>>,
@ -309,10 +147,11 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
goal.param_env,
goal.predicate,
) {
Some(obligations) => self.evaluate_all_and_make_canonical_response(
obligations.into_iter().map(|o| o.into()).collect(),
),
None => self.make_canonical_response(Certainty::AMBIGUOUS),
Some(obligations) => {
self.add_goals(obligations.into_iter().map(|o| o.into()));
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS),
}
}
@ -326,14 +165,14 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
let evaluate_normalizes_to = |ecx: &mut EvalCtxt<'_, 'tcx>, alias, other| {
debug!("evaluate_normalizes_to(alias={:?}, other={:?})", alias, other);
let r = ecx.probe(|ecx| {
let (_, certainty) = ecx.evaluate_goal(goal.with(
ecx.add_goal(goal.with(
tcx,
ty::Binder::dummy(ty::ProjectionPredicate {
projection_ty: alias,
term: other,
}),
))?;
ecx.make_canonical_response(certainty)
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
});
debug!("evaluate_normalizes_to(..) -> {:?}", r);
r
@ -360,10 +199,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
// Evaluate all 3 potential candidates for the alias' being equal
candidates.push(evaluate_normalizes_to(self, alias_lhs, goal.predicate.1));
candidates.push(evaluate_normalizes_to(self, alias_rhs, goal.predicate.0));
candidates.push(self.probe(|this| {
candidates.push(self.probe(|ecx| {
debug!("compute_alias_eq_goal: alias defids are equal, equating substs");
let nested_goals = this.eq(goal.param_env, alias_lhs, alias_rhs)?;
this.evaluate_all_and_make_canonical_response(nested_goals)
ecx.eq(goal.param_env, alias_lhs, alias_rhs)?;
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}));
debug!(?candidates);
@ -379,62 +218,31 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
goal: Goal<'tcx, (ty::Const<'tcx>, Ty<'tcx>)>,
) -> QueryResult<'tcx> {
let (ct, ty) = goal.predicate;
let nested_goals = self.eq(goal.param_env, ct.ty(), ty)?;
self.evaluate_all_and_make_canonical_response(nested_goals)
self.eq(goal.param_env, ct.ty(), ty)?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
impl<'tcx> EvalCtxt<'_, 'tcx> {
// Recursively evaluates a list of goals to completion, returning the certainty
// of all of the goals.
fn evaluate_all(
&mut self,
mut goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
) -> Result<Certainty, NoSolution> {
let mut new_goals = Vec::new();
self.repeat_while_none(
|_| Ok(Certainty::Maybe(MaybeCause::Overflow)),
|this| {
let mut has_changed = Err(Certainty::Yes);
for goal in goals.drain(..) {
let (changed, certainty) = match this.evaluate_goal(goal) {
Ok(result) => result,
Err(NoSolution) => return Some(Err(NoSolution)),
};
if changed {
has_changed = Ok(());
#[instrument(level = "debug", skip(self))]
fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>) {
assert!(
self.nested_goals.normalizes_to_hack_goal.is_none(),
"attempted to set the projection eq hack goal when one already exists"
);
self.nested_goals.normalizes_to_hack_goal = Some(goal);
}
match certainty {
Certainty::Yes => {}
Certainty::Maybe(_) => {
new_goals.push(goal);
has_changed = has_changed.map_err(|c| c.unify_and(certainty));
}
}
#[instrument(level = "debug", skip(self))]
fn add_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
self.nested_goals.goals.push(goal);
}
match has_changed {
Ok(()) => {
mem::swap(&mut new_goals, &mut goals);
None
}
Err(certainty) => Some(Ok(certainty)),
}
},
)
}
// Recursively evaluates a list of goals to completion, making a query response.
//
// This is just a convenient way of calling [`EvalCtxt::evaluate_all`],
// then [`EvalCtxt::make_canonical_response`].
fn evaluate_all_and_make_canonical_response(
&mut self,
goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
) -> QueryResult<'tcx> {
self.evaluate_all(goals).and_then(|certainty| self.make_canonical_response(certainty))
#[instrument(level = "debug", skip(self, goals))]
fn add_goals(&mut self, goals: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>) {
let current_len = self.nested_goals.goals.len();
self.nested_goals.goals.extend(goals);
debug!("added_goals={:?}", &self.nested_goals.goals[current_len..]);
}
fn try_merge_responses(
@ -466,7 +274,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
});
// FIXME(-Ztrait-solver=next): We should take the intersection of the constraints on all the
// responses and use that for the constraints of this ambiguous response.
let response = self.make_canonical_response(certainty);
let response = self.evaluate_added_goals_and_make_canonical_response(certainty);
if let Ok(response) = &response {
assert!(response.has_no_inference_or_external_constraints());
}

View file

@ -20,6 +20,7 @@ use rustc_span::{sym, DUMMY_SP};
use std::iter;
impl<'tcx> EvalCtxt<'_, 'tcx> {
#[instrument(level = "debug", skip(self), ret)]
pub(super) fn compute_projection_goal(
&mut self,
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
@ -36,54 +37,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
self.merge_candidates_and_discard_reservation_impls(candidates)
} else {
let predicate = goal.predicate;
let unconstrained_rhs = match predicate.term.unpack() {
ty::TermKind::Ty(_) => self.next_ty_infer().into(),
ty::TermKind::Const(ct) => self.next_const_infer(ct.ty()).into(),
};
let unconstrained_predicate = ty::Clause::Projection(ProjectionPredicate {
let unconstrained_rhs = self.next_term_infer_of_kind(predicate.term);
let unconstrained_predicate = ProjectionPredicate {
projection_ty: goal.predicate.projection_ty,
term: unconstrained_rhs,
});
let (_has_changed, normalize_certainty) = self.in_projection_eq_hack(|this| {
this.evaluate_goal(goal.with(this.tcx(), unconstrained_predicate))
})?;
};
let nested_eq_goals = self.eq(goal.param_env, unconstrained_rhs, predicate.term)?;
let eval_certainty = self.evaluate_all(nested_eq_goals)?;
self.make_canonical_response(normalize_certainty.unify_and(eval_certainty))
self.set_normalizes_to_hack_goal(goal.with(self.tcx(), unconstrained_predicate));
self.try_evaluate_added_goals()?;
self.eq(goal.param_env, unconstrained_rhs, predicate.term)?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
/// This sets a flag used by a debug assert in [`EvalCtxt::evaluate_goal`],
/// see the comment in that method for more details.
fn in_projection_eq_hack<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
self.in_projection_eq_hack = true;
let result = f(self);
self.in_projection_eq_hack = false;
result
}
/// After normalizing the projection to `normalized_alias` with the given
/// `normalization_certainty`, constrain the inference variable `term` to it
/// and return a query response.
fn eq_term_and_make_canonical_response(
&mut self,
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
normalization_certainty: Certainty,
normalized_alias: impl Into<ty::Term<'tcx>>,
) -> QueryResult<'tcx> {
// The term of our goal should be fully unconstrained, so this should never fail.
//
// It can however be ambiguous when the `normalized_alias` contains a projection.
let nested_goals = self
.eq(goal.param_env, goal.predicate.term, normalized_alias.into())
.expect("failed to unify with unconstrained term");
let unify_certainty =
self.evaluate_all(nested_goals).expect("failed to unify with unconstrained term");
self.make_canonical_response(normalization_certainty.unify_and(unify_certainty))
}
}
impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
@ -111,19 +76,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
ecx.probe(|ecx| {
let assumption_projection_pred =
ecx.instantiate_binder_with_infer(poly_projection_pred);
let mut nested_goals = ecx.eq(
ecx.eq(
goal.param_env,
goal.predicate.projection_ty,
assumption_projection_pred.projection_ty,
)?;
nested_goals.extend(requirements);
let subst_certainty = ecx.evaluate_all(nested_goals)?;
ecx.eq_term_and_make_canonical_response(
goal,
subst_certainty,
assumption_projection_pred.term,
)
ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)?;
ecx.add_goals(requirements);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
} else {
Err(NoSolution)
@ -139,21 +99,22 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
&& poly_projection_pred.projection_def_id() == goal.predicate.def_id()
{
ecx.probe(|ecx| {
let tcx = ecx.tcx();
let assumption_projection_pred =
ecx.instantiate_binder_with_infer(poly_projection_pred);
let mut nested_goals = ecx.eq(
ecx.eq(
goal.param_env,
goal.predicate.projection_ty,
assumption_projection_pred.projection_ty,
)?;
let tcx = ecx.tcx();
let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
bug!("expected object type in `consider_object_bound_candidate`");
};
nested_goals.extend(
ecx.add_goals(
structural_traits::predicates_for_object_candidate(
ecx,
&ecx,
goal.param_env,
goal.predicate.projection_ty.trait_ref(tcx),
bounds,
@ -161,14 +122,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
.into_iter()
.map(|pred| goal.with(tcx, pred)),
);
let subst_certainty = ecx.evaluate_all(nested_goals)?;
ecx.eq_term_and_make_canonical_response(
goal,
subst_certainty,
assumption_projection_pred.term,
)
ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)?;
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
} else {
Err(NoSolution)
@ -195,16 +150,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
let mut nested_goals = ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
let where_clause_bounds = tcx
.predicates_of(impl_def_id)
.instantiate(tcx, impl_substs)
.predicates
.into_iter()
.map(|pred| goal.with(tcx, pred));
nested_goals.extend(where_clause_bounds);
let match_impl_certainty = ecx.evaluate_all(nested_goals)?;
ecx.add_goals(where_clause_bounds);
// In case the associated item is hidden due to specialization, we have to
// return ambiguity this would otherwise be incomplete, resulting in
@ -216,7 +170,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
goal.predicate.def_id(),
impl_def_id
)? else {
return ecx.make_canonical_response(match_impl_certainty.unify_and(Certainty::AMBIGUOUS));
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
};
if !assoc_def.item.defaultness(tcx).has_value() {
@ -263,7 +217,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
ty.map_bound(|ty| ty.into())
};
ecx.eq_term_and_make_canonical_response(goal, match_impl_certainty, term.subst(tcx, substs))
ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))?;
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
@ -308,13 +263,17 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
goal_kind: ty::ClosureKind,
) -> QueryResult<'tcx> {
let tcx = ecx.tcx();
let Some(tupled_inputs_and_output) =
structural_traits::extract_tupled_inputs_and_output_from_callable(
let tupled_inputs_and_output =
match structural_traits::extract_tupled_inputs_and_output_from_callable(
tcx,
goal.predicate.self_ty(),
goal_kind,
)? else {
return ecx.make_canonical_response(Certainty::AMBIGUOUS);
)? {
Some(tupled_inputs_and_output) => tupled_inputs_and_output,
None => {
return ecx
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
}
};
let output_is_sized_pred = tupled_inputs_and_output
.map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output]));
@ -380,13 +339,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
[ty::GenericArg::from(goal.predicate.self_ty())],
));
let (_, is_sized_certainty) =
ecx.evaluate_goal(goal.with(tcx, sized_predicate))?;
return ecx.eq_term_and_make_canonical_response(
goal,
is_sized_certainty,
tcx.types.unit,
);
ecx.add_goal(goal.with(tcx, sized_predicate));
ecx.eq(goal.param_env, goal.predicate.term, tcx.types.unit.into())?;
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
}
ty::Adt(def, substs) if def.is_struct() => {
@ -394,12 +349,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
None => tcx.types.unit,
Some(field_def) => {
let self_ty = field_def.ty(tcx, substs);
let new_goal = goal.with(
ecx.add_goal(goal.with(
tcx,
ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)),
);
let (_, certainty) = ecx.evaluate_goal(new_goal)?;
return ecx.make_canonical_response(certainty);
));
return ecx
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
}
}
}
@ -408,12 +363,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
ty::Tuple(elements) => match elements.last() {
None => tcx.types.unit,
Some(&self_ty) => {
let new_goal = goal.with(
ecx.add_goal(goal.with(
tcx,
ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)),
);
let (_, certainty) = ecx.evaluate_goal(new_goal)?;
return ecx.make_canonical_response(certainty);
));
return ecx
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
}
},
@ -426,7 +381,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
),
};
ecx.eq_term_and_make_canonical_response(goal, Certainty::Yes, metadata_ty)
ecx.eq(goal.param_env, goal.predicate.term, metadata_ty.into())?;
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
@ -522,7 +478,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
let discriminant = goal.predicate.self_ty().discriminant_ty(ecx.tcx());
ecx.probe(|ecx| ecx.eq_term_and_make_canonical_response(goal, Certainty::Yes, discriminant))
ecx.probe(|ecx| {
ecx.eq(goal.param_env, goal.predicate.term, discriminant.into())?;
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
}

View file

@ -39,9 +39,7 @@ impl<'tcx> SearchGraph<'tcx> {
}
pub(super) fn is_empty(&self) -> bool {
self.stack.is_empty()
&& self.provisional_cache.is_empty()
&& !self.overflow_data.did_overflow()
self.stack.is_empty() && self.provisional_cache.is_empty()
}
/// Whether we're currently in a cycle. This should only be used

View file

@ -47,7 +47,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
let mut nested_goals =
ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
let where_clause_bounds = tcx
.predicates_of(impl_def_id)
@ -55,8 +54,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
.predicates
.into_iter()
.map(|pred| goal.with(tcx, pred));
nested_goals.extend(where_clause_bounds);
ecx.evaluate_all_and_make_canonical_response(nested_goals)
ecx.add_goals(where_clause_bounds);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
@ -73,13 +72,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx.probe(|ecx| {
let assumption_trait_pred =
ecx.instantiate_binder_with_infer(poly_trait_pred);
let mut nested_goals = ecx.eq(
ecx.eq(
goal.param_env,
goal.predicate.trait_ref,
assumption_trait_pred.trait_ref,
)?;
nested_goals.extend(requirements);
ecx.evaluate_all_and_make_canonical_response(nested_goals)
ecx.add_goals(requirements);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
} else {
Err(NoSolution)
@ -98,7 +97,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx.probe(|ecx| {
let assumption_trait_pred =
ecx.instantiate_binder_with_infer(poly_trait_pred);
let mut nested_goals = ecx.eq(
ecx.eq(
goal.param_env,
goal.predicate.trait_ref,
assumption_trait_pred.trait_ref,
@ -108,9 +107,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
bug!("expected object type in `consider_object_bound_candidate`");
};
nested_goals.extend(
ecx.add_goals(
structural_traits::predicates_for_object_candidate(
ecx,
&ecx,
goal.param_env,
goal.predicate.trait_ref,
bounds,
@ -118,8 +117,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
.into_iter()
.map(|pred| goal.with(tcx, pred)),
);
ecx.evaluate_all_and_make_canonical_response(nested_goals)
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
} else {
Err(NoSolution)
@ -166,9 +164,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
let nested_obligations = tcx
.predicates_of(goal.predicate.def_id())
.instantiate(tcx, goal.predicate.trait_ref.substs);
ecx.evaluate_all_and_make_canonical_response(
nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)).collect(),
)
ecx.add_goals(nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
@ -197,7 +194,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
if goal.predicate.self_ty().has_non_region_infer() {
return ecx.make_canonical_response(Certainty::AMBIGUOUS);
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
}
let tcx = ecx.tcx();
@ -209,7 +206,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
&& layout.layout.align().abi == usize_layout.align().abi
{
// FIXME: We could make this faster by making a no-constraints response
ecx.make_canonical_response(Certainty::Yes)
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
Err(NoSolution)
}
@ -221,13 +218,17 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
goal_kind: ty::ClosureKind,
) -> QueryResult<'tcx> {
let tcx = ecx.tcx();
let Some(tupled_inputs_and_output) =
structural_traits::extract_tupled_inputs_and_output_from_callable(
let tupled_inputs_and_output =
match structural_traits::extract_tupled_inputs_and_output_from_callable(
tcx,
goal.predicate.self_ty(),
goal_kind,
)? else {
return ecx.make_canonical_response(Certainty::AMBIGUOUS);
)? {
Some(a) => a,
None => {
return ecx
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
}
};
let output_is_sized_pred = tupled_inputs_and_output
.map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output]));
@ -247,7 +248,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
if let ty::Tuple(..) = goal.predicate.self_ty().kind() {
ecx.make_canonical_response(Certainty::Yes)
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
Err(NoSolution)
}
@ -257,7 +258,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx: &mut EvalCtxt<'_, 'tcx>,
_goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
ecx.make_canonical_response(Certainty::Yes)
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
fn consider_builtin_future_candidate(
@ -277,7 +278,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
// Async generator unconditionally implement `Future`
// Technically, we need to check that the future output type is Sized,
// but that's already proven by the generator being WF.
ecx.make_canonical_response(Certainty::Yes)
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
fn consider_builtin_generator_candidate(
@ -317,7 +318,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
let a_ty = goal.predicate.self_ty();
let b_ty = goal.predicate.trait_ref.substs.type_at(1);
if b_ty.is_ty_var() {
return ecx.make_canonical_response(Certainty::AMBIGUOUS);
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
}
ecx.probe(|ecx| {
match (a_ty.kind(), b_ty.kind()) {
@ -326,7 +327,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
// Dyn upcasting is handled separately, since due to upcasting,
// when there are two supertraits that differ by substs, we
// may return more than one query response.
return Err(NoSolution);
Err(NoSolution)
}
// `T` -> `dyn Trait` unsizing
(_, &ty::Dynamic(data, region, ty::Dyn)) => {
@ -341,29 +342,26 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
let Some(sized_def_id) = tcx.lang_items().sized_trait() else {
return Err(NoSolution);
};
let nested_goals: Vec<_> = data
.iter()
// Check that the type implements all of the predicates of the def-id.
// (i.e. the principal, all of the associated types match, and any auto traits)
.map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty)))
.chain([
ecx.add_goals(
data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
);
// The type must be Sized to be unsized.
goal.with(
tcx,
ty::Binder::dummy(tcx.mk_trait_ref(sized_def_id, [a_ty])),
),
ecx.add_goal(
goal.with(tcx, ty::Binder::dummy(tcx.mk_trait_ref(sized_def_id, [a_ty]))),
);
// The type must outlive the lifetime of the `dyn` we're unsizing into.
ecx.add_goal(
goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))),
])
.collect();
ecx.evaluate_all_and_make_canonical_response(nested_goals)
);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// `[T; n]` -> `[T]` unsizing
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
// We just require that the element type stays the same
let nested_goals = ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
ecx.evaluate_all_and_make_canonical_response(nested_goals)
ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
(&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs))
@ -397,15 +395,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
// Finally, we require that `TailA: Unsize<TailB>` for the tail field
// types.
let mut nested_goals = ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
nested_goals.push(goal.with(
ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
ecx.add_goal(goal.with(
tcx,
ty::Binder::dummy(
tcx.mk_trait_ref(goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
),
));
ecx.evaluate_all_and_make_canonical_response(nested_goals)
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
(&ty::Tuple(a_tys), &ty::Tuple(b_tys))
@ -417,17 +414,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
// Substitute just the tail field of B., and require that they're equal.
let unsized_a_ty =
tcx.mk_tup_from_iter(a_rest_tys.iter().chain([b_last_ty]).copied());
let mut nested_goals = ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
// Similar to ADTs, require that the rest of the fields are equal.
nested_goals.push(goal.with(
ecx.add_goal(goal.with(
tcx,
ty::Binder::dummy(
tcx.mk_trait_ref(goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
),
));
ecx.evaluate_all_and_make_canonical_response(nested_goals)
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
_ => Err(NoSolution),
}
@ -477,12 +473,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
let new_a_ty = tcx.mk_dynamic(new_a_data, b_region, ty::Dyn);
// We also require that A's lifetime outlives B's lifetime.
let mut nested_obligations = ecx.eq(goal.param_env, new_a_ty, b_ty)?;
nested_obligations.push(
ecx.eq(goal.param_env, new_a_ty, b_ty)?;
ecx.add_goal(
goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))),
);
ecx.evaluate_all_and_make_canonical_response(nested_obligations)
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
};
@ -516,7 +511,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
_goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
// `DiscriminantKind` is automatically implemented for every type.
ecx.make_canonical_response(Certainty::Yes)
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
@ -530,21 +525,23 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
goal: Goal<'tcx, TraitPredicate<'tcx>>,
constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result<Vec<Ty<'tcx>>, NoSolution>,
) -> QueryResult<'tcx> {
self.probe(|this| {
this.evaluate_all_and_make_canonical_response(
constituent_tys(this, goal.predicate.self_ty())?
self.probe(|ecx| {
ecx.add_goals(
constituent_tys(ecx, goal.predicate.self_ty())?
.into_iter()
.map(|ty| {
goal.with(
this.tcx(),
ty::Binder::dummy(goal.predicate.with_self_ty(this.tcx(), ty)),
ecx.tcx(),
ty::Binder::dummy(goal.predicate.with_self_ty(ecx.tcx(), ty)),
)
})
.collect(),
)
.collect::<Vec<_>>(),
);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
#[instrument(level = "debug", skip(self))]
pub(super) fn compute_trait_goal(
&mut self,
goal: Goal<'tcx, TraitPredicate<'tcx>>,

View file

@ -333,7 +333,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'_, 'tcx> {
// FIXME: Technically this folder could be fallible?
let nested = self
.ecx
.eq(self.param_env, alias_ty, proj.projection_ty)
.eq_and_get_goals(self.param_env, alias_ty, proj.projection_ty)
.expect("expected to be able to unify goal projection with dyn's projection");
// FIXME: Technically we could register these too..
assert!(nested.is_empty(), "did not expect unification to have any nested goals");

View file

@ -17,10 +17,10 @@ use rustc_errors::{DelayDm, FatalError, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, InternalSubsts};
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::{
self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
};
use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
use rustc_span::symbol::Symbol;
use rustc_span::Span;
@ -139,6 +139,10 @@ fn object_safety_violations_for_trait(
if !spans.is_empty() {
violations.push(ObjectSafetyViolation::SupertraitSelf(spans));
}
let spans = super_predicates_have_non_lifetime_binders(tcx, trait_def_id);
if !spans.is_empty() {
violations.push(ObjectSafetyViolation::SupertraitNonLifetimeBinder(spans));
}
violations.extend(
tcx.associated_items(trait_def_id)
@ -348,6 +352,21 @@ fn predicate_references_self<'tcx>(
}
}
fn super_predicates_have_non_lifetime_binders(
tcx: TyCtxt<'_>,
trait_def_id: DefId,
) -> SmallVec<[Span; 1]> {
// If non_lifetime_binders is disabled, then exit early
if !tcx.features().non_lifetime_binders {
return SmallVec::new();
}
tcx.super_predicates_of(trait_def_id)
.predicates
.iter()
.filter_map(|(pred, span)| pred.has_non_region_late_bound().then_some(*span))
.collect()
}
fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
generics_require_sized_self(tcx, trait_def_id)
}

View file

@ -7,7 +7,10 @@ use rustc_middle::ty::{
TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
};
use rustc_session::config::TraitSolver;
use rustc_span::def_id::{DefId, CRATE_DEF_ID};
use rustc_span::{
def_id::{DefId, CRATE_DEF_ID},
DUMMY_SP,
};
use rustc_trait_selection::traits;
fn sized_constraint_for_ty<'tcx>(
@ -275,16 +278,22 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
}
fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
if let ty::Alias(ty::Projection, alias_ty) = *ty.kind()
&& self.tcx.is_impl_trait_in_trait(alias_ty.def_id)
&& self.tcx.impl_trait_in_trait_parent_fn(alias_ty.def_id) == self.fn_def_id
&& self.seen.insert(alias_ty.def_id)
if let ty::Alias(ty::Projection, unshifted_alias_ty) = *ty.kind()
&& self.tcx.is_impl_trait_in_trait(unshifted_alias_ty.def_id)
&& self.tcx.impl_trait_in_trait_parent_fn(unshifted_alias_ty.def_id) == self.fn_def_id
&& self.seen.insert(unshifted_alias_ty.def_id)
{
// We have entered some binders as we've walked into the
// bounds of the RPITIT. Shift these binders back out when
// constructing the top-level projection predicate.
let alias_ty = self.tcx.fold_regions(alias_ty, |re, _| {
let shifted_alias_ty = self.tcx.fold_regions(unshifted_alias_ty, |re, depth| {
if let ty::ReLateBound(index, bv) = re.kind() {
if depth != ty::INNERMOST {
return self.tcx.mk_re_error_with_message(
DUMMY_SP,
"we shouldn't walk non-predicate binders with `impl Trait`...",
);
}
self.tcx.mk_re_late_bound(index.shifted_out_to_binder(self.depth), bv)
} else {
re
@ -295,26 +304,27 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
// the `type_of` of the trait's associated item. If we're using the old lowering
// strategy, then just reinterpret the associated type like an opaque :^)
let default_ty = if self.tcx.lower_impl_trait_in_trait_to_assoc_ty() {
self
.tcx
.type_of(alias_ty.def_id)
.subst(self.tcx, alias_ty.substs)
self.tcx.type_of(shifted_alias_ty.def_id).subst(self.tcx, shifted_alias_ty.substs)
} else {
self.tcx.mk_alias(ty::Opaque, alias_ty)
self.tcx.mk_alias(ty::Opaque, shifted_alias_ty)
};
self.predicates.push(
ty::Binder::bind_with_vars(
ty::ProjectionPredicate {
projection_ty: alias_ty,
term: default_ty.into(),
},
ty::ProjectionPredicate { projection_ty: shifted_alias_ty, term: default_ty.into() },
self.bound_vars,
)
.to_predicate(self.tcx),
);
for bound in self.tcx.item_bounds(alias_ty.def_id).subst_iter(self.tcx, alias_ty.substs)
// We walk the *un-shifted* alias ty, because we're tracking the de bruijn
// binder depth, and if we were to walk `shifted_alias_ty` instead, we'd
// have to reset `self.depth` back to `ty::INNERMOST` or something. It's
// easier to just do this.
for bound in self
.tcx
.item_bounds(unshifted_alias_ty.def_id)
.subst_iter(self.tcx, unshifted_alias_ty.substs)
{
bound.visit_with(self);
}

View file

@ -806,3 +806,9 @@ changelog-seen = 2
#
# This list must be non-empty.
#compression-formats = ["gz", "xz"]
# How much time should be spent compressing the tarballs. The better the
# compression profile, the longer compression will take.
#
# Available options: fast, balanced, best
#compression-profile = "fast"

View file

@ -164,12 +164,13 @@ where
/// element is encountered:
///
/// ```
/// let f = |&x: &i32| if x < 0 { Err("Negative element found") } else { Ok(x) };
/// let v = vec![1, 2];
/// let res: Result<i32, &'static str> = v.iter().map(|&x: &i32|
/// if x < 0 { Err("Negative element found") }
/// else { Ok(x) }
/// ).sum();
/// let res: Result<i32, _> = v.iter().map(f).sum();
/// assert_eq!(res, Ok(3));
/// let v = vec![1, -2];
/// let res: Result<i32, _> = v.iter().map(f).sum();
/// assert_eq!(res, Err("Negative element found"));
/// ```
fn sum<I>(iter: I) -> Result<T, E>
where
@ -187,6 +188,20 @@ where
/// Takes each element in the [`Iterator`]: if it is an [`Err`], no further
/// elements are taken, and the [`Err`] is returned. Should no [`Err`]
/// occur, the product of all elements is returned.
///
/// # Examples
///
/// This multiplies each number in a vector of strings,
/// if a string could not be parsed the operation returns `Err`:
///
/// ```
/// let nums = vec!["5", "10", "1", "2"];
/// let total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();
/// assert_eq!(total, Ok(100));
/// let nums = vec!["5", "10", "one", "2"];
/// let total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();
/// assert!(total.is_err());
/// ```
fn product<I>(iter: I) -> Result<T, E>
where
I: Iterator<Item = Result<U, E>>,
@ -213,6 +228,9 @@ where
/// let words = vec!["have", "a", "great", "day"];
/// let total: Option<usize> = words.iter().map(|w| w.find('a')).sum();
/// assert_eq!(total, Some(5));
/// let words = vec!["have", "a", "good", "day"];
/// let total: Option<usize> = words.iter().map(|w| w.find('a')).sum();
/// assert_eq!(total, None);
/// ```
fn sum<I>(iter: I) -> Option<T>
where
@ -230,6 +248,20 @@ where
/// Takes each element in the [`Iterator`]: if it is a [`None`], no further
/// elements are taken, and the [`None`] is returned. Should no [`None`]
/// occur, the product of all elements is returned.
///
/// # Examples
///
/// This multiplies each number in a vector of strings,
/// if a string could not be parsed the operation returns `None`:
///
/// ```
/// let nums = vec!["5", "10", "1", "2"];
/// let total: Option<usize> = nums.iter().map(|w| w.parse::<usize>().ok()).product();
/// assert_eq!(total, Some(100));
/// let nums = vec!["5", "10", "one", "2"];
/// let total: Option<usize> = nums.iter().map(|w| w.parse::<usize>().ok()).product();
/// assert_eq!(total, None);
/// ```
fn product<I>(iter: I) -> Option<T>
where
I: Iterator<Item = Option<U>>,

View file

@ -3448,6 +3448,9 @@ pub trait Iterator {
///
/// An empty iterator returns the zero value of the type.
///
/// `sum()` can be used to sum any type implementing [`Sum`][`core::iter::Sum`],
/// including [`Option`][`Option::sum`] and [`Result`][`Result::sum`].
///
/// # Panics
///
/// When calling `sum()` and a primitive integer type is being returned, this
@ -3478,6 +3481,9 @@ pub trait Iterator {
///
/// An empty iterator returns the one value of the type.
///
/// `product()` can be used to multiply any type implementing [`Product`][`core::iter::Product`],
/// including [`Option`][`Option::product`] and [`Result`][`Result::product`].
///
/// # Panics
///
/// When calling `product()` and a primitive integer type is being returned,

View file

@ -2,7 +2,8 @@ use crate::io::prelude::*;
use crate::env;
use crate::fs::{self, File, OpenOptions};
use crate::io::{ErrorKind, SeekFrom};
use crate::io::{BorrowedBuf, ErrorKind, SeekFrom};
use crate::mem::MaybeUninit;
use crate::path::Path;
use crate::str;
use crate::sync::Arc;
@ -401,6 +402,23 @@ fn file_test_io_seek_read_write() {
check!(fs::remove_file(&filename));
}
#[test]
fn file_test_read_buf() {
let tmpdir = tmpdir();
let filename = &tmpdir.join("test");
check!(fs::write(filename, &[1, 2, 3, 4]));
let mut buf: [MaybeUninit<u8>; 128] = MaybeUninit::uninit_array();
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
let mut file = check!(File::open(filename));
check!(file.read_buf(buf.unfilled()));
assert_eq!(buf.filled(), &[1, 2, 3, 4]);
// File::read_buf should omit buffer initialization.
assert_eq!(buf.init_len(), 4);
check!(fs::remove_file(filename));
}
#[test]
fn file_test_stat_is_correct_on_is_file() {
let tmpdir = tmpdir();

View file

@ -8,7 +8,7 @@ use crate::io::prelude::*;
use crate::cell::{Cell, RefCell};
use crate::fmt;
use crate::fs::File;
use crate::io::{self, BufReader, IoSlice, IoSliceMut, LineWriter, Lines};
use crate::io::{self, BorrowedCursor, BufReader, IoSlice, IoSliceMut, LineWriter, Lines};
use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sync::{Arc, Mutex, MutexGuard, OnceLock, ReentrantMutex, ReentrantMutexGuard};
use crate::sys::stdio;
@ -97,6 +97,10 @@ impl Read for StdinRaw {
handle_ebadf(self.0.read(buf), 0)
}
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
handle_ebadf(self.0.read_buf(buf), ())
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
handle_ebadf(self.0.read_vectored(bufs), 0)
}
@ -418,6 +422,9 @@ impl Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.lock().read(buf)
}
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
self.lock().read_buf(buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.lock().read_vectored(bufs)
}
@ -450,6 +457,10 @@ impl Read for StdinLock<'_> {
self.inner.read(buf)
}
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
self.inner.read_buf(buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.read_vectored(bufs)
}

View file

@ -6,7 +6,7 @@ mod tests;
use crate::io::prelude::*;
use crate::fmt;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::iter::FusedIterator;
use crate::net::{Shutdown, SocketAddr, ToSocketAddrs};
use crate::sys_common::net as net_imp;
@ -619,6 +619,10 @@ impl Read for TcpStream {
self.0.read(buf)
}
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
self.0.read_buf(buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0.read_vectored(bufs)
}
@ -653,6 +657,10 @@ impl Read for &TcpStream {
self.0.read(buf)
}
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
self.0.read_buf(buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0.read_vectored(bufs)
}

View file

@ -1,6 +1,7 @@
use crate::fmt;
use crate::io::prelude::*;
use crate::io::{ErrorKind, IoSlice, IoSliceMut};
use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut};
use crate::mem::MaybeUninit;
use crate::net::test::{next_test_ip4, next_test_ip6};
use crate::net::*;
use crate::sync::mpsc::channel;
@ -279,6 +280,31 @@ fn partial_read() {
})
}
#[test]
fn read_buf() {
each_ip(&mut |addr| {
let srv = t!(TcpListener::bind(&addr));
let t = thread::spawn(move || {
let mut s = t!(TcpStream::connect(&addr));
s.write_all(&[1, 2, 3, 4]).unwrap();
});
let mut s = t!(srv.accept()).0;
let mut buf: [MaybeUninit<u8>; 128] = MaybeUninit::uninit_array();
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
t!(s.read_buf(buf.unfilled()));
assert_eq!(buf.filled(), &[1, 2, 3, 4]);
// FIXME: sgx uses default_read_buf that initializes the buffer.
if cfg!(not(target_env = "sgx")) {
// TcpStream::read_buf should omit buffer initialization.
assert_eq!(buf.init_len(), 4);
}
t.join().ok().expect("thread panicked");
})
}
#[test]
fn read_vectored() {
each_ip(&mut |addr| {

View file

@ -110,7 +110,7 @@ use crate::convert::Infallible;
use crate::ffi::OsStr;
use crate::fmt;
use crate::fs;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::num::NonZeroI32;
use crate::path::Path;
use crate::str;
@ -354,6 +354,10 @@ impl Read for ChildStdout {
self.inner.read(buf)
}
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
self.inner.read_buf(buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.read_vectored(bufs)
}
@ -419,6 +423,10 @@ impl Read for ChildStderr {
self.inner.read(buf)
}
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
self.inner.read_buf(buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.read_vectored(bufs)
}

View file

@ -1,7 +1,8 @@
use crate::io::prelude::*;
use super::{Command, Output, Stdio};
use crate::io::ErrorKind;
use crate::io::{BorrowedBuf, ErrorKind};
use crate::mem::MaybeUninit;
use crate::str;
fn known_command() -> Command {
@ -119,6 +120,37 @@ fn stdin_works() {
assert_eq!(out, "foobar\n");
}
#[test]
#[cfg_attr(any(target_os = "vxworks"), ignore)]
fn child_stdout_read_buf() {
let mut cmd = if cfg!(target_os = "windows") {
let mut cmd = Command::new("cmd");
cmd.arg("/C").arg("echo abc");
cmd
} else {
let mut cmd = shell_cmd();
cmd.arg("-c").arg("echo abc");
cmd
};
cmd.stdin(Stdio::null());
cmd.stdout(Stdio::piped());
let child = cmd.spawn().unwrap();
let mut stdout = child.stdout.unwrap();
let mut buf: [MaybeUninit<u8>; 128] = MaybeUninit::uninit_array();
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
stdout.read_buf(buf.unfilled()).unwrap();
// ChildStdout::read_buf should omit buffer initialization.
if cfg!(target_os = "windows") {
assert_eq!(buf.filled(), b"abc\r\n");
assert_eq!(buf.init_len(), 5);
} else {
assert_eq!(buf.filled(), b"abc\n");
assert_eq!(buf.init_len(), 4);
};
}
#[test]
#[cfg_attr(any(target_os = "vxworks"), ignore)]
fn test_process_status() {

View file

@ -1,7 +1,7 @@
use fortanix_sgx_abi::Fd;
use super::abi::usercalls;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::mem;
use crate::sys::{AsInner, FromInner, IntoInner};
@ -30,6 +30,10 @@ impl FileDesc {
usercalls::read(self.fd, &mut [IoSliceMut::new(buf)])
}
pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
crate::io::default_read_buf(|b| self.read(b), buf)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
usercalls::read(self.fd, bufs)
}

View file

@ -1,6 +1,6 @@
use crate::error;
use crate::fmt;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs};
use crate::sync::Arc;
use crate::sys::fd::FileDesc;
@ -144,6 +144,10 @@ impl TcpStream {
self.inner.inner.read(buf)
}
pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
self.inner.inner.read_buf(buf)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.inner.read_vectored(bufs)
}

View file

@ -469,6 +469,15 @@ impl<'a> Read for &'a FileDesc {
fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
(**self).read_buf(cursor)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
(**self).read_vectored(bufs)
}
#[inline]
fn is_read_vectored(&self) -> bool {
(**self).is_read_vectored()
}
}
impl AsInner<OwnedFd> for FileDesc {

View file

@ -1,6 +1,6 @@
use crate::cmp;
use crate::ffi::CStr;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut};
use crate::mem;
use crate::net::{Shutdown, SocketAddr};
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
@ -242,19 +242,35 @@ impl Socket {
self.0.duplicate().map(Socket)
}
fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> {
let ret = cvt(unsafe {
libc::recv(self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags)
libc::recv(
self.as_raw_fd(),
buf.as_mut().as_mut_ptr() as *mut c_void,
buf.capacity(),
flags,
)
})?;
Ok(ret as usize)
unsafe {
buf.advance(ret as usize);
}
Ok(())
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.recv_with_flags(buf, 0)
let mut buf = BorrowedBuf::from(buf);
self.recv_with_flags(buf.unfilled(), 0)?;
Ok(buf.len())
}
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
self.recv_with_flags(buf, MSG_PEEK)
let mut buf = BorrowedBuf::from(buf);
self.recv_with_flags(buf.unfilled(), MSG_PEEK)?;
Ok(buf.len())
}
pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
self.recv_with_flags(buf, 0)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {

View file

@ -1,4 +1,4 @@
use crate::io::{self, IoSlice, IoSliceMut};
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::mem;
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::sys::fd::FileDesc;
@ -49,6 +49,10 @@ impl AnonPipe {
self.0.read(buf)
}
pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
self.0.read_buf(buf)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0.read_vectored(bufs)
}

View file

@ -1,4 +1,4 @@
use crate::io::{self, IoSlice, IoSliceMut};
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::mem::ManuallyDrop;
use crate::os::unix::io::FromRawFd;
use crate::sys::fd::FileDesc;
@ -18,6 +18,10 @@ impl io::Read for Stdin {
unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read(buf) }
}
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_buf(buf) }
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_vectored(bufs) }
}

View file

@ -1,5 +1,5 @@
use crate::fmt;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use crate::sys::unsupported;
use crate::time::Duration;
@ -39,6 +39,10 @@ impl TcpStream {
self.0
}
pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> {
self.0
}
pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0
}

View file

@ -1,4 +1,4 @@
use crate::io::{self, IoSlice, IoSliceMut};
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
pub struct AnonPipe(!);
@ -7,6 +7,10 @@ impl AnonPipe {
self.0
}
pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> {
self.0
}
pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0
}

View file

@ -2,7 +2,7 @@
#![allow(dead_code)]
use super::err2io;
use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
use crate::mem;
use crate::net::Shutdown;
use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
@ -46,6 +46,22 @@ impl WasiFd {
unsafe { wasi::fd_read(self.as_raw_fd() as wasi::Fd, iovec(bufs)).map_err(err2io) }
}
pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
unsafe {
let bufs = [wasi::Iovec {
buf: buf.as_mut().as_mut_ptr() as *mut u8,
buf_len: buf.capacity(),
}];
match wasi::fd_read(self.as_raw_fd() as wasi::Fd, &bufs) {
Ok(n) => {
buf.advance(n);
Ok(())
}
Err(e) => Err(err2io(e)),
}
}
}
pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
unsafe { wasi::fd_write(self.as_raw_fd() as wasi::Fd, ciovec(bufs)).map_err(err2io) }
}

View file

@ -441,7 +441,7 @@ impl File {
}
pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
crate::io::default_read_buf(|buf| self.read(buf), cursor)
self.fd.read_buf(cursor)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {

View file

@ -3,7 +3,7 @@
use super::err2io;
use super::fd::WasiFd;
use crate::fmt;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::sys::unsupported;
@ -91,6 +91,10 @@ impl TcpStream {
self.read_vectored(&mut [IoSliceMut::new(buf)])
}
pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
self.socket().as_inner().read_buf(buf)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.socket().as_inner().read(bufs)
}

View file

@ -327,7 +327,16 @@ impl<'a> Read for &'a Handle {
(**self).read(buf)
}
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
(**self).read_buf(buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
(**self).read_vectored(bufs)
}
#[inline]
fn is_read_vectored(&self) -> bool {
(**self).is_read_vectored()
}
}

View file

@ -1,7 +1,7 @@
#![unstable(issue = "none", feature = "windows_net")]
use crate::cmp;
use crate::io::{self, IoSlice, IoSliceMut, Read};
use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut, Read};
use crate::mem;
use crate::net::{Shutdown, SocketAddr};
use crate::os::windows::io::{
@ -214,28 +214,38 @@ impl Socket {
Ok(Self(self.0.try_clone()?))
}
fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> {
// On unix when a socket is shut down all further reads return 0, so we
// do the same on windows to map a shut down socket to returning EOF.
let length = cmp::min(buf.len(), i32::MAX as usize) as i32;
let result =
unsafe { c::recv(self.as_raw_socket(), buf.as_mut_ptr() as *mut _, length, flags) };
let length = cmp::min(buf.capacity(), i32::MAX as usize) as i32;
let result = unsafe {
c::recv(self.as_raw_socket(), buf.as_mut().as_mut_ptr() as *mut _, length, flags)
};
match result {
c::SOCKET_ERROR => {
let error = unsafe { c::WSAGetLastError() };
if error == c::WSAESHUTDOWN {
Ok(0)
Ok(())
} else {
Err(io::Error::from_raw_os_error(error))
}
}
_ => Ok(result as usize),
_ => {
unsafe { buf.advance(result as usize) };
Ok(())
}
}
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let mut buf = BorrowedBuf::from(buf);
self.recv_with_flags(buf.unfilled(), 0)?;
Ok(buf.len())
}
pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
self.recv_with_flags(buf, 0)
}
@ -277,7 +287,9 @@ impl Socket {
}
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
self.recv_with_flags(buf, c::MSG_PEEK)
let mut buf = BorrowedBuf::from(buf);
self.recv_with_flags(buf.unfilled(), c::MSG_PEEK)?;
Ok(buf.len())
}
fn recv_from_with_flags(

View file

@ -1,7 +1,7 @@
use crate::os::windows::prelude::*;
use crate::ffi::OsStr;
use crate::io::{self, IoSlice, IoSliceMut, Read};
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read};
use crate::mem;
use crate::path::Path;
use crate::ptr;
@ -252,6 +252,28 @@ impl AnonPipe {
}
}
pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
let result = unsafe {
let len = crate::cmp::min(buf.capacity(), c::DWORD::MAX as usize) as c::DWORD;
self.alertable_io_internal(c::ReadFileEx, buf.as_mut().as_mut_ptr() as _, len)
};
match result {
// The special treatment of BrokenPipe is to deal with Windows
// pipe semantics, which yields this error when *reading* from
// a pipe after the other end has closed; we interpret that as
// EOF on the pipe.
Err(ref e) if e.kind() == io::ErrorKind::BrokenPipe => Ok(()),
Err(e) => Err(e),
Ok(n) => {
unsafe {
buf.advance(n);
}
Ok(())
}
}
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.read_vectored(bufs)
}

View file

@ -4,7 +4,7 @@ mod tests;
use crate::cmp;
use crate::convert::{TryFrom, TryInto};
use crate::fmt;
use crate::io::{self, ErrorKind, IoSlice, IoSliceMut};
use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut};
use crate::mem;
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use crate::ptr;
@ -272,6 +272,10 @@ impl TcpStream {
self.inner.read(buf)
}
pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
self.inner.read_buf(buf)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.read_vectored(bufs)
}

View file

@ -191,6 +191,7 @@ pub struct Config {
pub dist_sign_folder: Option<PathBuf>,
pub dist_upload_addr: Option<String>,
pub dist_compression_formats: Option<Vec<String>>,
pub dist_compression_profile: String,
pub dist_include_mingw_linker: bool,
// libstd features
@ -703,6 +704,7 @@ define_config! {
src_tarball: Option<bool> = "src-tarball",
missing_tools: Option<bool> = "missing-tools",
compression_formats: Option<Vec<String>> = "compression-formats",
compression_profile: Option<String> = "compression-profile",
include_mingw_linker: Option<bool> = "include-mingw-linker",
}
}
@ -821,6 +823,7 @@ impl Config {
config.deny_warnings = true;
config.bindir = "bin".into();
config.dist_include_mingw_linker = true;
config.dist_compression_profile = "fast".into();
// set by build.rs
config.build = TargetSelection::from_user(&env!("BUILD_TRIPLE"));
@ -1308,6 +1311,7 @@ impl Config {
config.dist_sign_folder = t.sign_folder.map(PathBuf::from);
config.dist_upload_addr = t.upload_addr;
config.dist_compression_formats = t.compression_formats;
set(&mut config.dist_compression_profile, t.compression_profile);
set(&mut config.rust_dist_src, t.src_tarball);
set(&mut config.missing_tools, t.missing_tools);
set(&mut config.dist_include_mingw_linker, t.include_mingw_linker)

View file

@ -11,3 +11,7 @@ extended = true
[llvm]
# Most users installing from source want to build all parts of the project from source, not just rustc itself.
download-ci-llvm = false
[dist]
# Use better compression when preparing tarballs.
compression-profile = "balanced"

View file

@ -318,6 +318,7 @@ impl<'a> Tarball<'a> {
assert!(!formats.is_empty(), "dist.compression-formats can't be empty");
cmd.arg("--compression-formats").arg(formats.join(","));
}
cmd.args(&["--compression-profile", &self.builder.config.dist_compression_profile]);
self.builder.run(&mut cmd);
// Ensure there are no symbolic links in the tarball. In particular,

View file

@ -58,6 +58,7 @@ RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-manage-submodules"
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-locked-deps"
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-cargo-native-static"
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.codegen-units-std=1"
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set dist.compression-profile=best"
# Only produce xz tarballs on CI. gz tarballs will be generated by the release
# process by recompressing the existing xz ones. This decreases the storage

View file

@ -1,7 +1,7 @@
use super::Scripter;
use super::Tarballer;
use crate::{
compression::{CompressionFormat, CompressionFormats},
compression::{CompressionFormat, CompressionFormats, CompressionProfile},
util::*,
};
use anyhow::{bail, Context, Result};
@ -48,6 +48,10 @@ actor! {
#[clap(value_name = "DIR")]
output_dir: String = "./dist",
/// The profile used to compress the tarball.
#[clap(value_name = "FORMAT", default_value_t)]
compression_profile: CompressionProfile,
/// The formats used to compress the tarball
#[clap(value_name = "FORMAT", default_value_t)]
compression_formats: CompressionFormats,
@ -153,6 +157,7 @@ impl Combiner {
.work_dir(self.work_dir)
.input(self.package_name)
.output(path_to_str(&output)?.into())
.compression_profile(self.compression_profile)
.compression_formats(self.compression_formats.clone());
tarballer.run()?;

View file

@ -4,6 +4,37 @@ use rayon::prelude::*;
use std::{convert::TryFrom, fmt, io::Read, io::Write, path::Path, str::FromStr};
use xz2::{read::XzDecoder, write::XzEncoder};
#[derive(Default, Debug, Copy, Clone)]
pub enum CompressionProfile {
Fast,
#[default]
Balanced,
Best,
}
impl FromStr for CompressionProfile {
type Err = Error;
fn from_str(input: &str) -> Result<Self, Error> {
Ok(match input {
"fast" => Self::Fast,
"balanced" => Self::Balanced,
"best" => Self::Best,
other => anyhow::bail!("invalid compression profile: {other}"),
})
}
}
impl fmt::Display for CompressionProfile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CompressionProfile::Fast => f.write_str("fast"),
CompressionProfile::Balanced => f.write_str("balanced"),
CompressionProfile::Best => f.write_str("best"),
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum CompressionFormat {
Gz,
@ -26,7 +57,11 @@ impl CompressionFormat {
}
}
pub(crate) fn encode(&self, path: impl AsRef<Path>) -> Result<Box<dyn Encoder>, Error> {
pub(crate) fn encode(
&self,
path: impl AsRef<Path>,
profile: CompressionProfile,
) -> Result<Box<dyn Encoder>, Error> {
let mut os = path.as_ref().as_os_str().to_os_string();
os.push(format!(".{}", self.extension()));
let path = Path::new(&os);
@ -37,8 +72,23 @@ impl CompressionFormat {
let file = crate::util::create_new_file(path)?;
Ok(match self {
CompressionFormat::Gz => Box::new(GzEncoder::new(file, flate2::Compression::best())),
CompressionFormat::Gz => Box::new(GzEncoder::new(
file,
match profile {
CompressionProfile::Fast => flate2::Compression::fast(),
CompressionProfile::Balanced => flate2::Compression::new(6),
CompressionProfile::Best => flate2::Compression::best(),
},
)),
CompressionFormat::Xz => {
let encoder = match profile {
CompressionProfile::Fast => {
xz2::stream::MtStreamBuilder::new().threads(6).preset(1).encoder().unwrap()
}
CompressionProfile::Balanced => {
xz2::stream::MtStreamBuilder::new().threads(6).preset(6).encoder().unwrap()
}
CompressionProfile::Best => {
let mut filters = xz2::stream::Filters::new();
// the preset is overridden by the other options so it doesn't matter
let mut lzma_ops = xz2::stream::LzmaOptions::new_preset(9).unwrap();
@ -75,11 +125,11 @@ impl CompressionFormat {
} else {
builder.threads(6);
}
builder.encoder().unwrap()
}
};
let compressor = XzEncoder::new_stream(
std::io::BufWriter::new(file),
builder.encoder().unwrap(),
);
let compressor = XzEncoder::new_stream(std::io::BufWriter::new(file), encoder);
Box::new(compressor)
}
})

View file

@ -1,6 +1,6 @@
use super::Scripter;
use super::Tarballer;
use crate::compression::CompressionFormats;
use crate::compression::{CompressionFormats, CompressionProfile};
use crate::util::*;
use anyhow::{bail, format_err, Context, Result};
use std::collections::BTreeSet;
@ -54,6 +54,10 @@ actor! {
#[clap(value_name = "DIR")]
output_dir: String = "./dist",
/// The profile used to compress the tarball.
#[clap(value_name = "FORMAT", default_value_t)]
compression_profile: CompressionProfile,
/// The formats used to compress the tarball
#[clap(value_name = "FORMAT", default_value_t)]
compression_formats: CompressionFormats,
@ -113,6 +117,7 @@ impl Generator {
.work_dir(self.work_dir)
.input(self.package_name)
.output(path_to_str(&output)?.into())
.compression_profile(self.compression_profile)
.compression_formats(self.compression_formats.clone());
tarballer.run()?;

View file

@ -6,7 +6,7 @@ use tar::{Builder, Header};
use walkdir::WalkDir;
use crate::{
compression::{CombinedEncoder, CompressionFormats},
compression::{CombinedEncoder, CompressionFormats, CompressionProfile},
util::*,
};
@ -25,6 +25,10 @@ actor! {
#[clap(value_name = "DIR")]
work_dir: String = "./workdir",
/// The profile used to compress the tarball.
#[clap(value_name = "FORMAT", default_value_t)]
compression_profile: CompressionProfile,
/// The formats used to compress the tarball.
#[clap(value_name = "FORMAT", default_value_t)]
compression_formats: CompressionFormats,
@ -38,7 +42,7 @@ impl Tarballer {
let encoder = CombinedEncoder::new(
self.compression_formats
.iter()
.map(|f| f.encode(&tarball_name))
.map(|f| f.encode(&tarball_name, self.compression_profile))
.collect::<Result<Vec<_>>>()?,
);

View file

@ -0,0 +1,16 @@
// MIR for `adt` after built
fn adt() -> Onion {
let mut _0: Onion; // return place in scope 0 at $DIR/aggregate_exprs.rs:+0:13: +0:18
let mut _1: i32; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
let mut _2: Foo; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
let mut _3: Bar; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
bb0: {
_1 = const 1_i32; // scope 0 at $DIR/aggregate_exprs.rs:+6:13: +6:20
_2 = Foo { a: const 1_i32, b: const 2_i32 }; // scope 0 at $DIR/aggregate_exprs.rs:+7:13: +10:14
_3 = Bar::Foo(move _2, _1); // scope 0 at $DIR/aggregate_exprs.rs:+11:13: +11:39
_0 = Onion { neon: ((_3 as variant#0).1: i32) }; // scope 0 at $DIR/aggregate_exprs.rs:+12:13: +12:58
return; // scope 0 at $DIR/aggregate_exprs.rs:+13:13: +13:21
}
}

View file

@ -0,0 +1,15 @@
// MIR for `array` after built
fn array() -> [i32; 2] {
let mut _0: [i32; 2]; // return place in scope 0 at $DIR/aggregate_exprs.rs:+0:15: +0:23
let mut _1: [i32; 2]; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
let mut _2: i32; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
bb0: {
_1 = [const 42_i32, const 43_i32]; // scope 0 at $DIR/aggregate_exprs.rs:+5:13: +5:25
_2 = const 1_i32; // scope 0 at $DIR/aggregate_exprs.rs:+6:13: +6:20
_1 = [_2, const 2_i32]; // scope 0 at $DIR/aggregate_exprs.rs:+7:13: +7:25
_0 = move _1; // scope 0 at $DIR/aggregate_exprs.rs:+8:13: +8:26
return; // scope 0 at $DIR/aggregate_exprs.rs:+9:13: +9:21
}
}

View file

@ -0,0 +1,71 @@
#![feature(custom_mir, core_intrinsics)]
extern crate core;
use core::intrinsics::mir::*;
// EMIT_MIR aggregate_exprs.tuple.built.after.mir
#[custom_mir(dialect = "built")]
fn tuple() -> (i32, bool) {
mir!(
{
RET = (1, true);
Return()
}
)
}
// EMIT_MIR aggregate_exprs.array.built.after.mir
#[custom_mir(dialect = "built")]
fn array() -> [i32; 2] {
mir!(
let x: [i32; 2];
let one: i32;
{
x = [42, 43];
one = 1;
x = [one, 2];
RET = Move(x);
Return()
}
)
}
struct Foo {
a: i32,
b: i32,
}
enum Bar {
Foo(Foo, i32),
}
union Onion {
neon: i32,
noun: f32,
}
// EMIT_MIR aggregate_exprs.adt.built.after.mir
#[custom_mir(dialect = "built")]
fn adt() -> Onion {
mir!(
let one: i32;
let x: Foo;
let y: Bar;
{
one = 1;
x = Foo {
a: 1,
b: 2,
};
y = Bar::Foo(Move(x), one);
RET = Onion { neon: Field(Variant(y, 0), 1) };
Return()
}
)
}
fn main() {
assert_eq!(tuple(), (1, true));
assert_eq!(array(), [1, 2]);
assert_eq!(unsafe { adt().neon }, 1);
}

View file

@ -0,0 +1,10 @@
// MIR for `tuple` after built
fn tuple() -> (i32, bool) {
let mut _0: (i32, bool); // return place in scope 0 at $DIR/aggregate_exprs.rs:+0:15: +0:26
bb0: {
_0 = (const 1_i32, const true); // scope 0 at $DIR/aggregate_exprs.rs:+3:13: +3:28
return; // scope 0 at $DIR/aggregate_exprs.rs:+4:13: +4:21
}
}

View file

@ -13,4 +13,10 @@ trait Trait {
fn method(&self) -> impl Trait<Type = impl Sized + '_>;
}
trait Trait2 {
type Type;
fn method(&self) -> impl Trait2<Type = impl Trait2<Type = impl Sized + '_> + '_>;
}
fn main() {}

View file

@ -0,0 +1,24 @@
#![feature(non_lifetime_binders)]
//~^ WARN the feature `non_lifetime_binders` is incomplete
trait Foo: for<T> Bar<T> {}
trait Bar<T: ?Sized> {
fn method(&self) {}
}
fn needs_bar(x: &(impl Bar<i32> + ?Sized)) {
x.method();
}
impl Foo for () {}
impl<T: ?Sized> Bar<T> for () {}
fn main() {
let x: &dyn Foo = &();
//~^ ERROR the trait `Foo` cannot be made into an object
//~| ERROR the trait `Foo` cannot be made into an object
needs_bar(x);
//~^ ERROR the trait `Foo` cannot be made into an object
}

View file

@ -0,0 +1,56 @@
warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/supertrait-object-safety.rs:1:12
|
LL | #![feature(non_lifetime_binders)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
= note: `#[warn(incomplete_features)]` on by default
error[E0038]: the trait `Foo` cannot be made into an object
--> $DIR/supertrait-object-safety.rs:19:23
|
LL | let x: &dyn Foo = &();
| ^^^ `Foo` cannot be made into an object
|
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> $DIR/supertrait-object-safety.rs:4:12
|
LL | trait Foo: for<T> Bar<T> {}
| --- ^^^^^^^^^^^^^ ...because where clause cannot reference non-lifetime `for<...>` variables
| |
| this trait cannot be made into an object...
= note: required for `&()` to implement `CoerceUnsized<&dyn Foo>`
= note: required by cast to type `&dyn Foo`
error[E0038]: the trait `Foo` cannot be made into an object
--> $DIR/supertrait-object-safety.rs:19:12
|
LL | let x: &dyn Foo = &();
| ^^^^^^^^ `Foo` cannot be made into an object
|
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> $DIR/supertrait-object-safety.rs:4:12
|
LL | trait Foo: for<T> Bar<T> {}
| --- ^^^^^^^^^^^^^ ...because where clause cannot reference non-lifetime `for<...>` variables
| |
| this trait cannot be made into an object...
error[E0038]: the trait `Foo` cannot be made into an object
--> $DIR/supertrait-object-safety.rs:22:5
|
LL | needs_bar(x);
| ^^^^^^^^^ `Foo` cannot be made into an object
|
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> $DIR/supertrait-object-safety.rs:4:12
|
LL | trait Foo: for<T> Bar<T> {}
| --- ^^^^^^^^^^^^^ ...because where clause cannot reference non-lifetime `for<...>` variables
| |
| this trait cannot be made into an object...
error: aborting due to 3 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0038`.