Skip to content

Commit

Permalink
Auto merge of rust-lang#117164 - fmease:orphan-norm, r=<try>
Browse files Browse the repository at this point in the history
Normalize trait ref before orphan check & consider ty params in alias types to be uncovered

Fixes rust-lang#99554, fixes rust-lang/types-team#104.

Supersedes rust-lang#100555.

r? lcnr
  • Loading branch information
bors committed Feb 26, 2024
2 parents 829308e + 55d96b3 commit 575e416
Show file tree
Hide file tree
Showing 23 changed files with 529 additions and 70 deletions.
8 changes: 4 additions & 4 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -429,13 +429,13 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de
.label = needs at most one field with non-trivial size or alignment, but has {$field_count}
.labels = this field has non-zero size or requires alignment
hir_analysis_ty_param_first_local = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`)
.label = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`)
hir_analysis_ty_param_first_local = type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`)
.label = type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`)
.note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
.case_note = in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
hir_analysis_ty_param_some = type parameter `{$param_ty}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param_ty}>`)
.label = type parameter `{$param_ty}` must be used as the type parameter for some local type
hir_analysis_ty_param_some = type parameter `{$param}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param}>`)
.label = type parameter `{$param}` must be used as the type parameter for some local type
.note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
.only_note = only traits defined in the current crate can be implemented for a type parameter
Expand Down
104 changes: 79 additions & 25 deletions compiler/rustc_hir_analysis/src/coherence/orphan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@
//! crate or pertains to a type defined in this crate.

use crate::errors;

use std::ops::ControlFlow;

use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_middle::ty::{self, AliasKind, Ty, TyCtxt, TypeVisitableExt};
use rustc_infer::infer::type_variable::TypeVariableOriginKind;
use rustc_infer::infer::InferCtxt;
use rustc_middle::ty::{self, AliasKind, Ty, TyCtxt};
use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
use rustc_span::{Span, Symbol};
use rustc_trait_selection::traits::{self, IsFirstInputType};

#[instrument(skip(tcx), level = "debug")]
Expand All @@ -23,20 +29,17 @@ pub(crate) fn orphan_check_impl(
Ok(()) => {}
Err(err) => {
let item = tcx.hir().expect_item(impl_def_id);
let hir::ItemKind::Impl(impl_) = item.kind else {
bug!("{:?} is not an impl: {:?}", impl_def_id, item);
};
let tr = impl_.of_trait.as_ref().unwrap();
let sp = tcx.def_span(impl_def_id);
let impl_ = item.expect_impl();
let hir_trait_ref = impl_.of_trait.as_ref().unwrap();

emit_orphan_check_error(
tcx,
sp,
tcx.def_span(impl_def_id),
item.span,
tr.path.span,
hir_trait_ref.path.span,
trait_ref,
impl_.self_ty.span,
impl_.generics,
tcx.generics_of(impl_def_id),
err,
)?
}
Expand Down Expand Up @@ -277,7 +280,7 @@ fn emit_orphan_check_error<'tcx>(
trait_span: Span,
trait_ref: ty::TraitRef<'tcx>,
self_ty_span: Span,
generics: &hir::Generics<'tcx>,
generics: &'tcx ty::Generics,
err: traits::OrphanCheckErr<'tcx>,
) -> Result<!, ErrorGuaranteed> {
let self_ty = trait_ref.self_ty();
Expand Down Expand Up @@ -404,23 +407,74 @@ fn emit_orphan_check_error<'tcx>(
};
tcx.dcx().emit_err(err_struct)
}
traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => {
let mut sp = sp;
for param in generics.params {
if param.name.ident().to_string() == param_ty.to_string() {
sp = param.span;
// FIXME(fmease): The uncovered ty may contain infer vars.
// Somwhere we need to remap them to ty params.
traits::OrphanCheckErr::UncoveredTy(uncovered_ty, infcx, local_type) => {
let mut collector = UncoveredTyParamCollector {
tcx,
infcx,
generics,
uncovered_params: Default::default(),
};
uncovered_ty.visit_with(&mut collector);

let mut reported = None;
for (param, span) in collector.uncovered_params {
reported.get_or_insert(match local_type {
Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal {
span,
note: (),
param,
local_type,
}),
None => tcx.dcx().emit_err(errors::TyParamSome { span, note: (), param }),
});
}

struct UncoveredTyParamCollector<'tcx> {
tcx: TyCtxt<'tcx>,
infcx: Option<Box<InferCtxt<'tcx>>>,
generics: &'tcx ty::Generics,
uncovered_params: FxIndexMap<Symbol, Span>,
}

impl<'tcx> UncoveredTyParamCollector<'tcx> {
fn add_uncovered_param_ty(&mut self, index: u32) {
let param_def = self.generics.param_at(index as _, self.tcx);
let span = self.tcx.def_ident_span(param_def.def_id).unwrap();
self.uncovered_params.insert(param_def.name, span);
}
}

match local_type {
Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal {
span: sp,
note: (),
param_ty,
local_type,
}),
None => tcx.dcx().emit_err(errors::TyParamSome { span: sp, note: (), param_ty }),
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UncoveredTyParamCollector<'tcx> {
type BreakTy = !;

fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
match *ty.kind() {
ty::Param(param_ty) => {
self.add_uncovered_param_ty(param_ty.index);
return ControlFlow::Continue(());
}
ty::Infer(ty::TyVar(_)) => {
if let Some(infcx) = &self.infcx
&& let Some(origin) = infcx.type_var_origin(ty)
&& let TypeVariableOriginKind::TypeParameterDefinition(_, def_id) =
origin.kind
&& let Some(index) =
self.generics.param_def_id_to_index(self.tcx, def_id)
{
self.add_uncovered_param_ty(index);
return ControlFlow::Continue(());
}
}
_ => {}
}

ty.super_visit_with(self)
}
}

reported.unwrap_or_else(|| bug!("failed to find ty param in `{uncovered_ty}`"))
}
})
}
6 changes: 3 additions & 3 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1328,20 +1328,20 @@ pub struct TyParamFirstLocal<'a> {
pub span: Span,
#[note(hir_analysis_case_note)]
pub note: (),
pub param_ty: Ty<'a>,
pub param: Symbol,
pub local_type: Ty<'a>,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_ty_param_some, code = E0210)]
#[note]
pub struct TyParamSome<'a> {
pub struct TyParamSome {
#[primary_span]
#[label]
pub span: Span,
#[note(hir_analysis_only_note)]
pub note: (),
pub param_ty: Ty<'a>,
pub param: Symbol,
}

#[derive(Diagnostic)]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(extract_if)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(option_take_if)]
#![feature(never_type)]
Expand Down
Loading

0 comments on commit 575e416

Please sign in to comment.