Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ fn coroutine_closure_to_ambiguous_coroutine<I: Interner>(
pub(in crate::solve) fn extract_fn_def_from_const_callable<I: Interner>(
cx: I,
self_ty: I::Ty,
) -> Result<(ty::Binder<I, (I::FnInputTys, I::Ty)>, I::FunctionId, I::GenericArgs), NoSolution> {
) -> Result<(ty::Binder<I, (I::Ty, I::Ty)>, I::FunctionId, I::GenericArgs), NoSolution> {
match self_ty.kind() {
ty::FnDef(def_id, args) => {
let sig = cx.fn_sig(def_id);
Expand All @@ -673,7 +673,8 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable<I: Interner>(
&& cx.fn_is_const(def_id)
{
Ok((
sig.instantiate(cx, args).map_bound(|sig| (sig.inputs(), sig.output())),
sig.instantiate(cx, args)
.map_bound(|sig| (Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())),
def_id,
args,
))
Expand Down
21 changes: 9 additions & 12 deletions compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,12 +234,12 @@ where
let self_ty = goal.predicate.self_ty();
let (inputs_and_output, def_id, args) =
structural_traits::extract_fn_def_from_const_callable(cx, self_ty)?;
let (inputs, output) = ecx.instantiate_binder_with_infer(inputs_and_output);

// A built-in `Fn` impl only holds if the output is sized.
// (FIXME: technically we only need to check this if the type is a fn ptr...)
let output_is_sized_pred = inputs_and_output.map_bound(|(_, output)| {
ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output])
});
let output_is_sized_pred =
ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]);
let requirements = cx
.const_conditions(def_id.into())
.iter_instantiated(cx, args)
Expand All @@ -251,15 +251,12 @@ where
})
.chain([(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))]);

let pred = inputs_and_output
.map_bound(|(inputs, _)| {
ty::TraitRef::new(
cx,
goal.predicate.def_id(),
[goal.predicate.self_ty(), Ty::new_tup(cx, inputs.as_slice())],
)
})
.to_host_effect_clause(cx, goal.predicate.constness);
let pred = ty::Binder::dummy(ty::TraitRef::new(
cx,
goal.predicate.def_id(),
[goal.predicate.self_ty(), inputs],
))
.to_host_effect_clause(cx, goal.predicate.constness);

Self::probe_and_consider_implied_clause(
ecx,
Expand Down
19 changes: 5 additions & 14 deletions compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -633,28 +633,19 @@ where
// the certainty of all the goals.
#[instrument(level = "trace", skip(self))]
pub(super) fn try_evaluate_added_goals(&mut self) -> Result<Certainty, NoSolution> {
let mut response = Ok(Certainty::overflow(false));
for _ in 0..FIXPOINT_STEP_LIMIT {
// FIXME: This match is a bit ugly, it might be nice to change the inspect
// stuff to use a closure instead. which should hopefully simplify this a bit.
match self.evaluate_added_goals_step() {
Ok(Some(cert)) => {
response = Ok(cert);
break;
}
Ok(None) => {}
Ok(Some(cert)) => return Ok(cert),
Err(NoSolution) => {
response = Err(NoSolution);
break;
self.tainted = Err(NoSolution);
return Err(NoSolution);
}
}
}

if response.is_err() {
self.tainted = Err(NoSolution);
}

response
debug!("try_evaluate_added_goals: encountered overflow");
Ok(Certainty::overflow(false))
}

/// Iterate over all added goals: returning `Ok(Some(_))` in case we can stop rerunning.
Expand Down
135 changes: 57 additions & 78 deletions compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,23 +451,22 @@ where
return ecx.forced_ambiguity(MaybeCause::Ambiguity);
}
};
let (inputs, output) = ecx.instantiate_binder_with_infer(tupled_inputs_and_output);

// A built-in `Fn` impl only holds if the output is sized.
// (FIXME: technically we only need to check this if the type is a fn ptr...)
let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output])
});
let output_is_sized_pred =
ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]);

let pred = tupled_inputs_and_output
.map_bound(|(inputs, output)| ty::ProjectionPredicate {
projection_term: ty::AliasTerm::new(
cx,
goal.predicate.def_id(),
[goal.predicate.self_ty(), inputs],
),
term: output.into(),
})
.upcast(cx);
let pred = ty::ProjectionPredicate {
projection_term: ty::AliasTerm::new(
cx,
goal.predicate.def_id(),
[goal.predicate.self_ty(), inputs],
),
term: output.into(),
}
.upcast(cx);

Self::probe_and_consider_implied_clause(
ecx,
Expand Down Expand Up @@ -497,76 +496,56 @@ where
goal_kind,
env_region,
)?;
let AsyncCallableRelevantTypes {
tupled_inputs_ty,
output_coroutine_ty,
coroutine_return_ty,
} = ecx.instantiate_binder_with_infer(tupled_inputs_and_output_and_coroutine);

// A built-in `AsyncFn` impl only holds if the output is sized.
// (FIXME: technically we only need to check this if the type is a fn ptr...)
let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
|AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| {
ty::TraitRef::new(
cx,
cx.require_trait_lang_item(SolverTraitLangItem::Sized),
[output_ty],
)
},
let output_is_sized_pred = ty::TraitRef::new(
cx,
cx.require_trait_lang_item(SolverTraitLangItem::Sized),
[output_coroutine_ty],
);

let pred = tupled_inputs_and_output_and_coroutine
.map_bound(
|AsyncCallableRelevantTypes {
tupled_inputs_ty,
output_coroutine_ty,
coroutine_return_ty,
}| {
let (projection_term, term) = if cx
.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallOnceFuture)
{
(
ty::AliasTerm::new(
cx,
goal.predicate.def_id(),
[goal.predicate.self_ty(), tupled_inputs_ty],
),
output_coroutine_ty.into(),
)
} else if cx
.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallRefFuture)
{
(
ty::AliasTerm::new(
cx,
goal.predicate.def_id(),
[
I::GenericArg::from(goal.predicate.self_ty()),
tupled_inputs_ty.into(),
env_region.into(),
],
),
output_coroutine_ty.into(),
)
} else if cx
.is_lang_item(goal.predicate.def_id(), SolverLangItem::AsyncFnOnceOutput)
{
(
ty::AliasTerm::new(
cx,
goal.predicate.def_id(),
[
I::GenericArg::from(goal.predicate.self_ty()),
tupled_inputs_ty.into(),
],
),
coroutine_return_ty.into(),
)
} else {
panic!(
"no such associated type in `AsyncFn*`: {:?}",
goal.predicate.def_id()
)
};
ty::ProjectionPredicate { projection_term, term }
},
)
.upcast(cx);
let (projection_term, term) =
if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallOnceFuture) {
(
ty::AliasTerm::new(
cx,
goal.predicate.def_id(),
[goal.predicate.self_ty(), tupled_inputs_ty],
),
output_coroutine_ty.into(),
)
} else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallRefFuture) {
(
ty::AliasTerm::new(
cx,
goal.predicate.def_id(),
[
I::GenericArg::from(goal.predicate.self_ty()),
tupled_inputs_ty.into(),
env_region.into(),
],
),
output_coroutine_ty.into(),
)
} else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::AsyncFnOnceOutput) {
(
ty::AliasTerm::new(
cx,
goal.predicate.def_id(),
[goal.predicate.self_ty(), tupled_inputs_ty],
),
coroutine_return_ty.into(),
)
} else {
panic!("no such associated type in `AsyncFn*`: {:?}", goal.predicate.def_id())
};
let pred = ty::ProjectionPredicate { projection_term, term }.upcast(cx);

Self::probe_and_consider_implied_clause(
ecx,
Expand Down
46 changes: 21 additions & 25 deletions compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,18 +369,16 @@ where
return ecx.forced_ambiguity(MaybeCause::Ambiguity);
}
};
let (inputs, output) = ecx.instantiate_binder_with_infer(tupled_inputs_and_output);

// A built-in `Fn` impl only holds if the output is sized.
// (FIXME: technically we only need to check this if the type is a fn ptr...)
let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output])
});
let output_is_sized_pred =
ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]);

let pred = tupled_inputs_and_output
.map_bound(|(inputs, _)| {
ty::TraitRef::new(cx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
})
.upcast(cx);
let pred =
ty::TraitRef::new(cx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
.upcast(cx);
Self::probe_and_consider_implied_clause(
ecx,
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
Expand Down Expand Up @@ -408,28 +406,26 @@ where
// This region doesn't matter because we're throwing away the coroutine type
Region::new_static(cx),
)?;
let AsyncCallableRelevantTypes {
tupled_inputs_ty,
output_coroutine_ty,
coroutine_return_ty: _,
} = ecx.instantiate_binder_with_infer(tupled_inputs_and_output_and_coroutine);

// A built-in `AsyncFn` impl only holds if the output is sized.
// (FIXME: technically we only need to check this if the type is a fn ptr...)
let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
|AsyncCallableRelevantTypes { output_coroutine_ty, .. }| {
ty::TraitRef::new(
cx,
cx.require_trait_lang_item(SolverTraitLangItem::Sized),
[output_coroutine_ty],
)
},
let output_is_sized_pred = ty::TraitRef::new(
cx,
cx.require_trait_lang_item(SolverTraitLangItem::Sized),
[output_coroutine_ty],
);

let pred = tupled_inputs_and_output_and_coroutine
.map_bound(|AsyncCallableRelevantTypes { tupled_inputs_ty, .. }| {
ty::TraitRef::new(
cx,
goal.predicate.def_id(),
[goal.predicate.self_ty(), tupled_inputs_ty],
)
})
.upcast(cx);
let pred = ty::TraitRef::new(
cx,
goal.predicate.def_id(),
[goal.predicate.self_ty(), tupled_inputs_ty],
)
.upcast(cx);
Self::probe_and_consider_implied_clause(
ecx,
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//@ ignore-compare-mode-next-solver
//@ compile-flags: -Znext-solver
//@ check-pass

// Regression test for trait-system-refactor-initiative#220. Builtin `Fn`-trait
// candidates required `for<'latebound> Output<'latebound>: Sized` which ended
// up resulting in overflow if the return type is an opaque in the defining scope.
//
// We now eagerly instantiate the binder of the function definition which avoids
// that overflow by relating the lifetime of the opaque to something from the
// input.
fn flat_map<T, F, I, G>(_: F, _: G)
where
F: FnOnce(T) -> I,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

previously we had to prove for<'a> opaque<'a>: Sized here and each time we tried to prove it resulted in a new opaque<'!a_placeholderN> = ?unconstrained use of the opaque.

This then caused try_evaluate_added_goals to overflow

I: Iterator,
G: Fn(<I as Iterator>::Item) -> usize,
{
}

fn rarw<'a>(_: &'a ()) -> impl Iterator<Item = &'a str> {
flat_map(rarw, |x| x.len());
std::iter::empty()
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//@ ignore-compare-mode-next-solver
//@ compile-flags: -Znext-solver
//@ check-pass

// Regression test for trait-system-refactor-initiative#204, see
// the sibling test for more details.

fn constrain<'a, F: FnOnce(&'a ())>(_: F) {}
fn foo<'a>(_: &'a ()) -> impl Sized + use<'a> {
constrain(foo);
()
}

fn main() {}
Loading