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
37 changes: 29 additions & 8 deletions compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use rustc_ast::InlineAsmOptions;
use rustc_middle::mir::*;
use rustc_middle::span_bug;
use rustc_middle::ty::{self, TyCtxt, layout};
use rustc_span::sym;
use rustc_target::spec::PanicStrategy;

/// A pass that runs which is targeted at ensuring that codegen guarantees about
Expand Down Expand Up @@ -33,6 +34,19 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls {
return;
}

// Represent whether this compilation target fundamentally doesn't
// support unwinding at all at an ABI level. If this the target has no
// support for unwinding then cleanup actions, for example, are all
// unnecessary and can be considered unreachable.
//
// Currently this is only true for wasm targets on panic=abort when the
// `exception-handling` target feature is disabled. In such a
// configuration it's illegal to emit exception-related instructions so
// it's not possible to unwind.
let target_supports_unwinding = !(tcx.sess.target.is_like_wasm
&& tcx.sess.panic_strategy() == PanicStrategy::Abort
&& !tcx.asm_target_features(def_id).contains(&sym::exception_handling));

// Here we test for this function itself whether its ABI allows
// unwinding or not.
let body_ty = tcx.type_of(def_id).skip_binder();
Expand All @@ -54,12 +68,18 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls {
let Some(terminator) = &mut block.terminator else { continue };
let span = terminator.source_info.span;

// If we see an `UnwindResume` terminator inside a function that cannot unwind, we need
// to replace it with `UnwindTerminate`.
if let TerminatorKind::UnwindResume = &terminator.kind
&& !body_can_unwind
{
terminator.kind = TerminatorKind::UnwindTerminate(UnwindTerminateReason::Abi);
// If we see an `UnwindResume` terminator inside a function then:
//
// * If the target doesn't support unwinding at all, then this is an
// unreachable block.
// * If the body cannot unwind, we need to replace it with
// `UnwindTerminate`.
if let TerminatorKind::UnwindResume = &terminator.kind {
if !target_supports_unwinding {
terminator.kind = TerminatorKind::Unreachable;
} else if !body_can_unwind {
terminator.kind = TerminatorKind::UnwindTerminate(UnwindTerminateReason::Abi);
}
}

if block.is_cleanup {
Expand Down Expand Up @@ -93,8 +113,9 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls {
_ => continue,
};

if !call_can_unwind {
// If this function call can't unwind, then there's no need for it
if !call_can_unwind || !target_supports_unwinding {
// If this function call can't unwind, or if the target doesn't
// support unwinding at all, then there's no need for it
// to have a landing pad. This means that we can remove any cleanup
// registered for it (and turn it into `UnwindAction::Unreachable`).
let cleanup = block.terminator_mut().unwind_mut().unwrap();
Expand Down
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
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,7 @@ symbols! {
ermsb_target_feature,
exact_div,
except,
exception_handling: "exception-handling",
exchange_malloc,
exclusive_range_pattern,
exhaustive_integer_patterns,
Expand Down
Loading
Loading