enforce and report pick-constraint errors

The error message here is not great.
This commit is contained in:
Niko Matsakis 2019-06-03 16:31:44 -04:00
parent f0eebcd02f
commit d6ec0ae777
11 changed files with 323 additions and 65 deletions

View file

@ -53,6 +53,7 @@ use crate::infer::{self, SuppressRegionErrors};
use crate::hir;
use crate::hir::def_id::DefId;
use crate::hir::Node;
use crate::infer::opaque_types;
use crate::middle::region;
use crate::traits::{ObligationCause, ObligationCauseCode};
use crate::ty::error::TypeError;
@ -375,6 +376,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
);
}
}
RegionResolutionError::PickConstraintFailure {
opaque_type_def_id,
hidden_ty,
pick_region,
span: _,
option_regions: _,
} => {
let hidden_ty = self.resolve_vars_if_possible(&hidden_ty);
opaque_types::report_unexpected_hidden_region(
self.tcx,
Some(region_scope_tree),
opaque_type_def_id,
hidden_ty,
pick_region,
);
}
}
}
}
@ -411,7 +429,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e {
RegionResolutionError::GenericBoundFailure(..) => true,
RegionResolutionError::ConcreteFailure(..)
| RegionResolutionError::SubSupConflict(..) => false,
| RegionResolutionError::SubSupConflict(..)
| RegionResolutionError::PickConstraintFailure { .. } => false,
};
let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
@ -429,6 +448,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(),
RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _) => rvo.span(),
RegionResolutionError::PickConstraintFailure { span, .. } => span,
});
errors
}

View file

@ -1,5 +1,6 @@
//! Lexical region resolution.
use crate::hir::def_id::DefId;
use crate::infer::region_constraints::Constraint;
use crate::infer::region_constraints::GenericKind;
use crate::infer::region_constraints::PickConstraint;
@ -22,6 +23,7 @@ use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use smallvec::SmallVec;
use std::fmt;
use std::u32;
use syntax_pos::Span;
mod graphviz;
@ -81,6 +83,17 @@ pub enum RegionResolutionError<'tcx> {
SubregionOrigin<'tcx>,
Region<'tcx>,
),
/// Indicates a failure of a `PickConstraint`. These arise during
/// impl trait processing explicitly -- basically, the impl trait's hidden type
/// included some region that it was not supposed to.
PickConstraintFailure {
span: Span,
opaque_type_def_id: DefId,
hidden_ty: Ty<'tcx>,
pick_region: Region<'tcx>,
option_regions: Vec<Region<'tcx>>,
},
}
struct RegionAndOrigin<'tcx> {
@ -550,6 +563,24 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}
}
for pick_constraint in &self.data.pick_constraints {
let pick_region = var_data.normalize(self.tcx(), pick_constraint.pick_region);
let option_regions = pick_constraint
.option_regions
.iter()
.map(|&option_region| var_data.normalize(self.tcx(), option_region));
if !option_regions.clone().any(|option_region| pick_region == option_region) {
let span = self.tcx().def_span(pick_constraint.opaque_type_def_id);
errors.push(RegionResolutionError::PickConstraintFailure {
span,
opaque_type_def_id: pick_constraint.opaque_type_def_id,
hidden_ty: pick_constraint.hidden_ty,
pick_region,
option_regions: option_regions.collect(),
});
}
}
for verify in &self.data.verifys {
debug!("collect_errors: verify={:?}", verify);
let sub = var_data.normalize(self.tcx(), verify.region);

View file

@ -909,13 +909,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
/// the set `regions`.
pub fn pick_constraint(
&self,
origin: SubregionOrigin<'tcx>,
opaque_type_def_id: DefId,
hidden_ty: Ty<'tcx>,
region: ty::Region<'tcx>,
in_regions: &Rc<Vec<ty::Region<'tcx>>>,
) {
debug!("sub_regions({:?} <: {:?})", region, in_regions);
self.borrow_region_constraints()
.pick_constraint(origin, region, in_regions);
.pick_constraint(opaque_type_def_id, hidden_ty, region, in_regions);
}
pub fn subtype_predicate(

View file

@ -3,6 +3,7 @@ use crate::hir::def_id::DefId;
use crate::hir::Node;
use crate::infer::outlives::free_region_map::FreeRegionRelations;
use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin, TypeVariableOriginKind};
use crate::middle::region;
use crate::traits::{self, PredicateObligation};
use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor};
use crate::ty::subst::{InternalSubsts, Kind, SubstsRef, UnpackedKind};
@ -10,7 +11,6 @@ use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt};
use crate::util::nodemap::DefIdMap;
use rustc_data_structures::fx::FxHashMap;
use std::rc::Rc;
use syntax::source_map::Span;
pub type OpaqueTypeMap<'tcx> = DefIdMap<OpaqueTypeDecl<'tcx>>;
@ -374,11 +374,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// we will create a "in bound" like `'r in
// ['a, 'b, 'c]`, where `'a..'c` are the
// regions that appear in the impl trait.
return self.generate_in_constraint(
span,
return self.generate_pick_constraint(
concrete_ty,
abstract_type_generics,
opaque_defn,
def_id,
);
}
}
@ -399,14 +399,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
/// related, we would generate a constraint `'r in ['a, 'b,
/// 'static]` for each region `'r` that appears in the hidden type
/// (i.e., it must be equal to `'a`, `'b`, or `'static`).
fn generate_in_constraint(
fn generate_pick_constraint(
&self,
span: Span,
concrete_ty: Ty<'tcx>,
abstract_type_generics: &ty::Generics,
opaque_defn: &OpaqueTypeDecl<'tcx>,
opaque_type_def_id: DefId,
) {
let in_regions: Rc<Vec<ty::Region<'tcx>>> = Rc::new(
// Create the set of option regions: each region in the hidden
// type can be equal to any of the region parameters of the
// opaque type definition.
let option_regions: Rc<Vec<ty::Region<'tcx>>> = Rc::new(
abstract_type_generics
.params
.iter()
@ -421,7 +424,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
tcx: self.tcx,
op: |r| self.pick_constraint(infer::CallReturn(span), r, &in_regions),
op: |r| self.pick_constraint(
opaque_type_def_id,
concrete_ty,
r,
&option_regions,
),
});
}
@ -445,7 +453,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
/// # Parameters
///
/// - `def_id`, the `impl Trait` type
/// - `opaque_defn`, the opaque definition created in `instantiate_opaque_types`
/// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of
/// `opaque_defn.concrete_ty`
@ -492,6 +499,83 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
}
pub fn report_unexpected_hidden_region(
tcx: TyCtxt<'_, '_, 'tcx>,
region_scope_tree: Option<&region::ScopeTree>,
opaque_type_def_id: DefId,
hidden_ty: Ty<'tcx>,
hidden_region: ty::Region<'tcx>,
) {
let span = tcx.def_span(opaque_type_def_id);
let mut err = struct_span_err!(
tcx.sess,
span,
E0700,
"hidden type for `impl Trait` captures lifetime that does not appear in bounds",
);
// Explain the region we are capturing.
if let ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty = hidden_region {
// Assuming regionck succeeded (*), we ought to always be
// capturing *some* region from the fn header, and hence it
// ought to be free. So under normal circumstances, we will go
// down this path which gives a decent human readable
// explanation.
//
// (*) if not, the `tainted_by_errors` flag would be set to
// true in any case, so we wouldn't be here at all.
tcx.note_and_explain_free_region(
&mut err,
&format!("hidden type `{}` captures ", hidden_ty),
hidden_region,
"",
);
} else {
// Ugh. This is a painful case: the hidden region is not one
// that we can easily summarize or explain. This can happens
// in a case like
// `src/test/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`:
//
// ```
// fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> {
// if condition() { a } else { b }
// }
// ```
//
// Here the captured lifetime is the intersection of `'a` and
// `'b`, which we can't quite express. This prticulararticular
// is kind of an unfortunate error anyway.
if let Some(region_scope_tree) = region_scope_tree {
// If the `region_scope_tree` is available, this is being
// invoked from the "region inferencer error". We can at
// least report a really cryptic error for now.
tcx.note_and_explain_region(
region_scope_tree,
&mut err,
&format!("hidden type `{}` captures ", hidden_ty),
hidden_region,
"",
);
} else {
// If the `region_scope_tree` is *unavailable*, this is
// being invoked by the code that comes *after* region
// inferencing. This is a bug, as the region inferencer
// ought to have noticed the failed constraint and invoked
// error reporting, which in turn should have prevented us
// from getting trying to infer the hidden type
// completely.
span_bug!(
span,
"hidden type captures unexpected lifetime `{:?}` but no region inference failure",
hidden_region,
);
}
}
err.emit();
}
// Visitor that requires that (almost) all regions in the type visited outlive
// `least_region`. We cannot use `push_outlives_components` because regions in
// closure signatures are not included in their outlives components. We need to
@ -640,45 +724,13 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> {
None => {
if !self.map_missing_regions_to_empty && !self.tainted_by_errors {
if let Some(hidden_ty) = self.hidden_ty.take() {
let span = self.tcx.def_span(self.opaque_type_def_id);
let mut err = struct_span_err!(
self.tcx.sess,
span,
E0700,
"hidden type for `impl Trait` captures lifetime that \
does not appear in bounds",
report_unexpected_hidden_region(
self.tcx,
None,
self.opaque_type_def_id,
hidden_ty,
r,
);
// Explain the region we are capturing.
match r {
ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty => {
// Assuming regionck succeeded (*), we
// ought to always be capturing *some* region
// from the fn header, and hence it ought to
// be free. So under normal circumstances, we will
// go down this path which gives a decent human readable
// explanation.
//
// (*) if not, the `tainted_by_errors`
// flag would be set to true in any
// case, so we wouldn't be here at
// all.
self.tcx.note_and_explain_free_region(
&mut err,
&format!("hidden type `{}` captures ", hidden_ty),
r,
"",
);
}
_ => {
// This case should not happen: it indicates that regionck
// failed to enforce an "in constraint".
err.note(&format!("hidden type `{}` captures `{:?}`", hidden_ty, r));
err.note(&format!("this is likely a bug in the compiler, please file an issue on github"));
}
}
err.emit();
}
}
self.tcx.lifetimes.re_empty

View file

@ -9,6 +9,7 @@ use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::unify as ut;
use crate::hir::def_id::DefId;
use crate::ty::ReStatic;
use crate::ty::{self, Ty, TyCtxt};
use crate::ty::{ReLateBound, ReVar};
@ -151,8 +152,16 @@ impl Constraint<'_> {
/// ```
#[derive(Debug, Clone)]
pub struct PickConstraint<'tcx> {
pub origin: SubregionOrigin<'tcx>,
/// the def-id of the opaque type causing this constraint: used for error reporting
pub opaque_type_def_id: DefId,
/// the hidden type in which `pick_region` appears: used for error reporting
pub hidden_ty: Ty<'tcx>,
/// the region R0
pub pick_region: Region<'tcx>,
/// the options O1..On
pub option_regions: Rc<Vec<Region<'tcx>>>,
}
@ -664,7 +673,8 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
pub fn pick_constraint(
&mut self,
origin: SubregionOrigin<'tcx>,
opaque_type_def_id: DefId,
hidden_ty: Ty<'tcx>,
pick_region: ty::Region<'tcx>,
option_regions: &Rc<Vec<ty::Region<'tcx>>>,
) {
@ -675,7 +685,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
}
self.data.pick_constraints.push(PickConstraint {
origin, pick_region, option_regions: option_regions.clone()
opaque_type_def_id,
hidden_ty,
pick_region,
option_regions: option_regions.clone()
});
}

View file

@ -1,15 +1,25 @@
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
error: impl Trait captures unexpected lifetime
--> $DIR/inverse-bounds.rs:15:70
|
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e>
| ^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured
|
note: hidden type `Invert<'c>` captures the lifetime 'c as defined on the function body at 15:25
--> $DIR/inverse-bounds.rs:15:25
note: hidden type captures the lifetime 'd as defined on the function body at 15:29
--> $DIR/inverse-bounds.rs:15:29
|
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e>
| ^^
| ^^
note: hidden type would be allowed to capture the lifetime 'd as defined on the function body at 15:29
--> $DIR/inverse-bounds.rs:15:29
|
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e>
| ^^
note: hidden type would be allowed to capture the lifetime 'e as defined on the function body at 15:33
--> $DIR/inverse-bounds.rs:15:33
|
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e>
| ^^
= note: hidden type would be allowed to capture the static lifetime
error: aborting due to previous error
For more information about this error, try `rustc --explain E0700`.

View file

@ -0,0 +1,48 @@
error: impl Trait captures unexpected lifetime
--> $DIR/ordinary-bounds-pick-original.rs:11:50
|
LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
| ^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured
|
note: hidden type captures the lifetime 'a as defined on the function body at 11:17
--> $DIR/ordinary-bounds-pick-original.rs:11:17
|
LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
| ^^
note: hidden type would be allowed to capture the lifetime 'a as defined on the function body at 11:17
--> $DIR/ordinary-bounds-pick-original.rs:11:17
|
LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
| ^^
note: hidden type would be allowed to capture the lifetime 'b as defined on the function body at 11:21
--> $DIR/ordinary-bounds-pick-original.rs:11:21
|
LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
| ^^
= note: hidden type would be allowed to capture the static lifetime
error: impl Trait captures unexpected lifetime
--> $DIR/ordinary-bounds-pick-original.rs:11:50
|
LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
| ^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured
|
note: hidden type captures the lifetime 'b as defined on the function body at 11:21
--> $DIR/ordinary-bounds-pick-original.rs:11:21
|
LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
| ^^
note: hidden type would be allowed to capture the lifetime 'a as defined on the function body at 11:17
--> $DIR/ordinary-bounds-pick-original.rs:11:17
|
LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
| ^^
note: hidden type would be allowed to capture the lifetime 'b as defined on the function body at 11:21
--> $DIR/ordinary-bounds-pick-original.rs:11:21
|
LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
| ^^
= note: hidden type would be allowed to capture the static lifetime
error: aborting due to 2 previous errors

View file

@ -0,0 +1,25 @@
error: impl Trait captures unexpected lifetime
--> $DIR/ordinary-bounds-pick-other.rs:18:74
|
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e>
| ^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured
|
note: hidden type captures the lifetime 'e as defined on the function body at 18:33
--> $DIR/ordinary-bounds-pick-other.rs:18:33
|
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e>
| ^^
note: hidden type would be allowed to capture the lifetime 'd as defined on the function body at 18:29
--> $DIR/ordinary-bounds-pick-other.rs:18:29
|
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e>
| ^^
note: hidden type would be allowed to capture the lifetime 'e as defined on the function body at 18:33
--> $DIR/ordinary-bounds-pick-other.rs:18:33
|
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e>
| ^^
= note: hidden type would be allowed to capture the static lifetime
error: aborting due to previous error

View file

@ -4,8 +4,17 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appea
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e>
| ^^^^^^^^^^^^^^^^^^
|
= note: hidden type `Ordinary<'_>` captures `ReScope(CallSite(24))`
= note: this is likely a bug in the compiler, please file an issue on github
note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 23:1
--> $DIR/ordinary-bounds-unrelated.rs:23:1
|
LL | / {
LL | | // Hidden type `Ordinary<'0>` with constraints:
LL | | //
LL | | // ```
... |
LL | | if condition() { a } else { b }
LL | | }
| |_^
error: aborting due to previous error

View file

@ -4,8 +4,17 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appea
LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b>
| ^^^^^^^^^^^^^^^^^^
|
= note: hidden type `Ordinary<'_>` captures `ReScope(CallSite(24))`
= note: this is likely a bug in the compiler, please file an issue on github
note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 22:1
--> $DIR/ordinary-bounds-unsuited.rs:22:1
|
LL | / {
LL | | // We return a value:
LL | | //
LL | | // ```
... |
LL | | if condition() { a } else { b }
LL | | }
| |_^
error: aborting due to previous error

View file

@ -1,8 +1,48 @@
error: ambiguous lifetime bound in `impl Trait`
error: impl Trait captures unexpected lifetime
--> $DIR/needs_least_region_or_bound.rs:6:55
|
LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ neither `'a` nor `'b` outlives the other
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured
|
note: hidden type captures the lifetime 'a as defined on the function body at 6:20
--> $DIR/needs_least_region_or_bound.rs:6:20
|
LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> {
| ^^
note: hidden type would be allowed to capture the lifetime 'a as defined on the function body at 6:20
--> $DIR/needs_least_region_or_bound.rs:6:20
|
LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> {
| ^^
note: hidden type would be allowed to capture the lifetime 'b as defined on the function body at 6:24
--> $DIR/needs_least_region_or_bound.rs:6:24
|
LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> {
| ^^
= note: hidden type would be allowed to capture the static lifetime
error: aborting due to previous error
error: impl Trait captures unexpected lifetime
--> $DIR/needs_least_region_or_bound.rs:6:55
|
LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured
|
note: hidden type captures the lifetime 'b as defined on the function body at 6:24
--> $DIR/needs_least_region_or_bound.rs:6:24
|
LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> {
| ^^
note: hidden type would be allowed to capture the lifetime 'a as defined on the function body at 6:20
--> $DIR/needs_least_region_or_bound.rs:6:20
|
LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> {
| ^^
note: hidden type would be allowed to capture the lifetime 'b as defined on the function body at 6:24
--> $DIR/needs_least_region_or_bound.rs:6:24
|
LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> {
| ^^
= note: hidden type would be allowed to capture the static lifetime
error: aborting due to 2 previous errors