Auto merge of #53588 - tristanburgess:52985_diagnostics_no_concrete_type_behind_existential_type, r=oli-obk
52985 diagnostics no concrete type behind existential type
@oli-obk FYI. See below for new cycle error generated.
```rust
error[E0391]: cycle detected when processing `Foo`
--> /dev/staging/existential_type_no_concrete_type_nouse_potential.rs:3:1
|
3 | existential type Foo: Copy;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires processing `bar`...
--> /dev/staging/existential_type_no_concrete_type_nouse_potential.rs:6:23
|
6 | fn bar(x: Foo) -> Foo {
| _______________________^
7 | | x
8 | | }
| |_^
= note: ...which again requires processing `Foo`, completing the cycle
error: aborting due to previous error
For more information about this error, try `rustc --explain E0391`.
```
This commit is contained in:
commit
5ce5e08606
4 changed files with 95 additions and 66 deletions
|
|
@ -121,36 +121,14 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for QueryNormalizer<'cx, 'gcx, 'tcx
|
|||
let concrete_ty = generic_ty.subst(self.tcx(), substs);
|
||||
self.anon_depth += 1;
|
||||
if concrete_ty == ty {
|
||||
// The type in question can only be inferred in terms of itself. This
|
||||
// is likely a user code issue, not a compiler issue. Thus, we will
|
||||
// induce a cycle error by calling the parent query again on the type.
|
||||
//
|
||||
// FIXME: Perhaps a better solution would be to have fold_ty()
|
||||
// itself be a query. Then, a type fold cycle would be detected
|
||||
// and reported more naturally as part of the query system, rather
|
||||
// than forcing it here.
|
||||
//
|
||||
// FIXME: Need a better span than just one pointing to the type def.
|
||||
// Should point to a defining use of the type that results in this
|
||||
// un-normalizable state.
|
||||
if let Some(param_env_lifted) =
|
||||
self.tcx().lift_to_global(&self.param_env)
|
||||
{
|
||||
if let Some(ty_lifted) = self.tcx().lift_to_global(&concrete_ty) {
|
||||
let span = self.tcx().def_span(def_id);
|
||||
self.tcx()
|
||||
.global_tcx()
|
||||
.at(span)
|
||||
.normalize_ty_after_erasing_regions(
|
||||
param_env_lifted.and(ty_lifted),
|
||||
);
|
||||
self.tcx().sess.abort_if_errors();
|
||||
}
|
||||
}
|
||||
// If a cycle error can't be emitted, indicate a NoSolution error
|
||||
// and let the caller handle it.
|
||||
self.error = true;
|
||||
return concrete_ty;
|
||||
bug!(
|
||||
"infinite recursion generic_ty: {:#?}, substs: {:#?}, \
|
||||
concrete_ty: {:#?}, ty: {:#?}",
|
||||
generic_ty,
|
||||
substs,
|
||||
concrete_ty,
|
||||
ty
|
||||
);
|
||||
}
|
||||
let folded_ty = self.fold_ty(concrete_ty);
|
||||
self.anon_depth -= 1;
|
||||
|
|
|
|||
|
|
@ -17,15 +17,15 @@ use rustc::hir;
|
|||
use rustc::hir::def_id::{DefId, DefIndex};
|
||||
use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::ty::adjustment::{Adjust, Adjustment};
|
||||
use rustc::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder};
|
||||
use rustc::ty::subst::UnpackedKind;
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use rustc::ty::adjustment::{Adjust, Adjustment};
|
||||
use rustc::ty::fold::{TypeFoldable, TypeFolder, BottomUpFolder};
|
||||
use rustc::util::nodemap::DefIdSet;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use std::mem;
|
||||
use syntax::ast;
|
||||
use syntax_pos::Span;
|
||||
use std::mem;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Entry point
|
||||
|
|
@ -55,8 +55,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
);
|
||||
debug!(
|
||||
"used_trait_imports({:?}) = {:?}",
|
||||
item_def_id,
|
||||
used_trait_imports
|
||||
item_def_id, used_trait_imports
|
||||
);
|
||||
wbcx.tables.used_trait_imports = used_trait_imports;
|
||||
|
||||
|
|
@ -64,8 +63,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
|
||||
debug!(
|
||||
"writeback: tables for {:?} are {:#?}",
|
||||
item_def_id,
|
||||
wbcx.tables
|
||||
item_def_id, wbcx.tables
|
||||
);
|
||||
|
||||
self.tcx.alloc_tables(wbcx.tables)
|
||||
|
|
@ -118,8 +116,8 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
|||
// operating on scalars, we clear the overload.
|
||||
fn fix_scalar_builtin_expr(&mut self, e: &hir::Expr) {
|
||||
match e.node {
|
||||
hir::ExprKind::Unary(hir::UnNeg, ref inner) |
|
||||
hir::ExprKind::Unary(hir::UnNot, ref inner) => {
|
||||
hir::ExprKind::Unary(hir::UnNeg, ref inner)
|
||||
| hir::ExprKind::Unary(hir::UnNot, ref inner) => {
|
||||
let inner_ty = self.fcx.node_ty(inner.hir_id);
|
||||
let inner_ty = self.fcx.resolve_type_vars_if_possible(&inner_ty);
|
||||
|
||||
|
|
@ -178,8 +176,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
|||
let index_ty = tables.expr_ty_adjusted(&index);
|
||||
let index_ty = self.fcx.resolve_type_vars_if_possible(&index_ty);
|
||||
|
||||
if base_ty.builtin_index().is_some()
|
||||
&& index_ty == self.fcx.tcx.types.usize {
|
||||
if base_ty.builtin_index().is_some() && index_ty == self.fcx.tcx.types.usize {
|
||||
// Remove the method call record
|
||||
tables.type_dependent_defs_mut().remove(e.hir_id);
|
||||
tables.node_substs_mut().remove(e.hir_id);
|
||||
|
|
@ -191,24 +188,26 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
|||
// of size information - we need to get rid of it
|
||||
// Since this is "after" the other adjustment to be
|
||||
// discarded, we do an extra `pop()`
|
||||
Some(Adjustment { kind: Adjust::Unsize, .. }) => {
|
||||
Some(Adjustment {
|
||||
kind: Adjust::Unsize,
|
||||
..
|
||||
}) => {
|
||||
// So the borrow discard actually happens here
|
||||
a.pop();
|
||||
},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
// Might encounter non-valid indexes at this point, so there
|
||||
// has to be a fall-through
|
||||
_ => {},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Impl of Visitor for Resolver
|
||||
//
|
||||
|
|
@ -262,7 +261,9 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> {
|
|||
if let Some(&bm) = self.fcx.tables.borrow().pat_binding_modes().get(p.hir_id) {
|
||||
self.tables.pat_binding_modes_mut().insert(p.hir_id, bm);
|
||||
} else {
|
||||
self.tcx().sess.delay_span_bug(p.span, "missing binding mode");
|
||||
self.tcx()
|
||||
.sess
|
||||
.delay_span_bug(p.span, "missing binding mode");
|
||||
}
|
||||
}
|
||||
hir::PatKind::Struct(_, ref fields, _) => {
|
||||
|
|
@ -310,8 +311,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
|||
};
|
||||
debug!(
|
||||
"Upvar capture for {:?} resolved to {:?}",
|
||||
upvar_id,
|
||||
new_upvar_capture
|
||||
upvar_id, new_upvar_capture
|
||||
);
|
||||
self.tables
|
||||
.upvar_capture_map
|
||||
|
|
@ -425,8 +425,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
|||
if subst == ty {
|
||||
// found it in the substitution list, replace with the
|
||||
// parameter from the existential type
|
||||
return self
|
||||
.tcx()
|
||||
return self.tcx()
|
||||
.global_tcx()
|
||||
.mk_ty_param(param.index, param.name);
|
||||
}
|
||||
|
|
@ -464,14 +463,16 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
|||
name: p.name,
|
||||
};
|
||||
trace!("replace {:?} with {:?}", region, reg);
|
||||
return self.tcx().global_tcx()
|
||||
return self.tcx()
|
||||
.global_tcx()
|
||||
.mk_region(ty::ReEarlyBound(reg));
|
||||
}
|
||||
}
|
||||
}
|
||||
trace!("anon_defn: {:#?}", anon_defn);
|
||||
trace!("generics: {:#?}", generics);
|
||||
self.tcx().sess
|
||||
self.tcx()
|
||||
.sess
|
||||
.struct_span_err(
|
||||
span,
|
||||
"non-defining existential type use in defining scope",
|
||||
|
|
@ -480,7 +481,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
|||
span,
|
||||
format!(
|
||||
"lifetime `{}` is part of concrete type but not used \
|
||||
in parameter list of existential type",
|
||||
in parameter list of existential type",
|
||||
region,
|
||||
),
|
||||
)
|
||||
|
|
@ -488,17 +489,27 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
|||
self.tcx().global_tcx().mk_region(ty::ReStatic)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
};
|
||||
|
||||
let old = self.tables.concrete_existential_types.insert(def_id, definition_ty);
|
||||
if let ty::Anon(defin_ty_def_id, _substs) = definition_ty.sty {
|
||||
if def_id == defin_ty_def_id {
|
||||
// Concrete type resolved to the existential type itself
|
||||
// Force a cycle error
|
||||
self.tcx().at(span).type_of(defin_ty_def_id);
|
||||
}
|
||||
}
|
||||
|
||||
let old = self.tables
|
||||
.concrete_existential_types
|
||||
.insert(def_id, definition_ty);
|
||||
if let Some(old) = old {
|
||||
if old != definition_ty {
|
||||
span_bug!(
|
||||
span,
|
||||
"visit_anon_types tried to write \
|
||||
different types for the same existential type: {:?}, {:?}, {:?}",
|
||||
different types for the same existential type: {:?}, {:?}, {:?}",
|
||||
def_id,
|
||||
definition_ty,
|
||||
old,
|
||||
|
|
@ -510,7 +521,12 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
|||
|
||||
fn visit_field_id(&mut self, node_id: ast::NodeId) {
|
||||
let hir_id = self.tcx().hir.node_to_hir_id(node_id);
|
||||
if let Some(index) = self.fcx.tables.borrow_mut().field_indices_mut().remove(hir_id) {
|
||||
if let Some(index) = self.fcx
|
||||
.tables
|
||||
.borrow_mut()
|
||||
.field_indices_mut()
|
||||
.remove(hir_id)
|
||||
{
|
||||
self.tables.field_indices_mut().insert(hir_id, index);
|
||||
}
|
||||
}
|
||||
|
|
@ -559,8 +575,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
|||
let resolved_adjustment = self.resolve(&adjustment, &span);
|
||||
debug!(
|
||||
"Adjustments for node {:?}: {:?}",
|
||||
hir_id,
|
||||
resolved_adjustment
|
||||
hir_id, resolved_adjustment
|
||||
);
|
||||
self.tables
|
||||
.adjustments_mut()
|
||||
|
|
@ -584,8 +599,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
|||
let resolved_adjustment = self.resolve(&adjustment, &span);
|
||||
debug!(
|
||||
"pat_adjustments for node {:?}: {:?}",
|
||||
hir_id,
|
||||
resolved_adjustment
|
||||
hir_id, resolved_adjustment
|
||||
);
|
||||
self.tables
|
||||
.pat_adjustments_mut()
|
||||
|
|
@ -701,7 +715,8 @@ impl<'cx, 'gcx, 'tcx> Resolver<'cx, 'gcx, 'tcx> {
|
|||
fn report_error(&self, t: Ty<'tcx>) {
|
||||
if !self.tcx.sess.has_errors() {
|
||||
self.infcx
|
||||
.need_type_info_err(Some(self.body.id()), self.span.to_span(&self.tcx), t).emit();
|
||||
.need_type_info_err(Some(self.body.id()), self.span.to_span(&self.tcx), t)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
31
src/test/ui/existential_types/nested_existential_types.rs
Normal file
31
src/test/ui/existential_types/nested_existential_types.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(existential_type)]
|
||||
// compile-pass
|
||||
mod my_mod {
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub existential type Foo: Debug;
|
||||
pub existential type Foot: Debug;
|
||||
|
||||
pub fn get_foo() -> Foo {
|
||||
5i32
|
||||
}
|
||||
|
||||
pub fn get_foot() -> Foot {
|
||||
get_foo()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _: my_mod::Foot = my_mod::get_foot();
|
||||
}
|
||||
|
||||
|
|
@ -1,10 +1,15 @@
|
|||
error[E0391]: cycle detected when normalizing `ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: All }, value: Foo }`
|
||||
error[E0391]: cycle detected when processing `Foo`
|
||||
--> $DIR/no_inferrable_concrete_type.rs:16:1
|
||||
|
|
||||
LL | existential type Foo: Copy; //~ cycle detected
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: ...which again requires normalizing `ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: All }, value: Foo }`, completing the cycle
|
||||
note: ...which requires processing `bar`...
|
||||
--> $DIR/no_inferrable_concrete_type.rs:19:23
|
||||
|
|
||||
LL | fn bar(x: Foo) -> Foo { x }
|
||||
| ^^^^^
|
||||
= note: ...which again requires processing `Foo`, completing the cycle
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue