Skip to content

Commit

Permalink
Auto merge of #113086 - lcnr:rust8, r=compiler-errors
Browse files Browse the repository at this point in the history
implement deep normalization via the new solver

together with #112869 this should remove all uses of the old solver with `-Ztrait-solver=next`.

see https://hackmd.io/V0qsUB_fTxexfQO_pcOcrg for a description of this PR. Will move that doc to the `rustc-dev-guide` after merging this.

r? `@compiler-errors`
  • Loading branch information
bors committed Jul 3, 2023
2 parents 1623634 + be6a344 commit 737b461
Show file tree
Hide file tree
Showing 63 changed files with 834 additions and 296 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4136,6 +4136,7 @@ dependencies = [
name = "rustc_ty_utils"
version = "0.0.0"
dependencies = [
"itertools",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
Expand Down
17 changes: 10 additions & 7 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@ fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) {
if check_opaque_for_cycles(tcx, item.owner_id.def_id, substs, span, &origin).is_err() {
return;
}
check_opaque_meets_bounds(tcx, item.owner_id.def_id, span, &origin);

let _ = check_opaque_meets_bounds(tcx, item.owner_id.def_id, span, &origin);
}

/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
Expand Down Expand Up @@ -395,7 +396,7 @@ fn check_opaque_meets_bounds<'tcx>(
def_id: LocalDefId,
span: Span,
origin: &hir::OpaqueTyOrigin,
) {
) -> Result<(), ErrorGuaranteed> {
let defining_use_anchor = match *origin {
hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did,
hir::OpaqueTyOrigin::TyAlias { .. } => tcx.impl_trait_parent(def_id),
Expand Down Expand Up @@ -429,10 +430,10 @@ fn check_opaque_meets_bounds<'tcx>(
Ok(()) => {}
Err(ty_err) => {
let ty_err = ty_err.to_string(tcx);
tcx.sess.delay_span_bug(
return Err(tcx.sess.delay_span_bug(
span,
format!("could not unify `{hidden_ty}` with revealed type:\n{ty_err}"),
);
));
}
}

Expand All @@ -447,7 +448,8 @@ fn check_opaque_meets_bounds<'tcx>(
// version.
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
infcx.err_ctxt().report_fulfillment_errors(&errors);
let guar = infcx.err_ctxt().report_fulfillment_errors(&errors);
return Err(guar);
}
match origin {
// Checked when type checking the function containing them.
Expand All @@ -461,14 +463,15 @@ fn check_opaque_meets_bounds<'tcx>(
if tcx.def_kind(tcx.parent(def_id.to_def_id())) == DefKind::OpaqueTy => {}
// Can have different predicates to their defining use
hir::OpaqueTyOrigin::TyAlias { .. } => {
let wf_tys = ocx.assumed_wf_types(param_env, span, def_id);
let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, def_id)?;
let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, wf_tys);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
let _ = ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env);
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
}
}
// Clean up after ourselves
let _ = infcx.take_opaque_types();
Ok(())
}

fn is_enum_of_nonnullable_ptr<'tcx>(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2121,7 +2121,7 @@ pub(super) fn check_type_bounds<'tcx>(
_ => bug!(),
}
};
let assumed_wf_types = ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty_def_id);
let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl_ty_def_id)?;

let normalize_cause = ObligationCause::new(
impl_ty_span,
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,12 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
}
f(&mut wfcx);

let assumed_wf_types = wfcx.ocx.assumed_wf_types(param_env, span, body_def_id);
let assumed_wf_types = match wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)
{
Ok(wf_types) => wf_types,
Err(_guar) => return,
};

let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, assumed_wf_types);

let errors = wfcx.select_all_or_error();
Expand Down
22 changes: 10 additions & 12 deletions compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ use rustc_infer::traits::specialization_graph::Node;
use rustc_middle::ty::subst::{GenericArg, InternalSubsts, SubstsRef};
use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
use rustc_span::Span;
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::{self, translate_substs_with_cause, wf, ObligationCtxt};
Expand Down Expand Up @@ -113,7 +113,7 @@ fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node
let span = tcx.def_span(impl1_def_id);
check_has_items(tcx, impl1_def_id, impl2_node, span);

if let Some((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) {
if let Ok((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) {
let impl2_def_id = impl2_node.def_id();
debug!(?impl2_def_id, ?impl2_substs);

Expand Down Expand Up @@ -171,16 +171,14 @@ fn get_impl_substs(
tcx: TyCtxt<'_>,
impl1_def_id: LocalDefId,
impl2_node: Node,
) -> Option<(SubstsRef<'_>, SubstsRef<'_>)> {
) -> Result<(SubstsRef<'_>, SubstsRef<'_>), ErrorGuaranteed> {
let infcx = &tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(infcx);
let param_env = tcx.param_env(impl1_def_id);

let assumed_wf_types =
ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id);
let impl1_span = tcx.def_span(impl1_def_id);
let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl1_def_id)?;

let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id);
let impl1_span = tcx.def_span(impl1_def_id);
let impl2_substs = translate_substs_with_cause(
infcx,
param_env,
Expand All @@ -198,19 +196,19 @@ fn get_impl_substs(

let errors = ocx.select_all_or_error();
if !errors.is_empty() {
ocx.infcx.err_ctxt().report_fulfillment_errors(&errors);
return None;
let guar = ocx.infcx.err_ctxt().report_fulfillment_errors(&errors);
return Err(guar);
}

let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, assumed_wf_types);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env);
let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else {
let span = tcx.def_span(impl1_def_id);
tcx.sess.emit_err(SubstsOnOverriddenImpl { span });
return None;
let guar = tcx.sess.emit_err(SubstsOnOverriddenImpl { span });
return Err(guar);
};
Some((impl1_substs, impl2_substs))
Ok((impl1_substs, impl2_substs))
}

/// Returns a list of all of the unconstrained subst of the given impl.
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_typeck/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => self.check_expr(callee_expr),
};

let expr_ty = self.structurally_resolved_type(call_expr.span, original_callee_ty);
let expr_ty = self.structurally_resolve_type(call_expr.span, original_callee_ty);

let mut autoderef = self.autoderef(callee_expr.span, expr_ty);
let mut result = None;
Expand Down Expand Up @@ -138,7 +138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
autoderef: &Autoderef<'a, 'tcx>,
) -> Option<CallStep<'tcx>> {
let adjusted_ty =
self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));

// If the callee is a bare function or a closure, then we're all set.
match *adjusted_ty.kind() {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_typeck/src/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,8 +717,8 @@ impl<'a, 'tcx> CastCheck<'tcx> {

#[instrument(skip(fcx), level = "debug")]
pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) {
self.expr_ty = fcx.structurally_resolved_type(self.expr_span, self.expr_ty);
self.cast_ty = fcx.structurally_resolved_type(self.cast_span, self.cast_ty);
self.expr_ty = fcx.structurally_resolve_type(self.expr_span, self.expr_ty);
self.cast_ty = fcx.structurally_resolve_type(self.cast_span, self.cast_ty);

debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty);

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1005,7 +1005,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
allow_two_phase: AllowTwoPhase,
cause: Option<ObligationCause<'tcx>>,
) -> RelateResult<'tcx, Ty<'tcx>> {
let source = self.resolve_vars_with_obligations(expr_ty);
let source = self.try_structurally_resolve_type(expr.span, expr_ty);
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);

let cause =
Expand Down
16 changes: 8 additions & 8 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner);

if !oprnd_t.references_error() {
oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t);
oprnd_t = self.structurally_resolve_type(expr.span, oprnd_t);
match unop {
hir::UnOp::Deref => {
if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) {
Expand Down Expand Up @@ -1266,13 +1266,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> Ty<'tcx> {
let rcvr_t = self.check_expr(&rcvr);
// no need to check for bot/err -- callee does that
let rcvr_t = self.structurally_resolved_type(rcvr.span, rcvr_t);
let rcvr_t = self.structurally_resolve_type(rcvr.span, rcvr_t);
let span = segment.ident.span;

let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr, args) {
Ok(method) => {
// We could add a "consider `foo::<params>`" suggestion here, but I wasn't able to
// trigger this codepath causing `structurally_resolved_type` to emit an error.
// trigger this codepath causing `structurally_resolve_type` to emit an error.

self.write_method_call(expr.hir_id, method);
Ok(method)
Expand Down Expand Up @@ -2252,7 +2252,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> Ty<'tcx> {
debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
let base_ty = self.check_expr(base);
let base_ty = self.structurally_resolved_type(base.span, base_ty);
let base_ty = self.structurally_resolve_type(base.span, base_ty);
let mut private_candidate = None;
let mut autoderef = self.autoderef(expr.span, base_ty);
while let Some((deref_base_ty, _)) = autoderef.next() {
Expand Down Expand Up @@ -2300,7 +2300,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => {}
}
}
self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));

if let Some((adjustments, did)) = private_candidate {
// (#90483) apply adjustments to avoid ExprUseVisitor from
Expand Down Expand Up @@ -2857,7 +2857,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else if idx_t.references_error() {
idx_t
} else {
let base_t = self.structurally_resolved_type(base.span, base_t);
let base_t = self.structurally_resolve_type(base.span, base_t);
match self.lookup_indexing(expr, base, base_t, idx, idx_t) {
Some((index_ty, element_ty)) => {
// two-phase not needed because index_ty is never mutable
Expand Down Expand Up @@ -3084,7 +3084,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// allows them to be inferred based on how they are used later in the
// function.
if is_input {
let ty = self.structurally_resolved_type(expr.span, ty);
let ty = self.structurally_resolve_type(expr.span, ty);
match *ty.kind() {
ty::FnDef(..) => {
let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx));
Expand Down Expand Up @@ -3142,7 +3142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut current_container = container;

for &field in fields {
let container = self.structurally_resolved_type(expr.span, current_container);
let container = self.structurally_resolve_type(expr.span, current_container);

match container.kind() {
ty::Adt(container_def, substs) if !container_def.is_enum() => {
Expand Down
37 changes: 24 additions & 13 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// version (resolve_vars_if_possible), this version will
/// also select obligations if it seems useful, in an effort
/// to get more type information.
// FIXME(-Ztrait-solver=next): A lot of the calls to this method should
// probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead.
pub(in super::super) fn resolve_vars_with_obligations(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {})
}
Expand Down Expand Up @@ -1465,16 +1467,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

/// Resolves `typ` by a single level if `typ` is a type variable.
/// Try to resolve `ty` to a structural type, normalizing aliases.
///
/// When the new solver is enabled, this will also attempt to normalize
/// the type if it's a projection (note that it will not deeply normalize
/// projections within the type, just the outermost layer of the type).
///
/// If no resolution is possible, then an error is reported.
/// Numeric inference variables may be left unresolved.
pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
let mut ty = self.resolve_vars_with_obligations(ty);
/// In case there is still ambiguity, the returned type may be an inference
/// variable. This is different from `structurally_resolve_type` which errors
/// in this case.
pub fn try_structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
let ty = self.resolve_vars_with_obligations(ty);

if self.next_trait_solver()
&& let ty::Alias(ty::Projection, _) = ty.kind()
Expand All @@ -1483,15 +1482,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.at(&self.misc(sp), self.param_env)
.structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut())
{
Ok(normalized_ty) => {
ty = normalized_ty;
},
Ok(normalized_ty) => normalized_ty,
Err(errors) => {
let guar = self.err_ctxt().report_fulfillment_errors(&errors);
return self.tcx.ty_error(guar);
}
}
}
} else {
ty
}
}

/// Resolves `ty` by a single level if `ty` is a type variable.
///
/// When the new solver is enabled, this will also attempt to normalize
/// the type if it's a projection (note that it will not deeply normalize
/// projections within the type, just the outermost layer of the type).
///
/// If no resolution is possible, then an error is reported.
/// Numeric inference variables may be left unresolved.
pub fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
let ty = self.try_structurally_resolve_type(sp, ty);

if !ty.is_ty_var() {
ty
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

// If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments {
let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]);
let tuple_type = self.structurally_resolve_type(call_span, formal_input_tys[0]);
match tuple_type.kind() {
// We expected a tuple and got a tuple
ty::Tuple(arg_types) => {
Expand Down Expand Up @@ -412,7 +412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

// There are a few types which get autopromoted when passed via varargs
// in C but we just error out instead and require explicit casts.
let arg_ty = self.structurally_resolved_type(arg.span, arg_ty);
let arg_ty = self.structurally_resolve_type(arg.span, arg_ty);
match arg_ty.kind() {
ty::Float(ty::FloatTy::F32) => {
variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
assert_eq!(n, pick.autoderefs);

let mut adjustments = self.adjust_steps(&autoderef);
let mut target = self.structurally_resolved_type(autoderef.span(), ty);
let mut target = self.structurally_resolve_type(autoderef.span(), ty);

match pick.autoref_or_ptr_adjustment {
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
// Ended up encountering a type variable when doing autoderef,
// but it may not be a type variable after processing obligations
// in our local `FnCtxt`, so don't call `structurally_resolved_type`.
// in our local `FnCtxt`, so don't call `structurally_resolve_type`.
let ty = &bad_ty.ty;
let ty = self
.probe_instantiate_query_response(span, &orig_values, ty)
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// They can denote both statically and dynamically-sized byte arrays.
let mut pat_ty = ty;
if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(..), .. }) = lt.kind {
let expected = self.structurally_resolved_type(span, expected);
let expected = self.structurally_resolve_type(span, expected);
if let ty::Ref(_, inner_ty, _) = expected.kind()
&& matches!(inner_ty.kind(), ty::Slice(_))
{
Expand Down Expand Up @@ -501,7 +501,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// This check is needed if both sides are inference variables.
// We require types to be resolved here so that we emit inference failure
// rather than "_ is not a char or numeric".
let ty = self.structurally_resolved_type(span, expected);
let ty = self.structurally_resolve_type(span, expected);
if !(ty.is_numeric() || ty.is_char() || ty.references_error()) {
if let Some((ref mut fail, _, _)) = lhs {
*fail = true;
Expand Down Expand Up @@ -1289,7 +1289,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut expected_len = elements.len();
if ddpos.as_opt_usize().is_some() {
// Require known type only when `..` is present.
if let ty::Tuple(tys) = self.structurally_resolved_type(span, expected).kind() {
if let ty::Tuple(tys) = self.structurally_resolve_type(span, expected).kind() {
expected_len = tys.len();
}
}
Expand Down Expand Up @@ -2042,7 +2042,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
def_bm: BindingMode,
ti: TopInfo<'tcx>,
) -> Ty<'tcx> {
let expected = self.structurally_resolved_type(span, expected);
let expected = self.structurally_resolve_type(span, expected);
let (element_ty, opt_slice_ty, inferred) = match *expected.kind() {
// An array, so we might have something like `let [a, b, c] = [0, 1, 2];`.
ty::Array(element_ty, len) => {
Expand Down
Loading

0 comments on commit 737b461

Please sign in to comment.