Auto merge of #102700 - oli-obk:0xDEAD_TAIT, r=compiler-errors
Check hidden types in dead code fixes #99490 r? `@compiler-errors` best reviewed commit by commit
This commit is contained in:
commit
60bd3f9677
14 changed files with 387 additions and 319 deletions
|
@ -2,12 +2,10 @@ use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_data_structures::vec_map::VecMap;
|
use rustc_data_structures::vec_map::VecMap;
|
||||||
use rustc_hir::def_id::LocalDefId;
|
use rustc_hir::def_id::LocalDefId;
|
||||||
use rustc_hir::OpaqueTyOrigin;
|
use rustc_hir::OpaqueTyOrigin;
|
||||||
use rustc_infer::infer::error_reporting::unexpected_hidden_region_diagnostic;
|
|
||||||
use rustc_infer::infer::TyCtxtInferExt as _;
|
use rustc_infer::infer::TyCtxtInferExt as _;
|
||||||
use rustc_infer::infer::{DefiningAnchor, InferCtxt};
|
use rustc_infer::infer::{DefiningAnchor, InferCtxt};
|
||||||
use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine};
|
use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine};
|
||||||
use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
|
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
|
||||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts};
|
|
||||||
use rustc_middle::ty::visit::TypeVisitable;
|
use rustc_middle::ty::visit::TypeVisitable;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, OpaqueHiddenType, OpaqueTypeKey, ToPredicate, Ty, TyCtxt, TypeFoldable,
|
self, OpaqueHiddenType, OpaqueTypeKey, ToPredicate, Ty, TyCtxt, TypeFoldable,
|
||||||
|
@ -16,8 +14,6 @@ use rustc_span::Span;
|
||||||
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
|
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
|
||||||
use rustc_trait_selection::traits::TraitEngineExt as _;
|
use rustc_trait_selection::traits::TraitEngineExt as _;
|
||||||
|
|
||||||
use crate::session_diagnostics::ConstNotUsedTraitAlias;
|
|
||||||
|
|
||||||
use super::RegionInferenceContext;
|
use super::RegionInferenceContext;
|
||||||
|
|
||||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
|
@ -229,31 +225,9 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
|
||||||
return self.tcx.ty_error();
|
return self.tcx.ty_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
let OpaqueTypeKey { def_id, substs } = opaque_type_key;
|
let definition_ty = instantiated_ty
|
||||||
|
.remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false)
|
||||||
// Use substs to build up a reverse map from regions to their
|
.ty;
|
||||||
// identity mappings. This is necessary because of `impl
|
|
||||||
// Trait` lifetimes are computed by replacing existing
|
|
||||||
// lifetimes with 'static and remapping only those used in the
|
|
||||||
// `impl Trait` return type, resulting in the parameters
|
|
||||||
// shifting.
|
|
||||||
let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id.to_def_id());
|
|
||||||
debug!(?id_substs);
|
|
||||||
let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> =
|
|
||||||
substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect();
|
|
||||||
debug!("map = {:#?}", map);
|
|
||||||
|
|
||||||
// Convert the type from the function into a type valid outside
|
|
||||||
// the function, by replacing invalid regions with 'static,
|
|
||||||
// after producing an error for each of them.
|
|
||||||
let definition_ty = instantiated_ty.ty.fold_with(&mut ReverseMapper::new(
|
|
||||||
self.tcx,
|
|
||||||
opaque_type_key,
|
|
||||||
map,
|
|
||||||
instantiated_ty.ty,
|
|
||||||
instantiated_ty.span,
|
|
||||||
));
|
|
||||||
debug!(?definition_ty);
|
|
||||||
|
|
||||||
if !check_opaque_type_parameter_valid(
|
if !check_opaque_type_parameter_valid(
|
||||||
self.tcx,
|
self.tcx,
|
||||||
|
@ -269,6 +243,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
|
||||||
let OpaqueTyOrigin::TyAlias = origin else {
|
let OpaqueTyOrigin::TyAlias = origin else {
|
||||||
return definition_ty;
|
return definition_ty;
|
||||||
};
|
};
|
||||||
|
let def_id = opaque_type_key.def_id;
|
||||||
// This logic duplicates most of `check_opaque_meets_bounds`.
|
// This logic duplicates most of `check_opaque_meets_bounds`.
|
||||||
// FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely.
|
// FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely.
|
||||||
let param_env = self.tcx.param_env(def_id);
|
let param_env = self.tcx.param_env(def_id);
|
||||||
|
@ -284,6 +259,8 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
|
||||||
.to_predicate(infcx.tcx);
|
.to_predicate(infcx.tcx);
|
||||||
let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx);
|
let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx);
|
||||||
|
|
||||||
|
let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id.to_def_id());
|
||||||
|
|
||||||
// Require that the hidden type actually fulfills all the bounds of the opaque type, even without
|
// Require that the hidden type actually fulfills all the bounds of the opaque type, even without
|
||||||
// the bounds that the function supplies.
|
// the bounds that the function supplies.
|
||||||
match infcx.register_hidden_type(
|
match infcx.register_hidden_type(
|
||||||
|
@ -424,221 +401,3 @@ fn check_opaque_type_parameter_valid(
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ReverseMapper<'tcx> {
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
|
|
||||||
key: ty::OpaqueTypeKey<'tcx>,
|
|
||||||
map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
|
|
||||||
do_not_error: bool,
|
|
||||||
|
|
||||||
/// initially `Some`, set to `None` once error has been reported
|
|
||||||
hidden_ty: Option<Ty<'tcx>>,
|
|
||||||
|
|
||||||
/// Span of function being checked.
|
|
||||||
span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> ReverseMapper<'tcx> {
|
|
||||||
fn new(
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
key: ty::OpaqueTypeKey<'tcx>,
|
|
||||||
map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
|
|
||||||
hidden_ty: Ty<'tcx>,
|
|
||||||
span: Span,
|
|
||||||
) -> Self {
|
|
||||||
Self { tcx, key, map, do_not_error: false, hidden_ty: Some(hidden_ty), span }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> {
|
|
||||||
assert!(!self.do_not_error);
|
|
||||||
self.do_not_error = true;
|
|
||||||
let kind = kind.fold_with(self);
|
|
||||||
self.do_not_error = false;
|
|
||||||
kind
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold_kind_normally(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> {
|
|
||||||
assert!(!self.do_not_error);
|
|
||||||
kind.fold_with(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> {
|
|
||||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
|
||||||
self.tcx
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(skip(self), level = "debug")]
|
|
||||||
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
|
||||||
match *r {
|
|
||||||
// Ignore bound regions and `'static` regions that appear in the
|
|
||||||
// type, we only need to remap regions that reference lifetimes
|
|
||||||
// from the function declaration.
|
|
||||||
// This would ignore `'r` in a type like `for<'r> fn(&'r u32)`.
|
|
||||||
ty::ReLateBound(..) | ty::ReStatic => return r,
|
|
||||||
|
|
||||||
// If regions have been erased (by writeback), don't try to unerase
|
|
||||||
// them.
|
|
||||||
ty::ReErased => return r,
|
|
||||||
|
|
||||||
// The regions that we expect from borrow checking.
|
|
||||||
ty::ReEarlyBound(_) | ty::ReFree(_) => {}
|
|
||||||
|
|
||||||
ty::RePlaceholder(_) | ty::ReVar(_) => {
|
|
||||||
// All of the regions in the type should either have been
|
|
||||||
// erased by writeback, or mapped back to named regions by
|
|
||||||
// borrow checking.
|
|
||||||
bug!("unexpected region kind in opaque type: {:?}", r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let generics = self.tcx().generics_of(self.key.def_id);
|
|
||||||
match self.map.get(&r.into()).map(|k| k.unpack()) {
|
|
||||||
Some(GenericArgKind::Lifetime(r1)) => r1,
|
|
||||||
Some(u) => panic!("region mapped to unexpected kind: {:?}", u),
|
|
||||||
None if self.do_not_error => self.tcx.lifetimes.re_static,
|
|
||||||
None if generics.parent.is_some() => {
|
|
||||||
if let Some(hidden_ty) = self.hidden_ty.take() {
|
|
||||||
unexpected_hidden_region_diagnostic(
|
|
||||||
self.tcx,
|
|
||||||
self.tcx.def_span(self.key.def_id),
|
|
||||||
hidden_ty,
|
|
||||||
r,
|
|
||||||
self.key,
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
}
|
|
||||||
self.tcx.lifetimes.re_static
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.tcx
|
|
||||||
.sess
|
|
||||||
.struct_span_err(self.span, "non-defining opaque type use in defining scope")
|
|
||||||
.span_label(
|
|
||||||
self.span,
|
|
||||||
format!(
|
|
||||||
"lifetime `{}` is part of concrete type but not used in \
|
|
||||||
parameter list of the `impl Trait` type alias",
|
|
||||||
r
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
|
|
||||||
self.tcx().lifetimes.re_static
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
|
||||||
match *ty.kind() {
|
|
||||||
ty::Closure(def_id, substs) => {
|
|
||||||
// I am a horrible monster and I pray for death. When
|
|
||||||
// we encounter a closure here, it is always a closure
|
|
||||||
// from within the function that we are currently
|
|
||||||
// type-checking -- one that is now being encapsulated
|
|
||||||
// in an opaque type. Ideally, we would
|
|
||||||
// go through the types/lifetimes that it references
|
|
||||||
// and treat them just like we would any other type,
|
|
||||||
// which means we would error out if we find any
|
|
||||||
// reference to a type/region that is not in the
|
|
||||||
// "reverse map".
|
|
||||||
//
|
|
||||||
// **However,** in the case of closures, there is a
|
|
||||||
// somewhat subtle (read: hacky) consideration. The
|
|
||||||
// problem is that our closure types currently include
|
|
||||||
// all the lifetime parameters declared on the
|
|
||||||
// enclosing function, even if they are unused by the
|
|
||||||
// closure itself. We can't readily filter them out,
|
|
||||||
// so here we replace those values with `'empty`. This
|
|
||||||
// can't really make a difference to the rest of the
|
|
||||||
// compiler; those regions are ignored for the
|
|
||||||
// outlives relation, and hence don't affect trait
|
|
||||||
// selection or auto traits, and they are erased
|
|
||||||
// during codegen.
|
|
||||||
|
|
||||||
let generics = self.tcx.generics_of(def_id);
|
|
||||||
let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| {
|
|
||||||
if index < generics.parent_count {
|
|
||||||
// Accommodate missing regions in the parent kinds...
|
|
||||||
self.fold_kind_no_missing_regions_error(kind)
|
|
||||||
} else {
|
|
||||||
// ...but not elsewhere.
|
|
||||||
self.fold_kind_normally(kind)
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
self.tcx.mk_closure(def_id, substs)
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Generator(def_id, substs, movability) => {
|
|
||||||
let generics = self.tcx.generics_of(def_id);
|
|
||||||
let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| {
|
|
||||||
if index < generics.parent_count {
|
|
||||||
// Accommodate missing regions in the parent kinds...
|
|
||||||
self.fold_kind_no_missing_regions_error(kind)
|
|
||||||
} else {
|
|
||||||
// ...but not elsewhere.
|
|
||||||
self.fold_kind_normally(kind)
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
self.tcx.mk_generator(def_id, substs, movability)
|
|
||||||
}
|
|
||||||
|
|
||||||
ty::Param(param) => {
|
|
||||||
// Look it up in the substitution list.
|
|
||||||
match self.map.get(&ty.into()).map(|k| k.unpack()) {
|
|
||||||
// Found it in the substitution list; replace with the parameter from the
|
|
||||||
// opaque type.
|
|
||||||
Some(GenericArgKind::Type(t1)) => t1,
|
|
||||||
Some(u) => panic!("type mapped to unexpected kind: {:?}", u),
|
|
||||||
None => {
|
|
||||||
debug!(?param, ?self.map);
|
|
||||||
self.tcx
|
|
||||||
.sess
|
|
||||||
.struct_span_err(
|
|
||||||
self.span,
|
|
||||||
&format!(
|
|
||||||
"type parameter `{}` is part of concrete type but not \
|
|
||||||
used in parameter list for the `impl Trait` type alias",
|
|
||||||
ty
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.emit();
|
|
||||||
|
|
||||||
self.tcx().ty_error()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => ty.super_fold_with(self),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
|
||||||
trace!("checking const {:?}", ct);
|
|
||||||
// Find a const parameter
|
|
||||||
match ct.kind() {
|
|
||||||
ty::ConstKind::Param(..) => {
|
|
||||||
// Look it up in the substitution list.
|
|
||||||
match self.map.get(&ct.into()).map(|k| k.unpack()) {
|
|
||||||
// Found it in the substitution list, replace with the parameter from the
|
|
||||||
// opaque type.
|
|
||||||
Some(GenericArgKind::Const(c1)) => c1,
|
|
||||||
Some(u) => panic!("const mapped to unexpected kind: {:?}", u),
|
|
||||||
None => {
|
|
||||||
self.tcx.sess.emit_err(ConstNotUsedTraitAlias {
|
|
||||||
ct: ct.to_string(),
|
|
||||||
span: self.span,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.tcx().const_error(ct.ty())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => ct,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -52,15 +52,6 @@ pub(crate) struct VarNeedNotMut {
|
||||||
#[suggestion_short(applicability = "machine-applicable", code = "")]
|
#[suggestion_short(applicability = "machine-applicable", code = "")]
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
|
||||||
#[diag(borrowck::const_not_used_in_type_alias)]
|
|
||||||
pub(crate) struct ConstNotUsedTraitAlias {
|
|
||||||
pub ct: String,
|
|
||||||
#[primary_span]
|
|
||||||
pub span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(borrowck::var_cannot_escape_closure)]
|
#[diag(borrowck::var_cannot_escape_closure)]
|
||||||
#[note]
|
#[note]
|
||||||
|
|
|
@ -536,33 +536,36 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
||||||
let opaque_types =
|
let opaque_types =
|
||||||
self.fcx.infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
|
self.fcx.infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
|
||||||
for (opaque_type_key, decl) in opaque_types {
|
for (opaque_type_key, decl) in opaque_types {
|
||||||
let hidden_type = match decl.origin {
|
let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span);
|
||||||
hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => {
|
let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span);
|
||||||
let ty = self.resolve(decl.hidden_type.ty, &decl.hidden_type.span);
|
|
||||||
struct RecursionChecker {
|
struct RecursionChecker {
|
||||||
def_id: LocalDefId,
|
def_id: LocalDefId,
|
||||||
}
|
}
|
||||||
impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker {
|
impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker {
|
||||||
type BreakTy = ();
|
type BreakTy = ();
|
||||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||||
if let ty::Opaque(def_id, _) = *t.kind() {
|
if let ty::Opaque(def_id, _) = *t.kind() {
|
||||||
if def_id == self.def_id.to_def_id() {
|
if def_id == self.def_id.to_def_id() {
|
||||||
return ControlFlow::Break(());
|
return ControlFlow::Break(());
|
||||||
}
|
|
||||||
}
|
|
||||||
t.super_visit_with(self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ty
|
t.super_visit_with(self)
|
||||||
.visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id })
|
|
||||||
.is_break()
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Some(ty)
|
|
||||||
}
|
}
|
||||||
hir::OpaqueTyOrigin::TyAlias => None,
|
}
|
||||||
};
|
if hidden_type
|
||||||
|
.visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id })
|
||||||
|
.is_break()
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hidden_type = hidden_type.remap_generic_params_to_declaration_params(
|
||||||
|
opaque_type_key,
|
||||||
|
self.fcx.infcx.tcx,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type);
|
self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -565,6 +565,11 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
|
||||||
/// checked against it (we also carry the span of that first
|
/// checked against it (we also carry the span of that first
|
||||||
/// type).
|
/// type).
|
||||||
found: Option<ty::OpaqueHiddenType<'tcx>>,
|
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
|
||||||
|
/// the end that we actually found a type that matches (modulo regions).
|
||||||
|
typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConstraintLocator<'_> {
|
impl ConstraintLocator<'_> {
|
||||||
|
@ -591,18 +596,23 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
|
||||||
self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error() });
|
self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error() });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if !tables.concrete_opaque_types.contains_key(&self.def_id) {
|
let Some(&typeck_hidden_ty) = tables.concrete_opaque_types.get(&self.def_id) else {
|
||||||
debug!("no constraints in typeck results");
|
debug!("no constraints in typeck results");
|
||||||
return;
|
return;
|
||||||
|
};
|
||||||
|
if self.typeck_types.iter().all(|prev| prev.ty != typeck_hidden_ty.ty) {
|
||||||
|
self.typeck_types.push(typeck_hidden_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use borrowck to get the type with unerased regions.
|
// Use borrowck to get the type with unerased regions.
|
||||||
let concrete_opaque_types = &self.tcx.mir_borrowck(item_def_id).concrete_opaque_types;
|
let concrete_opaque_types = &self.tcx.mir_borrowck(item_def_id).concrete_opaque_types;
|
||||||
debug!(?concrete_opaque_types);
|
debug!(?concrete_opaque_types);
|
||||||
if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) {
|
if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) {
|
||||||
debug!(?concrete_type, "found constraint");
|
debug!(?concrete_type, "found constraint");
|
||||||
if let Some(prev) = self.found {
|
if let Some(prev) = &mut self.found {
|
||||||
if concrete_type.ty != prev.ty && !(concrete_type, prev).references_error() {
|
if concrete_type.ty != prev.ty && !(concrete_type, prev.ty).references_error() {
|
||||||
prev.report_mismatch(&concrete_type, self.tcx);
|
prev.report_mismatch(&concrete_type, self.tcx);
|
||||||
|
prev.ty = self.tcx.ty_error();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.found = Some(concrete_type);
|
self.found = Some(concrete_type);
|
||||||
|
@ -649,7 +659,7 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
|
||||||
|
|
||||||
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
|
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
|
||||||
let scope = tcx.hir().get_defining_scope(hir_id);
|
let scope = tcx.hir().get_defining_scope(hir_id);
|
||||||
let mut locator = ConstraintLocator { def_id: def_id, tcx, found: None };
|
let mut locator = ConstraintLocator { def_id: def_id, tcx, found: None, typeck_types: vec![] };
|
||||||
|
|
||||||
debug!(?scope);
|
debug!(?scope);
|
||||||
|
|
||||||
|
@ -679,16 +689,26 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match locator.found {
|
let Some(hidden) = locator.found else {
|
||||||
Some(hidden) => hidden.ty,
|
tcx.sess.emit_err(UnconstrainedOpaqueType {
|
||||||
None => {
|
span: tcx.def_span(def_id),
|
||||||
tcx.sess.emit_err(UnconstrainedOpaqueType {
|
name: tcx.item_name(tcx.local_parent(def_id).to_def_id()),
|
||||||
span: tcx.def_span(def_id),
|
});
|
||||||
name: tcx.item_name(tcx.local_parent(def_id).to_def_id()),
|
return tcx.ty_error();
|
||||||
});
|
};
|
||||||
tcx.ty_error()
|
|
||||||
|
// Only check against typeck if we didn't already error
|
||||||
|
if !hidden.ty.references_error() {
|
||||||
|
for concrete_type in locator.typeck_types {
|
||||||
|
if tcx.erase_regions(concrete_type.ty) != tcx.erase_regions(hidden.ty)
|
||||||
|
&& !(concrete_type, hidden).references_error()
|
||||||
|
{
|
||||||
|
hidden.report_mismatch(&concrete_type, tcx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hidden.ty
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_opaque_ty_constraints_for_rpit(
|
fn find_opaque_ty_constraints_for_rpit(
|
||||||
|
@ -789,20 +809,15 @@ fn find_opaque_ty_constraints_for_rpit(
|
||||||
// the `concrete_opaque_types` table.
|
// the `concrete_opaque_types` table.
|
||||||
tcx.ty_error()
|
tcx.ty_error()
|
||||||
} else {
|
} else {
|
||||||
table
|
table.concrete_opaque_types.get(&def_id).map(|ty| ty.ty).unwrap_or_else(|| {
|
||||||
.concrete_opaque_types
|
// We failed to resolve the opaque type or it
|
||||||
.get(&def_id)
|
// resolves to itself. We interpret this as the
|
||||||
.copied()
|
// no values of the hidden type ever being constructed,
|
||||||
.unwrap_or_else(|| {
|
// so we can just make the hidden type be `!`.
|
||||||
// We failed to resolve the opaque type or it
|
// For backwards compatibility reasons, we fall back to
|
||||||
// resolves to itself. We interpret this as the
|
// `()` until we the diverging default is changed.
|
||||||
// no values of the hidden type ever being constructed,
|
tcx.mk_diverging_default()
|
||||||
// so we can just make the hidden type be `!`.
|
})
|
||||||
// For backwards compatibility reasons, we fall back to
|
|
||||||
// `()` until we the diverging default is changed.
|
|
||||||
Some(tcx.mk_diverging_default())
|
|
||||||
})
|
|
||||||
.expect("RPIT always have a hidden type from typeck")
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -539,12 +539,10 @@ pub struct TypeckResults<'tcx> {
|
||||||
pub tainted_by_errors: Option<ErrorGuaranteed>,
|
pub tainted_by_errors: Option<ErrorGuaranteed>,
|
||||||
|
|
||||||
/// All the opaque types that have hidden types set
|
/// All the opaque types that have hidden types set
|
||||||
/// by this function. For return-position-impl-trait we also store the
|
/// by this function. We also store the
|
||||||
/// type here, so that mir-borrowck can figure out hidden types,
|
/// type here, so that mir-borrowck can use it as a hint for figuring out hidden types,
|
||||||
/// even if they are only set in dead code (which doesn't show up in MIR).
|
/// even if they are only set in dead code (which doesn't show up in MIR).
|
||||||
/// For type-alias-impl-trait, this map is only used to prevent query cycles,
|
pub concrete_opaque_types: VecMap<LocalDefId, ty::OpaqueHiddenType<'tcx>>,
|
||||||
/// so the hidden types are all `None`.
|
|
||||||
pub concrete_opaque_types: VecMap<LocalDefId, Option<Ty<'tcx>>>,
|
|
||||||
|
|
||||||
/// Tracks the minimum captures required for a closure;
|
/// Tracks the minimum captures required for a closure;
|
||||||
/// see `MinCaptureInformationMap` for more details.
|
/// see `MinCaptureInformationMap` for more details.
|
||||||
|
|
|
@ -511,3 +511,11 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
|
||||||
c.super_visit_with(self)
|
c.super_visit_with(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(borrowck::const_not_used_in_type_alias)]
|
||||||
|
pub(super) struct ConstNotUsedTraitAlias {
|
||||||
|
pub ct: String,
|
||||||
|
#[primary_span]
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
|
@ -131,6 +131,7 @@ mod generics;
|
||||||
mod impls_ty;
|
mod impls_ty;
|
||||||
mod instance;
|
mod instance;
|
||||||
mod list;
|
mod list;
|
||||||
|
mod opaque_types;
|
||||||
mod parameterized;
|
mod parameterized;
|
||||||
mod rvalue_scopes;
|
mod rvalue_scopes;
|
||||||
mod structural_impls;
|
mod structural_impls;
|
||||||
|
@ -1300,6 +1301,34 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
|
||||||
sub: sub_diag,
|
sub: sub_diag,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(tcx), ret)]
|
||||||
|
pub fn remap_generic_params_to_declaration_params(
|
||||||
|
self,
|
||||||
|
opaque_type_key: OpaqueTypeKey<'tcx>,
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
// typeck errors have subpar spans for opaque types, so delay error reporting until borrowck.
|
||||||
|
ignore_errors: bool,
|
||||||
|
) -> Self {
|
||||||
|
let OpaqueTypeKey { def_id, substs } = opaque_type_key;
|
||||||
|
|
||||||
|
// Use substs to build up a reverse map from regions to their
|
||||||
|
// identity mappings. This is necessary because of `impl
|
||||||
|
// Trait` lifetimes are computed by replacing existing
|
||||||
|
// lifetimes with 'static and remapping only those used in the
|
||||||
|
// `impl Trait` return type, resulting in the parameters
|
||||||
|
// shifting.
|
||||||
|
let id_substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
|
||||||
|
debug!(?id_substs);
|
||||||
|
let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> =
|
||||||
|
substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect();
|
||||||
|
debug!("map = {:#?}", map);
|
||||||
|
|
||||||
|
// Convert the type from the function into a type valid outside
|
||||||
|
// the function, by replacing invalid regions with 'static,
|
||||||
|
// after producing an error for each of them.
|
||||||
|
self.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span, ignore_errors))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The "placeholder index" fully defines a placeholder region, type, or const. Placeholders are
|
/// The "placeholder index" fully defines a placeholder region, type, or const. Placeholders are
|
||||||
|
|
218
compiler/rustc_middle/src/ty/opaque_types.rs
Normal file
218
compiler/rustc_middle/src/ty/opaque_types.rs
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
|
use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
|
||||||
|
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
|
||||||
|
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
/// Converts generic params of a TypeFoldable from one
|
||||||
|
/// item's generics to another. Usually from a function's generics
|
||||||
|
/// list to the opaque type's own generics.
|
||||||
|
pub(super) struct ReverseMapper<'tcx> {
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
|
||||||
|
/// see call sites to fold_kind_no_missing_regions_error
|
||||||
|
/// for an explanation of this field.
|
||||||
|
do_not_error: bool,
|
||||||
|
|
||||||
|
/// We do not want to emit any errors in typeck because
|
||||||
|
/// the spans in typeck are subpar at the moment.
|
||||||
|
/// Borrowck will do the same work again (this time with
|
||||||
|
/// lifetime information) and thus report better errors.
|
||||||
|
ignore_errors: bool,
|
||||||
|
|
||||||
|
/// Span of function being checked.
|
||||||
|
span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> ReverseMapper<'tcx> {
|
||||||
|
pub(super) fn new(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
|
||||||
|
span: Span,
|
||||||
|
ignore_errors: bool,
|
||||||
|
) -> Self {
|
||||||
|
Self { tcx, map, do_not_error: false, ignore_errors, span }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> {
|
||||||
|
assert!(!self.do_not_error);
|
||||||
|
self.do_not_error = true;
|
||||||
|
let kind = kind.fold_with(self);
|
||||||
|
self.do_not_error = false;
|
||||||
|
kind
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_kind_normally(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> {
|
||||||
|
assert!(!self.do_not_error);
|
||||||
|
kind.fold_with(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> {
|
||||||
|
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||||
|
self.tcx
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self), level = "debug")]
|
||||||
|
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
||||||
|
match *r {
|
||||||
|
// Ignore bound regions and `'static` regions that appear in the
|
||||||
|
// type, we only need to remap regions that reference lifetimes
|
||||||
|
// from the function declaration.
|
||||||
|
// This would ignore `'r` in a type like `for<'r> fn(&'r u32)`.
|
||||||
|
ty::ReLateBound(..) | ty::ReStatic => return r,
|
||||||
|
|
||||||
|
// If regions have been erased (by writeback), don't try to unerase
|
||||||
|
// them.
|
||||||
|
ty::ReErased => return r,
|
||||||
|
|
||||||
|
// The regions that we expect from borrow checking.
|
||||||
|
ty::ReEarlyBound(_) | ty::ReFree(_) => {}
|
||||||
|
|
||||||
|
ty::RePlaceholder(_) | ty::ReVar(_) => {
|
||||||
|
// All of the regions in the type should either have been
|
||||||
|
// erased by writeback, or mapped back to named regions by
|
||||||
|
// borrow checking.
|
||||||
|
bug!("unexpected region kind in opaque type: {:?}", r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.map.get(&r.into()).map(|k| k.unpack()) {
|
||||||
|
Some(GenericArgKind::Lifetime(r1)) => r1,
|
||||||
|
Some(u) => panic!("region mapped to unexpected kind: {:?}", u),
|
||||||
|
None if self.do_not_error => self.tcx.lifetimes.re_static,
|
||||||
|
None => {
|
||||||
|
self.tcx
|
||||||
|
.sess
|
||||||
|
.struct_span_err(self.span, "non-defining opaque type use in defining scope")
|
||||||
|
.span_label(
|
||||||
|
self.span,
|
||||||
|
format!(
|
||||||
|
"lifetime `{}` is part of concrete type but not used in \
|
||||||
|
parameter list of the `impl Trait` type alias",
|
||||||
|
r
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
|
||||||
|
self.tcx().lifetimes.re_static
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||||
|
match *ty.kind() {
|
||||||
|
ty::Closure(def_id, substs) => {
|
||||||
|
// I am a horrible monster and I pray for death. When
|
||||||
|
// we encounter a closure here, it is always a closure
|
||||||
|
// from within the function that we are currently
|
||||||
|
// type-checking -- one that is now being encapsulated
|
||||||
|
// in an opaque type. Ideally, we would
|
||||||
|
// go through the types/lifetimes that it references
|
||||||
|
// and treat them just like we would any other type,
|
||||||
|
// which means we would error out if we find any
|
||||||
|
// reference to a type/region that is not in the
|
||||||
|
// "reverse map".
|
||||||
|
//
|
||||||
|
// **However,** in the case of closures, there is a
|
||||||
|
// somewhat subtle (read: hacky) consideration. The
|
||||||
|
// problem is that our closure types currently include
|
||||||
|
// all the lifetime parameters declared on the
|
||||||
|
// enclosing function, even if they are unused by the
|
||||||
|
// closure itself. We can't readily filter them out,
|
||||||
|
// so here we replace those values with `'empty`. This
|
||||||
|
// can't really make a difference to the rest of the
|
||||||
|
// compiler; those regions are ignored for the
|
||||||
|
// outlives relation, and hence don't affect trait
|
||||||
|
// selection or auto traits, and they are erased
|
||||||
|
// during codegen.
|
||||||
|
|
||||||
|
let generics = self.tcx.generics_of(def_id);
|
||||||
|
let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| {
|
||||||
|
if index < generics.parent_count {
|
||||||
|
// Accommodate missing regions in the parent kinds...
|
||||||
|
self.fold_kind_no_missing_regions_error(kind)
|
||||||
|
} else {
|
||||||
|
// ...but not elsewhere.
|
||||||
|
self.fold_kind_normally(kind)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
self.tcx.mk_closure(def_id, substs)
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Generator(def_id, substs, movability) => {
|
||||||
|
let generics = self.tcx.generics_of(def_id);
|
||||||
|
let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| {
|
||||||
|
if index < generics.parent_count {
|
||||||
|
// Accommodate missing regions in the parent kinds...
|
||||||
|
self.fold_kind_no_missing_regions_error(kind)
|
||||||
|
} else {
|
||||||
|
// ...but not elsewhere.
|
||||||
|
self.fold_kind_normally(kind)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
self.tcx.mk_generator(def_id, substs, movability)
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Param(param) => {
|
||||||
|
// Look it up in the substitution list.
|
||||||
|
match self.map.get(&ty.into()).map(|k| k.unpack()) {
|
||||||
|
// Found it in the substitution list; replace with the parameter from the
|
||||||
|
// opaque type.
|
||||||
|
Some(GenericArgKind::Type(t1)) => t1,
|
||||||
|
Some(u) => panic!("type mapped to unexpected kind: {:?}", u),
|
||||||
|
None => {
|
||||||
|
debug!(?param, ?self.map);
|
||||||
|
if !self.ignore_errors {
|
||||||
|
self.tcx
|
||||||
|
.sess
|
||||||
|
.struct_span_err(
|
||||||
|
self.span,
|
||||||
|
&format!(
|
||||||
|
"type parameter `{}` is part of concrete type but not \
|
||||||
|
used in parameter list for the `impl Trait` type alias",
|
||||||
|
ty
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tcx().ty_error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => ty.super_fold_with(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||||
|
trace!("checking const {:?}", ct);
|
||||||
|
// Find a const parameter
|
||||||
|
match ct.kind() {
|
||||||
|
ty::ConstKind::Param(..) => {
|
||||||
|
// Look it up in the substitution list.
|
||||||
|
match self.map.get(&ct.into()).map(|k| k.unpack()) {
|
||||||
|
// Found it in the substitution list, replace with the parameter from the
|
||||||
|
// opaque type.
|
||||||
|
Some(GenericArgKind::Const(c1)) => c1,
|
||||||
|
Some(u) => panic!("const mapped to unexpected kind: {:?}", u),
|
||||||
|
None => {
|
||||||
|
if !self.ignore_errors {
|
||||||
|
self.tcx.sess.emit_err(ty::ConstNotUsedTraitAlias {
|
||||||
|
ct: ct.to_string(),
|
||||||
|
span: self.span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tcx().const_error(ct.ty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => ct,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -323,7 +323,7 @@ pub fn change_return_impl_trait() -> impl Clone {
|
||||||
#[cfg(not(any(cfail1,cfail4)))]
|
#[cfg(not(any(cfail1,cfail4)))]
|
||||||
#[rustc_clean(cfg = "cfail2")]
|
#[rustc_clean(cfg = "cfail2")]
|
||||||
#[rustc_clean(cfg = "cfail3")]
|
#[rustc_clean(cfg = "cfail3")]
|
||||||
#[rustc_clean(cfg = "cfail5")]
|
#[rustc_clean(cfg = "cfail5", except = "typeck")]
|
||||||
#[rustc_clean(cfg = "cfail6")]
|
#[rustc_clean(cfg = "cfail6")]
|
||||||
pub fn change_return_impl_trait() -> impl Copy {
|
pub fn change_return_impl_trait() -> impl Copy {
|
||||||
0u32
|
0u32
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
error: unconstrained opaque type
|
||||||
|
--> $DIR/issue-86800.rs:33:34
|
||||||
|
|
|
||||||
|
LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
=
|
||||||
|
|
||||||
|
|
||||||
stack backtrace:
|
stack backtrace:
|
||||||
|
|
||||||
|
@ -12,8 +20,7 @@ error: internal compiler error: unexpected panic
|
||||||
|
|
||||||
|
|
||||||
query stack during panic:
|
query stack during panic:
|
||||||
#0 [mir_borrowck] borrow-checking `execute_transaction_fut`
|
#0 [type_of] computing type of `TransactionFuture::{opaque#0}`
|
||||||
#1 [type_of] computing type of `TransactionFuture::{opaque#0}`
|
#1 [check_mod_item_types] checking item types in top-level module
|
||||||
#2 [check_mod_item_types] checking item types in top-level module
|
#2 [analysis] running analysis passes on this crate
|
||||||
#3 [analysis] running analysis passes on this crate
|
|
||||||
end of query stack
|
end of query stack
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
// check-pass
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
||||||
// two definitions with different types
|
// two definitions with different types
|
||||||
|
@ -9,7 +9,7 @@ fn foo() -> Foo {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bar() -> Foo {
|
fn bar() -> Foo { //~ ERROR: concrete type differs from previous defining opaque type use
|
||||||
panic!()
|
panic!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
error: concrete type differs from previous defining opaque type use
|
||||||
|
--> $DIR/different_defining_uses_never_type.rs:12:13
|
||||||
|
|
|
||||||
|
LL | fn bar() -> Foo {
|
||||||
|
| ^^^ expected `&'static str`, got `()`
|
||||||
|
|
|
||||||
|
note: previous use here
|
||||||
|
--> $DIR/different_defining_uses_never_type.rs:9:5
|
||||||
|
|
|
||||||
|
LL | ""
|
||||||
|
| ^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
type Tait = impl Sized;
|
||||||
|
|
||||||
|
struct One;
|
||||||
|
fn one() -> Tait { One }
|
||||||
|
|
||||||
|
struct Two<T>(T);
|
||||||
|
fn two() -> Tait { Two::<()>(todo!()) }
|
||||||
|
//~^ ERROR concrete type differs from previous defining opaque type use
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,14 @@
|
||||||
|
error: concrete type differs from previous defining opaque type use
|
||||||
|
--> $DIR/different_defining_uses_never_type3.rs:9:13
|
||||||
|
|
|
||||||
|
LL | fn two() -> Tait { Two::<()>(todo!()) }
|
||||||
|
| ^^^^ expected `One`, got `Two<()>`
|
||||||
|
|
|
||||||
|
note: previous use here
|
||||||
|
--> $DIR/different_defining_uses_never_type3.rs:6:20
|
||||||
|
|
|
||||||
|
LL | fn one() -> Tait { One }
|
||||||
|
| ^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue