mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 07:14:28 +00:00
Auto merge of #27641 - nikomatsakis:soundness-rfc-1214, r=nrc
This PR implements the majority of RFC 1214. In particular, it implements: - the new outlives relation - comprehensive WF checking For the most part, new code receives warnings, not errors, though 3 regressions were found via a crater run. There are some deviations from RFC 1214. Most notably: - we still consider implied bounds from fn ret; this intersects other soundness issues that I intend to address in detail in a follow-up RFC. Fixing this without breaking a lot of code probably requires rewriting compare-method somewhat (which is probably a good thing). - object types do not check trait bounds for fear of encountering `Self`; this was left as an unresolved question in RFC 1214, but ultimately feels inconsistent. Both of those two issues are highlighted in the tracking issue, https://github.com/rust-lang/rust/issues/27579. #27579 also includes a testing matrix with new tests that I wrote -- these probably duplicate some existing tests, I tried to check but wasn't quite sure what to look for. I tried to be thorough in testing the WF relation, at least, but would welcome suggestions for missing tests. r? @nrc (or perhaps someone else?)
This commit is contained in:
commit
e7261f3ab6
@ -2135,7 +2135,7 @@ impl<I: DoubleEndedIterator, F> DoubleEndedIterator for Inspect<I, F>
|
||||
/// two `Step` objects.
|
||||
#[unstable(feature = "step_trait",
|
||||
reason = "likely to be replaced by finer-grained traits")]
|
||||
pub trait Step: PartialOrd {
|
||||
pub trait Step: PartialOrd+Sized {
|
||||
/// Steps `self` if possible.
|
||||
fn step(&self, by: &Self) -> Option<Self>;
|
||||
|
||||
|
@ -56,7 +56,7 @@ pub trait Sized {
|
||||
/// Types that can be "unsized" to a dynamically sized type.
|
||||
#[unstable(feature = "unsize")]
|
||||
#[lang="unsize"]
|
||||
pub trait Unsize<T> {
|
||||
pub trait Unsize<T: ?Sized> {
|
||||
// Empty.
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ use char::CharExt;
|
||||
use cmp::{Eq, PartialOrd};
|
||||
use fmt;
|
||||
use intrinsics;
|
||||
use marker::Copy;
|
||||
use marker::{Copy, Sized};
|
||||
use mem::size_of;
|
||||
use option::Option::{self, Some, None};
|
||||
use result::Result::{self, Ok, Err};
|
||||
@ -1263,7 +1263,7 @@ pub enum FpCategory {
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "core_float",
|
||||
reason = "stable interface is via `impl f{32,64}` in later crates")]
|
||||
pub trait Float {
|
||||
pub trait Float: Sized {
|
||||
/// Returns the NaN value.
|
||||
fn nan() -> Self;
|
||||
/// Returns the infinite value.
|
||||
|
@ -25,6 +25,7 @@ use default::Default;
|
||||
use fmt;
|
||||
use iter::ExactSizeIterator;
|
||||
use iter::{Map, Iterator, DoubleEndedIterator};
|
||||
use marker::Sized;
|
||||
use mem;
|
||||
use ops::{Fn, FnMut, FnOnce};
|
||||
use option::Option::{self, None, Some};
|
||||
@ -37,7 +38,7 @@ pub mod pattern;
|
||||
/// A trait to abstract the idea of creating a new instance of a type from a
|
||||
/// string.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait FromStr {
|
||||
pub trait FromStr: Sized {
|
||||
/// The associated error which can be returned from parsing.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
type Err;
|
||||
|
@ -561,7 +561,7 @@ pub type Edges<'a,E> = Cow<'a,[E]>;
|
||||
/// `Cow<[T]>` to leave implementers the freedom to create
|
||||
/// entirely new vectors or to pass back slices into internally owned
|
||||
/// vectors.
|
||||
pub trait GraphWalk<'a, N, E> {
|
||||
pub trait GraphWalk<'a, N: Clone, E: Clone> {
|
||||
/// Returns all the nodes in this graph.
|
||||
fn nodes(&'a self) -> Nodes<'a, N>;
|
||||
/// Returns all of the edges in this graph.
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
// this is surprisingly complicated to be both generic & correct
|
||||
|
||||
use core::marker::Sized;
|
||||
use Rng;
|
||||
use distributions::{Sample, IndependentSample};
|
||||
|
||||
@ -57,7 +58,7 @@ impl<Sup: SampleRange> IndependentSample<Sup> for Range<Sup> {
|
||||
/// uniformly between two values. This should not be used directly,
|
||||
/// and is only to facilitate `Range`.
|
||||
#[doc(hidden)]
|
||||
pub trait SampleRange {
|
||||
pub trait SampleRange: Sized {
|
||||
/// Construct the `Range` object that `sample_range`
|
||||
/// requires. This should not ever be called directly, only via
|
||||
/// `Range::new`, which will check that `low < high`, so this
|
||||
|
@ -129,6 +129,7 @@ pub mod middle {
|
||||
pub mod lang_items;
|
||||
pub mod liveness;
|
||||
pub mod mem_categorization;
|
||||
pub mod outlives;
|
||||
pub mod pat_util;
|
||||
pub mod privacy;
|
||||
pub mod reachable;
|
||||
@ -143,6 +144,7 @@ pub mod middle {
|
||||
pub mod ty_match;
|
||||
pub mod ty_relate;
|
||||
pub mod ty_walk;
|
||||
pub mod wf;
|
||||
pub mod weak_lang_items;
|
||||
}
|
||||
|
||||
|
@ -790,6 +790,12 @@ fn parse_predicate_<'a,'tcx, F>(st: &mut PState<'a, 'tcx>,
|
||||
'o' => ty::Binder(ty::OutlivesPredicate(parse_ty_(st, conv),
|
||||
parse_region_(st, conv))).to_predicate(),
|
||||
'p' => ty::Binder(parse_projection_predicate_(st, conv)).to_predicate(),
|
||||
'w' => ty::Predicate::WellFormed(parse_ty_(st, conv)),
|
||||
'O' => {
|
||||
let def_id = parse_def_(st, NominalType, conv);
|
||||
assert_eq!(next(st), '|');
|
||||
ty::Predicate::ObjectSafe(def_id)
|
||||
}
|
||||
c => panic!("Encountered invalid character in metadata: {}", c)
|
||||
}
|
||||
}
|
||||
|
@ -457,6 +457,13 @@ pub fn enc_predicate<'a, 'tcx>(w: &mut Encoder,
|
||||
mywrite!(w, "p");
|
||||
enc_projection_predicate(w, cx, data)
|
||||
}
|
||||
ty::Predicate::WellFormed(data) => {
|
||||
mywrite!(w, "w");
|
||||
enc_ty(w, cx, data);
|
||||
}
|
||||
ty::Predicate::ObjectSafe(trait_def_id) => {
|
||||
mywrite!(w, "O{}|", (cx.ds)(trait_def_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1061,7 +1061,7 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
|
||||
}
|
||||
}
|
||||
|
||||
trait doc_decoder_helpers {
|
||||
trait doc_decoder_helpers: Sized {
|
||||
fn as_int(&self) -> isize;
|
||||
fn opt_child(&self, tag: c::astencode_tag) -> Option<Self>;
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ impl OverloadedCallType {
|
||||
// supplies types from the tree. After type checking is complete, you
|
||||
// can just use the tcx as the typer.
|
||||
|
||||
pub struct ExprUseVisitor<'d,'t,'a: 't, 'tcx:'a> {
|
||||
pub struct ExprUseVisitor<'d, 't, 'a: 't, 'tcx:'a+'d> {
|
||||
typer: &'t infer::InferCtxt<'a, 'tcx>,
|
||||
mc: mc::MemCategorizationContext<'t, 'a, 'tcx>,
|
||||
delegate: &'d mut (Delegate<'tcx>+'d),
|
||||
@ -273,7 +273,8 @@ enum PassArgs {
|
||||
impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
|
||||
pub fn new(delegate: &'d mut Delegate<'tcx>,
|
||||
typer: &'t infer::InferCtxt<'a, 'tcx>)
|
||||
-> ExprUseVisitor<'d,'t,'a, 'tcx> {
|
||||
-> ExprUseVisitor<'d,'t,'a,'tcx>
|
||||
{
|
||||
ExprUseVisitor {
|
||||
typer: typer,
|
||||
mc: mc::MemCategorizationContext::new(typer),
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
//! This file defines
|
||||
|
||||
use middle::implicator::Implication;
|
||||
use middle::wf::ImpliedBound;
|
||||
use middle::ty::{self, FreeRegion};
|
||||
use util::common::can_reach;
|
||||
use util::nodemap::{FnvHashMap, FnvHashSet};
|
||||
@ -30,18 +30,19 @@ impl FreeRegionMap {
|
||||
FreeRegionMap { map: FnvHashMap(), statics: FnvHashSet() }
|
||||
}
|
||||
|
||||
pub fn relate_free_regions_from_implications<'tcx>(&mut self,
|
||||
implications: &[Implication<'tcx>])
|
||||
pub fn relate_free_regions_from_implied_bounds<'tcx>(&mut self,
|
||||
implied_bounds: &[ImpliedBound<'tcx>])
|
||||
{
|
||||
for implication in implications {
|
||||
debug!("implication: {:?}", implication);
|
||||
match *implication {
|
||||
Implication::RegionSubRegion(_, ty::ReFree(free_a), ty::ReFree(free_b)) => {
|
||||
debug!("relate_free_regions_from_implied_bounds()");
|
||||
for implied_bound in implied_bounds {
|
||||
debug!("implied bound: {:?}", implied_bound);
|
||||
match *implied_bound {
|
||||
ImpliedBound::RegionSubRegion(ty::ReFree(free_a), ty::ReFree(free_b)) => {
|
||||
self.relate_free_regions(free_a, free_b);
|
||||
}
|
||||
Implication::RegionSubRegion(..) |
|
||||
Implication::RegionSubGeneric(..) |
|
||||
Implication::Predicate(..) => {
|
||||
ImpliedBound::RegionSubRegion(..) |
|
||||
ImpliedBound::RegionSubParam(..) |
|
||||
ImpliedBound::RegionSubProjection(..) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,6 +57,8 @@ impl FreeRegionMap {
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::Trait(..) |
|
||||
ty::Predicate::Equate(..) |
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::TypeOutlives(..) => {
|
||||
// No region bounds here
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
use middle::infer::{InferCtxt, GenericKind};
|
||||
use middle::subst::Substs;
|
||||
use middle::traits;
|
||||
use middle::ty::{self, RegionEscape, ToPolyTraitRef, ToPredicate, Ty};
|
||||
use middle::ty::{self, RegionEscape, ToPredicate, Ty};
|
||||
use middle::ty_fold::{TypeFoldable, TypeFolder};
|
||||
|
||||
use syntax::ast;
|
||||
@ -278,9 +278,7 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> {
|
||||
|
||||
for predicate in predicates.predicates.as_slice() {
|
||||
match *predicate {
|
||||
ty::Predicate::Trait(ref data) => {
|
||||
self.accumulate_from_assoc_types_transitive(data);
|
||||
}
|
||||
ty::Predicate::Trait(..) => { }
|
||||
ty::Predicate::Equate(..) => { }
|
||||
ty::Predicate::Projection(..) => { }
|
||||
ty::Predicate::RegionOutlives(ref data) => {
|
||||
@ -301,6 +299,9 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::Predicate::ObjectSafe(_) |
|
||||
ty::Predicate::WellFormed(_) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,53 +350,6 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Given that there is a requirement that `Foo<X> : 'a`, where
|
||||
/// `Foo` is declared like `struct Foo<T> where T : SomeTrait`,
|
||||
/// this code finds all the associated types defined in
|
||||
/// `SomeTrait` (and supertraits) and adds a requirement that `<X
|
||||
/// as SomeTrait>::N : 'a` (where `N` is some associated type
|
||||
/// defined in `SomeTrait`). This rule only applies to
|
||||
/// trait-bounds that are not higher-ranked, because we cannot
|
||||
/// project out of a HRTB. This rule helps code using associated
|
||||
/// types to compile, see Issue #22246 for an example.
|
||||
fn accumulate_from_assoc_types_transitive(&mut self,
|
||||
data: &ty::PolyTraitPredicate<'tcx>)
|
||||
{
|
||||
debug!("accumulate_from_assoc_types_transitive({:?})",
|
||||
data);
|
||||
|
||||
for poly_trait_ref in traits::supertraits(self.tcx(), data.to_poly_trait_ref()) {
|
||||
match self.tcx().no_late_bound_regions(&poly_trait_ref) {
|
||||
Some(trait_ref) => { self.accumulate_from_assoc_types(trait_ref); }
|
||||
None => { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn accumulate_from_assoc_types(&mut self,
|
||||
trait_ref: ty::TraitRef<'tcx>)
|
||||
{
|
||||
debug!("accumulate_from_assoc_types({:?})",
|
||||
trait_ref);
|
||||
|
||||
let trait_def_id = trait_ref.def_id;
|
||||
let trait_def = self.tcx().lookup_trait_def(trait_def_id);
|
||||
let assoc_type_projections: Vec<_> =
|
||||
trait_def.associated_type_names
|
||||
.iter()
|
||||
.map(|&name| self.tcx().mk_projection(trait_ref.clone(), name))
|
||||
.collect();
|
||||
debug!("accumulate_from_assoc_types: assoc_type_projections={:?}",
|
||||
assoc_type_projections);
|
||||
let tys = match self.fully_normalize(&assoc_type_projections) {
|
||||
Ok(tys) => { tys }
|
||||
Err(ErrorReported) => { return; }
|
||||
};
|
||||
for ty in tys {
|
||||
self.accumulate_from_ty(ty);
|
||||
}
|
||||
}
|
||||
|
||||
fn accumulate_from_object_ty(&mut self,
|
||||
ty: Ty<'tcx>,
|
||||
region_bound: ty::Region,
|
||||
|
@ -239,8 +239,7 @@ pub trait ErrorReporting<'tcx> {
|
||||
fn report_generic_bound_failure(&self,
|
||||
origin: SubregionOrigin<'tcx>,
|
||||
kind: GenericKind<'tcx>,
|
||||
sub: Region,
|
||||
sups: Vec<Region>);
|
||||
sub: Region);
|
||||
|
||||
fn report_sub_sup_conflict(&self,
|
||||
var_origin: RegionVariableOrigin,
|
||||
@ -292,8 +291,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
self.report_concrete_failure(origin, sub, sup);
|
||||
}
|
||||
|
||||
GenericBoundFailure(kind, param_ty, sub, sups) => {
|
||||
self.report_generic_bound_failure(kind, param_ty, sub, sups);
|
||||
GenericBoundFailure(kind, param_ty, sub) => {
|
||||
self.report_generic_bound_failure(kind, param_ty, sub);
|
||||
}
|
||||
|
||||
SubSupConflict(var_origin,
|
||||
@ -527,14 +526,18 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
fn report_generic_bound_failure(&self,
|
||||
origin: SubregionOrigin<'tcx>,
|
||||
bound_kind: GenericKind<'tcx>,
|
||||
sub: Region,
|
||||
_sups: Vec<Region>)
|
||||
sub: Region)
|
||||
{
|
||||
// FIXME: it would be better to report the first error message
|
||||
// with the span of the parameter itself, rather than the span
|
||||
// where the error was detected. But that span is not readily
|
||||
// accessible.
|
||||
|
||||
let is_warning = match origin {
|
||||
infer::RFC1214Subregion(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let labeled_user_string = match bound_kind {
|
||||
GenericKind::Param(ref p) =>
|
||||
format!("the parameter type `{}`", p),
|
||||
@ -545,7 +548,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
match sub {
|
||||
ty::ReFree(ty::FreeRegion {bound_region: ty::BrNamed(..), ..}) => {
|
||||
// Does the required lifetime have a nice name we can print?
|
||||
span_err!(self.tcx.sess, origin.span(), E0309,
|
||||
span_err_or_warn!(
|
||||
is_warning, self.tcx.sess, origin.span(), E0309,
|
||||
"{} may not live long enough", labeled_user_string);
|
||||
self.tcx.sess.fileline_help(
|
||||
origin.span(),
|
||||
@ -557,7 +561,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
|
||||
ty::ReStatic => {
|
||||
// Does the required lifetime have a nice name we can print?
|
||||
span_err!(self.tcx.sess, origin.span(), E0310,
|
||||
span_err_or_warn!(
|
||||
is_warning, self.tcx.sess, origin.span(), E0310,
|
||||
"{} may not live long enough", labeled_user_string);
|
||||
self.tcx.sess.fileline_help(
|
||||
origin.span(),
|
||||
@ -568,9 +573,10 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
|
||||
_ => {
|
||||
// If not, be less specific.
|
||||
span_err!(self.tcx.sess, origin.span(), E0311,
|
||||
"{} may not live long enough",
|
||||
labeled_user_string);
|
||||
span_err_or_warn!(
|
||||
is_warning, self.tcx.sess, origin.span(), E0311,
|
||||
"{} may not live long enough",
|
||||
labeled_user_string);
|
||||
self.tcx.sess.fileline_help(
|
||||
origin.span(),
|
||||
&format!(
|
||||
@ -583,6 +589,10 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
if is_warning {
|
||||
self.tcx.sess.note_rfc_1214(origin.span());
|
||||
}
|
||||
|
||||
self.note_region_origin(&origin);
|
||||
}
|
||||
|
||||
@ -591,6 +601,13 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
sub: Region,
|
||||
sup: Region) {
|
||||
match origin {
|
||||
infer::RFC1214Subregion(ref suborigin) => {
|
||||
// Ideally, this would be a warning, but it doesn't
|
||||
// seem to come up in practice, since the changes from
|
||||
// RFC1214 mostly trigger errors in type definitions
|
||||
// that don't wind up coming down this path.
|
||||
self.report_concrete_failure((**suborigin).clone(), sub, sup);
|
||||
}
|
||||
infer::Subtype(trace) => {
|
||||
let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
|
||||
self.report_and_explain_type_error(trace, &terr);
|
||||
@ -819,6 +836,23 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
sup,
|
||||
"");
|
||||
}
|
||||
infer::ParameterInScope(_, span) => {
|
||||
self.tcx.sess.span_err(
|
||||
span,
|
||||
&format!("type/lifetime parameter not in scope here"));
|
||||
self.tcx.note_and_explain_region(
|
||||
"the parameter is only valid for ",
|
||||
sub,
|
||||
"");
|
||||
}
|
||||
infer::DataBorrowed(ty, span) => {
|
||||
self.tcx.sess.span_err(
|
||||
span,
|
||||
&format!("a value of type `{}` is borrowed for too long",
|
||||
self.ty_to_string(ty)));
|
||||
self.tcx.note_and_explain_region("the type is valid for ", sub, "");
|
||||
self.tcx.note_and_explain_region("but the borrow lasts for ", sup, "");
|
||||
}
|
||||
infer::ReferenceOutlivesReferent(ty, span) => {
|
||||
self.tcx.sess.span_err(
|
||||
span,
|
||||
@ -1567,6 +1601,9 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
|
||||
fn note_region_origin(&self, origin: &SubregionOrigin<'tcx>) {
|
||||
match *origin {
|
||||
infer::RFC1214Subregion(ref suborigin) => {
|
||||
self.note_region_origin(suborigin);
|
||||
}
|
||||
infer::Subtype(ref trace) => {
|
||||
let desc = match trace.origin {
|
||||
infer::Misc(_) => {
|
||||
@ -1714,6 +1751,17 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
span,
|
||||
"...so that variable is valid at time of its declaration");
|
||||
}
|
||||
infer::ParameterInScope(_, span) => {
|
||||
self.tcx.sess.span_note(
|
||||
span,
|
||||
&format!("...so that a type/lifetime parameter is in scope here"));
|
||||
}
|
||||
infer::DataBorrowed(ty, span) => {
|
||||
self.tcx.sess.span_note(
|
||||
span,
|
||||
&format!("...so that the type `{}` is not borrowed for too long",
|
||||
self.ty_to_string(ty)));
|
||||
}
|
||||
infer::ReferenceOutlivesReferent(ty, span) => {
|
||||
self.tcx.sess.span_note(
|
||||
span,
|
||||
|
@ -17,7 +17,7 @@ pub use self::TypeOrigin::*;
|
||||
pub use self::ValuePairs::*;
|
||||
pub use middle::ty::IntVarValue;
|
||||
pub use self::freshen::TypeFreshener;
|
||||
pub use self::region_inference::GenericKind;
|
||||
pub use self::region_inference::{GenericKind, VerifyBound};
|
||||
|
||||
use middle::free_region::FreeRegionMap;
|
||||
use middle::mem_categorization as mc;
|
||||
@ -35,6 +35,7 @@ use middle::ty_relate::{Relate, RelateResult, TypeRelation};
|
||||
use rustc_data_structures::unify::{self, UnificationTable};
|
||||
use std::cell::{RefCell, Ref};
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
use syntax::ast;
|
||||
use syntax::codemap;
|
||||
use syntax::codemap::{Span, DUMMY_SP};
|
||||
@ -188,6 +189,11 @@ pub struct TypeTrace<'tcx> {
|
||||
/// See `error_reporting.rs` for more details
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SubregionOrigin<'tcx> {
|
||||
// Marker to indicate a constraint that only arises due to new
|
||||
// provisions from RFC 1214. This will result in a warning, not an
|
||||
// error.
|
||||
RFC1214Subregion(Rc<SubregionOrigin<'tcx>>),
|
||||
|
||||
// Arose from a subtyping relation
|
||||
Subtype(TypeTrace<'tcx>),
|
||||
|
||||
@ -229,9 +235,15 @@ pub enum SubregionOrigin<'tcx> {
|
||||
// Creating a pointer `b` to contents of an upvar
|
||||
ReborrowUpvar(Span, ty::UpvarId),
|
||||
|
||||
// Data with type `Ty<'tcx>` was borrowed
|
||||
DataBorrowed(Ty<'tcx>, Span),
|
||||
|
||||
// (&'a &'b T) where a >= b
|
||||
ReferenceOutlivesReferent(Ty<'tcx>, Span),
|
||||
|
||||
// Type or region parameters must be in scope.
|
||||
ParameterInScope(ParameterOrigin, Span),
|
||||
|
||||
// The type T of an expression E must outlive the lifetime for E.
|
||||
ExprTypeIsNotInScope(Ty<'tcx>, Span),
|
||||
|
||||
@ -260,6 +272,15 @@ pub enum SubregionOrigin<'tcx> {
|
||||
SafeDestructor(Span),
|
||||
}
|
||||
|
||||
/// Places that type/region parameters can appear.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ParameterOrigin {
|
||||
Path, // foo::bar
|
||||
MethodCall, // foo.bar() <-- parameters on impl providing bar()
|
||||
OverloadedOperator, // a + b when overloaded
|
||||
OverloadedDeref, // *a when overloaded
|
||||
}
|
||||
|
||||
/// Times when we replace late-bound regions with variables:
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum LateBoundRegionConversionTime {
|
||||
@ -1398,13 +1419,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
origin: SubregionOrigin<'tcx>,
|
||||
kind: GenericKind<'tcx>,
|
||||
a: ty::Region,
|
||||
bs: Vec<ty::Region>) {
|
||||
bound: VerifyBound) {
|
||||
debug!("verify_generic_bound({:?}, {:?} <: {:?})",
|
||||
kind,
|
||||
a,
|
||||
bs);
|
||||
bound);
|
||||
|
||||
self.region_vars.verify_generic_bound(origin, kind, a, bs);
|
||||
self.region_vars.verify_generic_bound(origin, kind, a, bound);
|
||||
}
|
||||
|
||||
pub fn can_equate<'b,T>(&'b self, a: &T, b: &T) -> UnitResult<'tcx>
|
||||
@ -1565,6 +1586,7 @@ impl TypeOrigin {
|
||||
impl<'tcx> SubregionOrigin<'tcx> {
|
||||
pub fn span(&self) -> Span {
|
||||
match *self {
|
||||
RFC1214Subregion(ref a) => a.span(),
|
||||
Subtype(ref a) => a.span(),
|
||||
InfStackClosure(a) => a,
|
||||
InvokeClosure(a) => a,
|
||||
@ -1577,7 +1599,9 @@ impl<'tcx> SubregionOrigin<'tcx> {
|
||||
RelateDefaultParamBound(a, _) => a,
|
||||
Reborrow(a) => a,
|
||||
ReborrowUpvar(a, _) => a,
|
||||
DataBorrowed(_, a) => a,
|
||||
ReferenceOutlivesReferent(_, a) => a,
|
||||
ParameterInScope(_, a) => a,
|
||||
ExprTypeIsNotInScope(_, a) => a,
|
||||
BindingTypeIsNotValidAtDecl(a) => a,
|
||||
CallRcvr(a) => a,
|
||||
|
@ -64,15 +64,41 @@ pub enum Verify<'tcx> {
|
||||
// outlive `RS`. Therefore verify that `R <= RS[i]` for some
|
||||
// `i`. Inference variables may be involved (but this verification
|
||||
// step doesn't influence inference).
|
||||
VerifyGenericBound(GenericKind<'tcx>, SubregionOrigin<'tcx>, Region, Vec<Region>),
|
||||
VerifyGenericBound(GenericKind<'tcx>, SubregionOrigin<'tcx>, Region, VerifyBound),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum GenericKind<'tcx> {
|
||||
Param(ty::ParamTy),
|
||||
Projection(ty::ProjectionTy<'tcx>),
|
||||
}
|
||||
|
||||
// When we introduce a verification step, we wish to test that a
|
||||
// particular region (let's call it `'min`) meets some bound.
|
||||
// The bound is described the by the following grammar:
|
||||
#[derive(Debug)]
|
||||
pub enum VerifyBound {
|
||||
// B = exists {R} --> some 'r in {R} must outlive 'min
|
||||
//
|
||||
// Put another way, the subject value is known to outlive all
|
||||
// regions in {R}, so if any of those outlives 'min, then the
|
||||
// bound is met.
|
||||
AnyRegion(Vec<Region>),
|
||||
|
||||
// B = forall {R} --> all 'r in {R} must outlive 'min
|
||||
//
|
||||
// Put another way, the subject value is known to outlive some
|
||||
// region in {R}, so if all of those outlives 'min, then the bound
|
||||
// is met.
|
||||
AllRegions(Vec<Region>),
|
||||
|
||||
// B = exists {B} --> 'min must meet some bound b in {B}
|
||||
AnyBound(Vec<VerifyBound>),
|
||||
|
||||
// B = forall {B} --> 'min must meet all bounds b in {B}
|
||||
AllBounds(Vec<VerifyBound>),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TwoRegions {
|
||||
a: Region,
|
||||
@ -102,12 +128,11 @@ pub enum RegionResolutionError<'tcx> {
|
||||
/// `o` requires that `a <= b`, but this does not hold
|
||||
ConcreteFailure(SubregionOrigin<'tcx>, Region, Region),
|
||||
|
||||
/// `GenericBoundFailure(p, s, a, bs)
|
||||
/// `GenericBoundFailure(p, s, a)
|
||||
///
|
||||
/// The parameter/associated-type `p` must be known to outlive the lifetime
|
||||
/// `a`, but it is only known to outlive `bs` (and none of the
|
||||
/// regions in `bs` outlive `a`).
|
||||
GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region, Vec<Region>),
|
||||
/// `a` (but none of the known bounds are sufficient).
|
||||
GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region),
|
||||
|
||||
/// `SubSupConflict(v, sub_origin, sub_r, sup_origin, sup_r)`:
|
||||
///
|
||||
@ -408,6 +433,14 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
|
||||
debug!("RegionVarBindings: add_verify({:?})",
|
||||
verify);
|
||||
|
||||
// skip no-op cases known to be satisfied
|
||||
match verify {
|
||||
VerifyGenericBound(_, _, _, VerifyBound::AllBounds(ref bs)) if bs.len() == 0 => {
|
||||
return;
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
|
||||
let mut verifys = self.verifys.borrow_mut();
|
||||
let index = verifys.len();
|
||||
verifys.push(verify);
|
||||
@ -497,8 +530,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
|
||||
origin: SubregionOrigin<'tcx>,
|
||||
kind: GenericKind<'tcx>,
|
||||
sub: Region,
|
||||
sups: Vec<Region>) {
|
||||
self.add_verify(VerifyGenericBound(kind, origin, sub, sups));
|
||||
bound: VerifyBound) {
|
||||
self.add_verify(VerifyGenericBound(kind, origin, sub, bound));
|
||||
}
|
||||
|
||||
pub fn lub_regions(&self,
|
||||
@ -663,12 +696,11 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
|
||||
&mut result_set, r,
|
||||
a, b);
|
||||
}
|
||||
VerifyGenericBound(_, _, a, ref bs) => {
|
||||
for &b in bs {
|
||||
consider_adding_bidirectional_edges(
|
||||
&mut result_set, r,
|
||||
a, b);
|
||||
}
|
||||
VerifyGenericBound(_, _, a, ref bound) => {
|
||||
bound.for_each_region(&mut |b| {
|
||||
consider_adding_bidirectional_edges(&mut result_set, r,
|
||||
a, b)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1258,26 +1290,22 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
|
||||
continue;
|
||||
}
|
||||
|
||||
debug!("ConcreteFailure: !(sub <= sup): sub={:?}, sup={:?}",
|
||||
sub,
|
||||
sup);
|
||||
debug!("region inference error at {:?}: {:?} <= {:?} is not true",
|
||||
origin, sub, sup);
|
||||
|
||||
errors.push(ConcreteFailure((*origin).clone(), sub, sup));
|
||||
}
|
||||
|
||||
VerifyGenericBound(ref kind, ref origin, sub, ref sups) => {
|
||||
VerifyGenericBound(ref kind, ref origin, sub, ref bound) => {
|
||||
let sub = normalize(values, sub);
|
||||
if sups.iter()
|
||||
.map(|&sup| normalize(values, sup))
|
||||
.any(|sup| free_regions.is_subregion_of(self.tcx, sub, sup))
|
||||
{
|
||||
if bound.is_met(self.tcx, free_regions, values, sub) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let sups = sups.iter().map(|&sup| normalize(values, sup))
|
||||
.collect();
|
||||
errors.push(
|
||||
GenericBoundFailure(
|
||||
(*origin).clone(), kind.clone(), sub, sups));
|
||||
debug!("region inference error at {:?}: verifying {:?} <= {:?}",
|
||||
origin, sub, bound);
|
||||
|
||||
errors.push(GenericBoundFailure((*origin).clone(), kind.clone(), sub));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1438,10 +1466,12 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
|
||||
if !free_regions.is_subregion_of(self.tcx,
|
||||
lower_bound.region,
|
||||
upper_bound.region) {
|
||||
debug!("pushing SubSupConflict sub: {:?} sup: {:?}",
|
||||
lower_bound.region, upper_bound.region);
|
||||
let origin = (*self.var_origins.borrow())[node_idx.index as usize].clone();
|
||||
debug!("region inference error at {:?} for {:?}: \
|
||||
SubSupConflict sub: {:?} sup: {:?}",
|
||||
origin, node_idx, lower_bound.region, upper_bound.region);
|
||||
errors.push(SubSupConflict(
|
||||
(*self.var_origins.borrow())[node_idx.index as usize].clone(),
|
||||
origin,
|
||||
lower_bound.origin.clone(),
|
||||
lower_bound.region,
|
||||
upper_bound.origin.clone(),
|
||||
@ -1484,16 +1514,20 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
|
||||
match self.glb_concrete_regions(free_regions,
|
||||
upper_bound_1.region,
|
||||
upper_bound_2.region) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
errors.push(SupSupConflict(
|
||||
(*self.var_origins.borrow())[node_idx.index as usize].clone(),
|
||||
upper_bound_1.origin.clone(),
|
||||
upper_bound_1.region,
|
||||
upper_bound_2.origin.clone(),
|
||||
upper_bound_2.region));
|
||||
return;
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
let origin = (*self.var_origins.borrow())[node_idx.index as usize].clone();
|
||||
debug!("region inference error at {:?} for {:?}: \
|
||||
SupSupConflict sub: {:?} sup: {:?}",
|
||||
origin, node_idx, upper_bound_1.region, upper_bound_2.region);
|
||||
errors.push(SupSupConflict(
|
||||
origin,
|
||||
upper_bound_1.origin.clone(),
|
||||
upper_bound_1.region,
|
||||
upper_bound_2.origin.clone(),
|
||||
upper_bound_2.region));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1676,3 +1710,82 @@ impl<'tcx> GenericKind<'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VerifyBound {
|
||||
fn for_each_region(&self, f: &mut FnMut(ty::Region)) {
|
||||
match self {
|
||||
&VerifyBound::AnyRegion(ref rs) |
|
||||
&VerifyBound::AllRegions(ref rs) =>
|
||||
for &r in rs { f(r); },
|
||||
|
||||
&VerifyBound::AnyBound(ref bs) |
|
||||
&VerifyBound::AllBounds(ref bs) =>
|
||||
for b in bs { b.for_each_region(f); },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn must_hold(&self) -> bool {
|
||||
match self {
|
||||
&VerifyBound::AnyRegion(ref bs) => bs.contains(&ty::ReStatic),
|
||||
&VerifyBound::AllRegions(ref bs) => bs.is_empty(),
|
||||
&VerifyBound::AnyBound(ref bs) => bs.iter().any(|b| b.must_hold()),
|
||||
&VerifyBound::AllBounds(ref bs) => bs.iter().all(|b| b.must_hold()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cannot_hold(&self) -> bool {
|
||||
match self {
|
||||
&VerifyBound::AnyRegion(ref bs) => bs.is_empty(),
|
||||
&VerifyBound::AllRegions(ref bs) => bs.contains(&ty::ReEmpty),
|
||||
&VerifyBound::AnyBound(ref bs) => bs.iter().all(|b| b.cannot_hold()),
|
||||
&VerifyBound::AllBounds(ref bs) => bs.iter().any(|b| b.cannot_hold()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn or(self, vb: VerifyBound) -> VerifyBound {
|
||||
if self.must_hold() || vb.cannot_hold() {
|
||||
self
|
||||
} else if self.cannot_hold() || vb.must_hold() {
|
||||
vb
|
||||
} else {
|
||||
VerifyBound::AnyBound(vec![self, vb])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn and(self, vb: VerifyBound) -> VerifyBound {
|
||||
if self.must_hold() && vb.must_hold() {
|
||||
self
|
||||
} else if self.cannot_hold() && vb.cannot_hold() {
|
||||
self
|
||||
} else {
|
||||
VerifyBound::AllBounds(vec![self, vb])
|
||||
}
|
||||
}
|
||||
|
||||
fn is_met<'tcx>(&self,
|
||||
tcx: &ty::ctxt<'tcx>,
|
||||
free_regions: &FreeRegionMap,
|
||||
var_values: &Vec<VarValue>,
|
||||
min: ty::Region)
|
||||
-> bool {
|
||||
match self {
|
||||
&VerifyBound::AnyRegion(ref rs) =>
|
||||
rs.iter()
|
||||
.map(|&r| normalize(var_values, r))
|
||||
.any(|r| free_regions.is_subregion_of(tcx, min, r)),
|
||||
|
||||
&VerifyBound::AllRegions(ref rs) =>
|
||||
rs.iter()
|
||||
.map(|&r| normalize(var_values, r))
|
||||
.all(|r| free_regions.is_subregion_of(tcx, min, r)),
|
||||
|
||||
&VerifyBound::AnyBound(ref bs) =>
|
||||
bs.iter()
|
||||
.any(|b| b.is_met(tcx, free_regions, var_values, min)),
|
||||
|
||||
&VerifyBound::AllBounds(ref bs) =>
|
||||
bs.iter()
|
||||
.all(|b| b.is_met(tcx, free_regions, var_values, min)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
231
src/librustc/middle/outlives.rs
Normal file
231
src/librustc/middle/outlives.rs
Normal file
@ -0,0 +1,231 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
// The outlines relation `T: 'a` or `'a: 'b`. This code frequently
|
||||
// refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that
|
||||
// RFC for reference.
|
||||
|
||||
use middle::infer::InferCtxt;
|
||||
use middle::ty::{self, RegionEscape, Ty};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Component<'tcx> {
|
||||
Region(ty::Region),
|
||||
Param(ty::ParamTy),
|
||||
UnresolvedInferenceVariable(ty::InferTy),
|
||||
|
||||
// Projections like `T::Foo` are tricky because a constraint like
|
||||
// `T::Foo: 'a` can be satisfied in so many ways. There may be a
|
||||
// where-clause that says `T::Foo: 'a`, or the defining trait may
|
||||
// include a bound like `type Foo: 'static`, or -- in the most
|
||||
// conservative way -- we can prove that `T: 'a` (more generally,
|
||||
// that all components in the projection outlive `'a`). This code
|
||||
// is not in a position to judge which is the best technique, so
|
||||
// we just product the projection as a component and leave it to
|
||||
// the consumer to decide (but see `EscapingProjection` below).
|
||||
Projection(ty::ProjectionTy<'tcx>),
|
||||
|
||||
// In the case where a projection has escaping regions -- meaning
|
||||
// regions bound within the type itself -- we always use
|
||||
// the most conservative rule, which requires that all components
|
||||
// outlive the bound. So for example if we had a type like this:
|
||||
//
|
||||
// for<'a> Trait1< <T as Trait2<'a,'b>>::Foo >
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// then the inner projection (underlined) has an escaping region
|
||||
// `'a`. We consider that outer trait `'c` to meet a bound if `'b`
|
||||
// outlives `'b: 'c`, and we don't consider whether the trait
|
||||
// declares that `Foo: 'static` etc. Therefore, we just return the
|
||||
// free components of such a projection (in this case, `'b`).
|
||||
//
|
||||
// However, in the future, we may want to get smarter, and
|
||||
// actually return a "higher-ranked projection" here. Therefore,
|
||||
// we mark that these components are part of an escaping
|
||||
// projection, so that implied bounds code can avoid relying on
|
||||
// them. This gives us room to improve the regionck reasoning in
|
||||
// the future without breaking backwards compat.
|
||||
EscapingProjection(Vec<Component<'tcx>>),
|
||||
|
||||
// This is a temporary marker indicating "outlives components"
|
||||
// that are due to the new rules introduced by RFC 1214. For the
|
||||
// time being, violations of these requirements generally induce
|
||||
// warnings, not errors.
|
||||
RFC1214(Vec<Component<'tcx>>),
|
||||
}
|
||||
|
||||
/// Returns all the things that must outlive `'a` for the condition
|
||||
/// `ty0: 'a` to hold.
|
||||
pub fn components<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
|
||||
ty0: Ty<'tcx>)
|
||||
-> Vec<Component<'tcx>> {
|
||||
let mut components = vec![];
|
||||
compute_components(infcx, ty0, &mut components);
|
||||
debug!("components({:?}) = {:?}", ty0, components);
|
||||
components
|
||||
}
|
||||
|
||||
fn compute_components<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
out: &mut Vec<Component<'tcx>>) {
|
||||
// Descend through the types, looking for the various "base"
|
||||
// components and collecting them into `out`. This is not written
|
||||
// with `collect()` because of the need to sometimes skip subtrees
|
||||
// in the `subtys` iterator (e.g., when encountering a
|
||||
// projection).
|
||||
match ty.sty {
|
||||
ty::TyClosure(_, ref substs) => {
|
||||
// FIXME(#27086). We do not accumulate from substs, since they
|
||||
// don't represent reachable data. This means that, in
|
||||
// practice, some of the lifetime parameters might not
|
||||
// be in scope when the body runs, so long as there is
|
||||
// no reachable data with that lifetime. For better or
|
||||
// worse, this is consistent with fn types, however,
|
||||
// which can also encapsulate data in this fashion
|
||||
// (though it's somewhat harder, and typically
|
||||
// requires virtual dispatch).
|
||||
//
|
||||
// Note that changing this (in a naive way, at least)
|
||||
// causes regressions for what appears to be perfectly
|
||||
// reasonable code like this:
|
||||
//
|
||||
// ```
|
||||
// fn foo<'a>(p: &Data<'a>) {
|
||||
// bar(|q: &mut Parser| q.read_addr())
|
||||
// }
|
||||
// fn bar(p: Box<FnMut(&mut Parser)+'static>) {
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Note that `p` (and `'a`) are not used in the
|
||||
// closure at all, but to meet the requirement that
|
||||
// the closure type `C: 'static` (so it can be coerced
|
||||
// to the object type), we get the requirement that
|
||||
// `'a: 'static` since `'a` appears in the closure
|
||||
// type `C`.
|
||||
//
|
||||
// A smarter fix might "prune" unused `func_substs` --
|
||||
// this would avoid breaking simple examples like
|
||||
// this, but would still break others (which might
|
||||
// indeed be invalid, depending on your POV). Pruning
|
||||
// would be a subtle process, since we have to see
|
||||
// what func/type parameters are used and unused,
|
||||
// taking into consideration UFCS and so forth.
|
||||
|
||||
for &upvar_ty in &substs.upvar_tys {
|
||||
compute_components(infcx, upvar_ty, out);
|
||||
}
|
||||
}
|
||||
|
||||
// Bare functions and traits are both binders. In the RFC,
|
||||
// this means we would add the bound regions to the "bound
|
||||
// regions list". In our representation, no such list is
|
||||
// maintained explicitly, because bound regions themselves can
|
||||
// be readily identified. However, because the outlives
|
||||
// relation did not used to be applied to fn/trait-object
|
||||
// arguments, we wrap the resulting components in an RFC1214
|
||||
// wrapper so we can issue warnings.
|
||||
ty::TyBareFn(..) | ty::TyTrait(..) => {
|
||||
// OutlivesFunction, OutlivesObject, OutlivesFragment
|
||||
let subcomponents = capture_components(infcx, ty);
|
||||
out.push(Component::RFC1214(subcomponents));
|
||||
}
|
||||
|
||||
// OutlivesTypeParameterEnv -- the actual checking that `X:'a`
|
||||
// is implied by the environment is done in regionck.
|
||||
ty::TyParam(p) => {
|
||||
out.push(Component::Param(p));
|
||||
}
|
||||
|
||||
// For projections, we prefer to generate an obligation like
|
||||
// `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
|
||||
// regionck more ways to prove that it holds. However,
|
||||
// regionck is not (at least currently) prepared to deal with
|
||||
// higher-ranked regions that may appear in the
|
||||
// trait-ref. Therefore, if we see any higher-ranke regions,
|
||||
// we simply fallback to the most restrictive rule, which
|
||||
// requires that `Pi: 'a` for all `i`.
|
||||
ty::TyProjection(ref data) => {
|
||||
if !data.has_escaping_regions() {
|
||||
// best case: no escaping regions, so push the
|
||||
// projection and skip the subtree (thus generating no
|
||||
// constraints for Pi). This defers the choice between
|
||||
// the rules OutlivesProjectionEnv,
|
||||
// OutlivesProjectionTraitDef, and
|
||||
// OutlivesProjectionComponents to regionck.
|
||||
out.push(Component::Projection(*data));
|
||||
} else {
|
||||
// fallback case: hard code
|
||||
// OutlivesProjectionComponents. Continue walking
|
||||
// through and constrain Pi.
|
||||
let subcomponents = capture_components(infcx, ty);
|
||||
out.push(Component::EscapingProjection(subcomponents));
|
||||
}
|
||||
}
|
||||
|
||||
// If we encounter an inference variable, try to resolve it
|
||||
// and proceed with resolved version. If we cannot resolve it,
|
||||
// then record the unresolved variable as a component.
|
||||
ty::TyInfer(_) => {
|
||||
let ty = infcx.resolve_type_vars_if_possible(&ty);
|
||||
if let ty::TyInfer(infer_ty) = ty.sty {
|
||||
out.push(Component::UnresolvedInferenceVariable(infer_ty));
|
||||
} else {
|
||||
compute_components(infcx, ty, out);
|
||||
}
|
||||
}
|
||||
|
||||
// Most types do not introduce any region binders, nor
|
||||
// involve any other subtle cases, and so the WF relation
|
||||
// simply constraints any regions referenced directly by
|
||||
// the type and then visits the types that are lexically
|
||||
// contained within. (The comments refer to relevant rules
|
||||
// from RFC1214.)
|
||||
ty::TyBool(..) | // OutlivesScalar
|
||||
ty::TyChar(..) | // OutlivesScalar
|
||||
ty::TyInt(..) | // OutlivesScalar
|
||||
ty::TyUint(..) | // OutlivesScalar
|
||||
ty::TyFloat(..) | // OutlivesScalar
|
||||
ty::TyEnum(..) | // OutlivesNominalType
|
||||
ty::TyStruct(..) | // OutlivesNominalType
|
||||
ty::TyBox(..) | // OutlivesNominalType (ish)
|
||||
ty::TyStr(..) | // OutlivesScalar (ish)
|
||||
ty::TyArray(..) | // ...
|
||||
ty::TySlice(..) | // ...
|
||||
ty::TyRawPtr(..) | // ...
|
||||
ty::TyRef(..) | // OutlivesReference
|
||||
ty::TyTuple(..) | // ...
|
||||
ty::TyError(..) => {
|
||||
push_region_constraints(out, ty.regions());
|
||||
for subty in ty.walk_shallow() {
|
||||
compute_components(infcx, subty, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn capture_components<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
|
||||
ty: Ty<'tcx>)
|
||||
-> Vec<Component<'tcx>> {
|
||||
let mut temp = vec![];
|
||||
push_region_constraints(&mut temp, ty.regions());
|
||||
for subty in ty.walk_shallow() {
|
||||
compute_components(infcx, subty, &mut temp);
|
||||
}
|
||||
temp
|
||||
}
|
||||
|
||||
fn push_region_constraints<'tcx>(out: &mut Vec<Component<'tcx>>, regions: Vec<ty::Region>) {
|
||||
for r in regions {
|
||||
if !r.is_bound() {
|
||||
out.push(Component::Region(r));
|
||||
}
|
||||
}
|
||||
}
|
@ -25,11 +25,12 @@ use super::{
|
||||
|
||||
use fmt_macros::{Parser, Piece, Position};
|
||||
use middle::infer::InferCtxt;
|
||||
use middle::ty::{self, ToPredicate, HasTypeFlags, ToPolyTraitRef, TraitRef};
|
||||
use middle::ty::{self, ToPredicate, HasTypeFlags, ToPolyTraitRef, TraitRef, Ty};
|
||||
use middle::ty_fold::TypeFoldable;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::ast;
|
||||
use syntax::attr::{AttributeMethods, AttrMetaMethods};
|
||||
|
||||
pub fn report_fulfillment_errors<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
@ -54,22 +55,28 @@ fn report_fulfillment_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_warning<T>(obligation: &Obligation<T>) -> bool {
|
||||
obligation.cause.code.is_rfc1214()
|
||||
}
|
||||
|
||||
pub fn report_projection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
error: &MismatchedProjectionTypes<'tcx>)
|
||||
{
|
||||
let predicate =
|
||||
infcx.resolve_type_vars_if_possible(&obligation.predicate);
|
||||
|
||||
// The TyError created by normalize_to_error can end up being unified
|
||||
// into all obligations: for example, if our obligation is something
|
||||
// like `$X = <() as Foo<$X>>::Out` and () does not implement Foo<_>,
|
||||
// then $X will be unified with TyError, but the error still needs to be
|
||||
// reported.
|
||||
if !infcx.tcx.sess.has_errors() || !predicate.references_error() {
|
||||
span_err!(infcx.tcx.sess, obligation.cause.span, E0271,
|
||||
"type mismatch resolving `{}`: {}",
|
||||
predicate,
|
||||
error.err);
|
||||
span_err_or_warn!(
|
||||
is_warning(obligation), infcx.tcx.sess, obligation.cause.span, E0271,
|
||||
"type mismatch resolving `{}`: {}",
|
||||
predicate,
|
||||
error.err);
|
||||
note_obligation_cause(infcx, obligation);
|
||||
}
|
||||
}
|
||||
@ -173,66 +180,90 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
error: &SelectionError<'tcx>)
|
||||
{
|
||||
let is_warning = is_warning(obligation);
|
||||
match *error {
|
||||
SelectionError::Unimplemented => {
|
||||
match &obligation.cause.code {
|
||||
&ObligationCauseCode::CompareImplMethodObligation => {
|
||||
span_err!(infcx.tcx.sess, obligation.cause.span, E0276,
|
||||
"the requirement `{}` appears on the impl \
|
||||
method but not on the corresponding trait method",
|
||||
obligation.predicate);;
|
||||
}
|
||||
_ => {
|
||||
match obligation.predicate {
|
||||
ty::Predicate::Trait(ref trait_predicate) => {
|
||||
let trait_predicate =
|
||||
infcx.resolve_type_vars_if_possible(trait_predicate);
|
||||
if let ObligationCauseCode::CompareImplMethodObligation = obligation.cause.code {
|
||||
span_err_or_warn!(
|
||||
is_warning, infcx.tcx.sess, obligation.cause.span, E0276,
|
||||
"the requirement `{}` appears on the impl \
|
||||
method but not on the corresponding trait method",
|
||||
obligation.predicate);;
|
||||
} else {
|
||||
match obligation.predicate {
|
||||
ty::Predicate::Trait(ref trait_predicate) => {
|
||||
let trait_predicate =
|
||||
infcx.resolve_type_vars_if_possible(trait_predicate);
|
||||
|
||||
if !infcx.tcx.sess.has_errors() ||
|
||||
!trait_predicate.references_error() {
|
||||
let trait_ref = trait_predicate.to_poly_trait_ref();
|
||||
span_err!(infcx.tcx.sess, obligation.cause.span, E0277,
|
||||
"the trait `{}` is not implemented for the type `{}`",
|
||||
trait_ref,
|
||||
trait_ref.self_ty());
|
||||
// Check if it has a custom "#[rustc_on_unimplemented]"
|
||||
// error message, report with that message if it does
|
||||
let custom_note = report_on_unimplemented(infcx, &trait_ref.0,
|
||||
obligation.cause.span);
|
||||
if let Some(s) = custom_note {
|
||||
infcx.tcx.sess.span_note(obligation.cause.span,
|
||||
&s);
|
||||
}
|
||||
if !infcx.tcx.sess.has_errors() || !trait_predicate.references_error() {
|
||||
let trait_ref = trait_predicate.to_poly_trait_ref();
|
||||
span_err_or_warn!(
|
||||
is_warning, infcx.tcx.sess, obligation.cause.span, E0277,
|
||||
"the trait `{}` is not implemented for the type `{}`",
|
||||
trait_ref, trait_ref.self_ty());
|
||||
|
||||
// Check if it has a custom "#[rustc_on_unimplemented]"
|
||||
// error message, report with that message if it does
|
||||
let custom_note = report_on_unimplemented(infcx, &trait_ref.0,
|
||||
obligation.cause.span);
|
||||
if let Some(s) = custom_note {
|
||||
infcx.tcx.sess.span_note(obligation.cause.span, &s);
|
||||
}
|
||||
note_obligation_cause(infcx, obligation);
|
||||
}
|
||||
}
|
||||
|
||||
ty::Predicate::Equate(ref predicate) => {
|
||||
let predicate = infcx.resolve_type_vars_if_possible(predicate);
|
||||
let err = infcx.equality_predicate(obligation.cause.span,
|
||||
&predicate).err().unwrap();
|
||||
span_err!(infcx.tcx.sess, obligation.cause.span, E0278,
|
||||
"the requirement `{}` is not satisfied (`{}`)",
|
||||
predicate,
|
||||
err);
|
||||
}
|
||||
ty::Predicate::Equate(ref predicate) => {
|
||||
let predicate = infcx.resolve_type_vars_if_possible(predicate);
|
||||
let err = infcx.equality_predicate(obligation.cause.span,
|
||||
&predicate).err().unwrap();
|
||||
span_err_or_warn!(
|
||||
is_warning, infcx.tcx.sess, obligation.cause.span, E0278,
|
||||
"the requirement `{}` is not satisfied (`{}`)",
|
||||
predicate,
|
||||
err);
|
||||
note_obligation_cause(infcx, obligation);
|
||||
}
|
||||
|
||||
ty::Predicate::RegionOutlives(ref predicate) => {
|
||||
let predicate = infcx.resolve_type_vars_if_possible(predicate);
|
||||
let err = infcx.region_outlives_predicate(obligation.cause.span,
|
||||
&predicate).err().unwrap();
|
||||
span_err!(infcx.tcx.sess, obligation.cause.span, E0279,
|
||||
"the requirement `{}` is not satisfied (`{}`)",
|
||||
predicate,
|
||||
err);
|
||||
}
|
||||
ty::Predicate::RegionOutlives(ref predicate) => {
|
||||
let predicate = infcx.resolve_type_vars_if_possible(predicate);
|
||||
let err = infcx.region_outlives_predicate(obligation.cause.span,
|
||||
&predicate).err().unwrap();
|
||||
span_err_or_warn!(
|
||||
is_warning, infcx.tcx.sess, obligation.cause.span, E0279,
|
||||
"the requirement `{}` is not satisfied (`{}`)",
|
||||
predicate,
|
||||
err);
|
||||
note_obligation_cause(infcx, obligation);
|
||||
}
|
||||
|
||||
ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => {
|
||||
let predicate =
|
||||
infcx.resolve_type_vars_if_possible(&obligation.predicate);
|
||||
span_err!(infcx.tcx.sess, obligation.cause.span, E0280,
|
||||
"the requirement `{}` is not satisfied",
|
||||
predicate);
|
||||
}
|
||||
ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => {
|
||||
let predicate =
|
||||
infcx.resolve_type_vars_if_possible(&obligation.predicate);
|
||||
span_err_or_warn!(
|
||||
is_warning, infcx.tcx.sess, obligation.cause.span, E0280,
|
||||
"the requirement `{}` is not satisfied",
|
||||
predicate);
|
||||
note_obligation_cause(infcx, obligation);
|
||||
}
|
||||
|
||||
ty::Predicate::ObjectSafe(trait_def_id) => {
|
||||
report_object_safety_error(infcx.tcx,
|
||||
obligation.cause.span,
|
||||
trait_def_id,
|
||||
is_warning);
|
||||
note_obligation_cause(infcx, obligation);
|
||||
}
|
||||
|
||||
ty::Predicate::WellFormed(ty) => {
|
||||
// WF predicates cannot themselves make
|
||||
// errors. They can only block due to
|
||||
// ambiguity; otherwise, they always
|
||||
// degenerate into other obligations
|
||||
// (which may fail).
|
||||
infcx.tcx.sess.span_bug(
|
||||
obligation.cause.span,
|
||||
&format!("WF predicate not satisfied for {:?}", ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -242,62 +273,73 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
let expected_trait_ref = infcx.resolve_type_vars_if_possible(&*expected_trait_ref);
|
||||
let actual_trait_ref = infcx.resolve_type_vars_if_possible(&*actual_trait_ref);
|
||||
if !actual_trait_ref.self_ty().references_error() {
|
||||
span_err!(infcx.tcx.sess, obligation.cause.span, E0281,
|
||||
"type mismatch: the type `{}` implements the trait `{}`, \
|
||||
but the trait `{}` is required ({})",
|
||||
expected_trait_ref.self_ty(),
|
||||
expected_trait_ref,
|
||||
actual_trait_ref,
|
||||
e);
|
||||
note_obligation_cause(infcx, obligation);
|
||||
span_err_or_warn!(
|
||||
is_warning, infcx.tcx.sess, obligation.cause.span, E0281,
|
||||
"type mismatch: the type `{}` implements the trait `{}`, \
|
||||
but the trait `{}` is required ({})",
|
||||
expected_trait_ref.self_ty(),
|
||||
expected_trait_ref,
|
||||
actual_trait_ref,
|
||||
e);
|
||||
note_obligation_cause(infcx, obligation);
|
||||
}
|
||||
}
|
||||
|
||||
TraitNotObjectSafe(did) => {
|
||||
span_err!(infcx.tcx.sess, obligation.cause.span, E0038,
|
||||
"cannot convert to a trait object because trait `{}` is not object-safe",
|
||||
infcx.tcx.item_path_str(did));
|
||||
report_object_safety_error(infcx.tcx, obligation.cause.span, did, is_warning);
|
||||
note_obligation_cause(infcx, obligation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for violation in object_safety_violations(infcx.tcx, did) {
|
||||
match violation {
|
||||
ObjectSafetyViolation::SizedSelf => {
|
||||
infcx.tcx.sess.span_note(
|
||||
obligation.cause.span,
|
||||
"the trait cannot require that `Self : Sized`");
|
||||
}
|
||||
pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
span: Span,
|
||||
trait_def_id: ast::DefId,
|
||||
is_warning: bool)
|
||||
{
|
||||
span_err_or_warn!(
|
||||
is_warning, tcx.sess, span, E0038,
|
||||
"the trait `{}` cannot be made into an object",
|
||||
tcx.item_path_str(trait_def_id));
|
||||
|
||||
ObjectSafetyViolation::SupertraitSelf => {
|
||||
infcx.tcx.sess.span_note(
|
||||
obligation.cause.span,
|
||||
"the trait cannot use `Self` as a type parameter \
|
||||
in the supertrait listing");
|
||||
}
|
||||
for violation in object_safety_violations(tcx, trait_def_id) {
|
||||
match violation {
|
||||
ObjectSafetyViolation::SizedSelf => {
|
||||
tcx.sess.span_note(
|
||||
span,
|
||||
"the trait cannot require that `Self : Sized`");
|
||||
}
|
||||
|
||||
ObjectSafetyViolation::Method(method,
|
||||
MethodViolationCode::StaticMethod) => {
|
||||
infcx.tcx.sess.span_note(
|
||||
obligation.cause.span,
|
||||
&format!("method `{}` has no receiver",
|
||||
method.name));
|
||||
}
|
||||
ObjectSafetyViolation::SupertraitSelf => {
|
||||
tcx.sess.span_note(
|
||||
span,
|
||||
"the trait cannot use `Self` as a type parameter \
|
||||
in the supertrait listing");
|
||||
}
|
||||
|
||||
ObjectSafetyViolation::Method(method,
|
||||
MethodViolationCode::ReferencesSelf) => {
|
||||
infcx.tcx.sess.span_note(
|
||||
obligation.cause.span,
|
||||
&format!("method `{}` references the `Self` type \
|
||||
in its arguments or return type",
|
||||
method.name));
|
||||
}
|
||||
ObjectSafetyViolation::Method(method,
|
||||
MethodViolationCode::StaticMethod) => {
|
||||
tcx.sess.span_note(
|
||||
span,
|
||||
&format!("method `{}` has no receiver",
|
||||
method.name));
|
||||
}
|
||||
|
||||
ObjectSafetyViolation::Method(method,
|
||||
MethodViolationCode::Generic) => {
|
||||
infcx.tcx.sess.span_note(
|
||||
obligation.cause.span,
|
||||
&format!("method `{}` has generic type parameters",
|
||||
method.name));
|
||||
}
|
||||
}
|
||||
ObjectSafetyViolation::Method(method,
|
||||
MethodViolationCode::ReferencesSelf) => {
|
||||
tcx.sess.span_note(
|
||||
span,
|
||||
&format!("method `{}` references the `Self` type \
|
||||
in its arguments or return type",
|
||||
method.name));
|
||||
}
|
||||
|
||||
ObjectSafetyViolation::Method(method,
|
||||
MethodViolationCode::Generic) => {
|
||||
tcx.sess.span_note(
|
||||
span,
|
||||
&format!("method `{}` has generic type parameters",
|
||||
method.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -342,14 +384,11 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
infcx.tcx.lang_items.sized_trait()
|
||||
.map_or(false, |sized_id| sized_id == trait_ref.def_id())
|
||||
{
|
||||
span_err!(infcx.tcx.sess, obligation.cause.span, E0282,
|
||||
"unable to infer enough type information about `{}`; \
|
||||
type annotations or generic parameter binding required",
|
||||
self_ty);
|
||||
need_type_info(infcx, obligation.cause.span, self_ty);
|
||||
} else {
|
||||
span_err!(infcx.tcx.sess, obligation.cause.span, E0283,
|
||||
"type annotations required: cannot resolve `{}`",
|
||||
predicate);;
|
||||
predicate);
|
||||
note_obligation_cause(infcx, obligation);
|
||||
}
|
||||
}
|
||||
@ -366,6 +405,14 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
ty::Predicate::WellFormed(ty) => {
|
||||
// Same hacky approach as above to avoid deluging user
|
||||
// with error messages.
|
||||
if !ty.references_error() && !infcx.tcx.sess.has_errors() {
|
||||
need_type_info(infcx, obligation.cause.span, ty);
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
if !infcx.tcx.sess.has_errors() {
|
||||
span_err!(infcx.tcx.sess, obligation.cause.span, E0284,
|
||||
@ -377,6 +424,16 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
fn need_type_info<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
span: Span,
|
||||
ty: Ty<'tcx>)
|
||||
{
|
||||
span_err!(infcx.tcx.sess, span, E0282,
|
||||
"unable to infer enough type information about `{}`; \
|
||||
type annotations or generic parameter binding required",
|
||||
ty);
|
||||
}
|
||||
|
||||
fn note_obligation_cause<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
obligation: &Obligation<'tcx, T>)
|
||||
where T: fmt::Display
|
||||
@ -396,6 +453,27 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
let tcx = infcx.tcx;
|
||||
match *cause_code {
|
||||
ObligationCauseCode::MiscObligation => { }
|
||||
ObligationCauseCode::RFC1214(ref subcode) => {
|
||||
tcx.sess.note_rfc_1214(cause_span);
|
||||
note_obligation_cause_code(infcx, predicate, cause_span, subcode);
|
||||
}
|
||||
ObligationCauseCode::SliceOrArrayElem => {
|
||||
tcx.sess.span_note(
|
||||
cause_span,
|
||||
&format!("slice and array elements must have `Sized` type"));
|
||||
}
|
||||
ObligationCauseCode::ProjectionWf(data) => {
|
||||
tcx.sess.span_note(
|
||||
cause_span,
|
||||
&format!("required so that the projection `{}` is well-formed",
|
||||
data));
|
||||
}
|
||||
ObligationCauseCode::ReferenceOutlivesReferent(ref_ty) => {
|
||||
tcx.sess.span_note(
|
||||
cause_span,
|
||||
&format!("required so that reference `{}` does not outlive its referent",
|
||||
ref_ty));
|
||||
}
|
||||
ObligationCauseCode::ItemObligation(item_def_id) => {
|
||||
let item_name = tcx.item_path_str(item_def_id);
|
||||
tcx.sess.span_note(
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
use middle::infer::InferCtxt;
|
||||
use middle::ty::{self, RegionEscape, Ty, HasTypeFlags};
|
||||
use middle::wf;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
@ -20,16 +21,19 @@ use util::nodemap::NodeMap;
|
||||
use super::CodeAmbiguity;
|
||||
use super::CodeProjectionError;
|
||||
use super::CodeSelectionError;
|
||||
use super::is_object_safe;
|
||||
use super::FulfillmentError;
|
||||
use super::ObligationCause;
|
||||
use super::ObligationCauseCode;
|
||||
use super::PredicateObligation;
|
||||
use super::project;
|
||||
use super::RFC1214Warning;
|
||||
use super::select::SelectionContext;
|
||||
use super::Unimplemented;
|
||||
use super::util::predicate_for_builtin_bound;
|
||||
|
||||
pub struct FulfilledPredicates<'tcx> {
|
||||
set: HashSet<ty::Predicate<'tcx>>
|
||||
set: HashSet<(RFC1214Warning, ty::Predicate<'tcx>)>
|
||||
}
|
||||
|
||||
/// The fulfillment context is used to drive trait resolution. It
|
||||
@ -187,7 +191,9 @@ impl<'tcx> FulfillmentContext<'tcx> {
|
||||
|
||||
assert!(!obligation.has_escaping_regions());
|
||||
|
||||
if self.is_duplicate_or_add(infcx.tcx, &obligation.predicate) {
|
||||
let w = RFC1214Warning(obligation.cause.code.is_rfc1214());
|
||||
|
||||
if self.is_duplicate_or_add(infcx.tcx, w, &obligation.predicate) {
|
||||
debug!("register_predicate({:?}) -- already seen, skip", obligation);
|
||||
return;
|
||||
}
|
||||
@ -250,7 +256,9 @@ impl<'tcx> FulfillmentContext<'tcx> {
|
||||
&self.predicates
|
||||
}
|
||||
|
||||
fn is_duplicate_or_add(&mut self, tcx: &ty::ctxt<'tcx>,
|
||||
fn is_duplicate_or_add(&mut self,
|
||||
tcx: &ty::ctxt<'tcx>,
|
||||
w: RFC1214Warning,
|
||||
predicate: &ty::Predicate<'tcx>)
|
||||
-> bool {
|
||||
// This is a kind of dirty hack to allow us to avoid "rederiving"
|
||||
@ -265,10 +273,12 @@ impl<'tcx> FulfillmentContext<'tcx> {
|
||||
// evaluating the 'nested obligations'. This cache lets us
|
||||
// skip those.
|
||||
|
||||
if self.errors_will_be_reported && predicate.is_global() {
|
||||
tcx.fulfilled_predicates.borrow_mut().is_duplicate_or_add(predicate)
|
||||
let will_warn_due_to_rfc1214 = w.0;
|
||||
let errors_will_be_reported = self.errors_will_be_reported && !will_warn_due_to_rfc1214;
|
||||
if errors_will_be_reported && predicate.is_global() {
|
||||
tcx.fulfilled_predicates.borrow_mut().is_duplicate_or_add(w, predicate)
|
||||
} else {
|
||||
self.duplicate_set.is_duplicate_or_add(predicate)
|
||||
self.duplicate_set.is_duplicate_or_add(w, predicate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -472,6 +482,32 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty::Predicate::ObjectSafe(trait_def_id) => {
|
||||
if !is_object_safe(selcx.tcx(), trait_def_id) {
|
||||
errors.push(FulfillmentError::new(
|
||||
obligation.clone(),
|
||||
CodeSelectionError(Unimplemented)));
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
ty::Predicate::WellFormed(ty) => {
|
||||
let rfc1214 = match obligation.cause.code {
|
||||
ObligationCauseCode::RFC1214(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
match wf::obligations(selcx.infcx(), obligation.cause.body_id,
|
||||
ty, obligation.cause.span, rfc1214) {
|
||||
Some(obligations) => {
|
||||
new_obligations.extend(obligations);
|
||||
true
|
||||
}
|
||||
None => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -492,11 +528,12 @@ fn register_region_obligation<'tcx>(t_a: Ty<'tcx>,
|
||||
sub_region: r_b,
|
||||
cause: cause };
|
||||
|
||||
debug!("register_region_obligation({:?})",
|
||||
region_obligation);
|
||||
debug!("register_region_obligation({:?}, cause={:?})",
|
||||
region_obligation, region_obligation.cause);
|
||||
|
||||
region_obligations.entry(region_obligation.cause.body_id).or_insert(vec![])
|
||||
.push(region_obligation);
|
||||
region_obligations.entry(region_obligation.cause.body_id)
|
||||
.or_insert(vec![])
|
||||
.push(region_obligation);
|
||||
|
||||
}
|
||||
|
||||
@ -507,11 +544,13 @@ impl<'tcx> FulfilledPredicates<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_duplicate(&self, p: &ty::Predicate<'tcx>) -> bool {
|
||||
self.set.contains(p)
|
||||
pub fn is_duplicate(&self, w: RFC1214Warning, p: &ty::Predicate<'tcx>) -> bool {
|
||||
let key = (w, p.clone());
|
||||
self.set.contains(&key)
|
||||
}
|
||||
|
||||
fn is_duplicate_or_add(&mut self, p: &ty::Predicate<'tcx>) -> bool {
|
||||
!self.set.insert(p.clone())
|
||||
fn is_duplicate_or_add(&mut self, w: RFC1214Warning, p: &ty::Predicate<'tcx>) -> bool {
|
||||
let key = (w, p.clone());
|
||||
!self.set.insert(key)
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ use syntax::codemap::{Span, DUMMY_SP};
|
||||
pub use self::error_reporting::report_fulfillment_errors;
|
||||
pub use self::error_reporting::report_overflow_error;
|
||||
pub use self::error_reporting::report_selection_error;
|
||||
pub use self::error_reporting::report_object_safety_error;
|
||||
pub use self::error_reporting::suggest_new_overflow_limit;
|
||||
pub use self::coherence::orphan_check;
|
||||
pub use self::coherence::overlapping_impls;
|
||||
@ -80,7 +81,7 @@ pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;
|
||||
pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>;
|
||||
|
||||
/// Why did we incur this obligation? Used for error reporting.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ObligationCause<'tcx> {
|
||||
pub span: Span,
|
||||
|
||||
@ -95,15 +96,27 @@ pub struct ObligationCause<'tcx> {
|
||||
pub code: ObligationCauseCode<'tcx>
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ObligationCauseCode<'tcx> {
|
||||
/// Not well classified or should be obvious from span.
|
||||
MiscObligation,
|
||||
|
||||
/// Obligation that triggers warning until RFC 1214 is fully in place.
|
||||
RFC1214(Rc<ObligationCauseCode<'tcx>>),
|
||||
|
||||
/// This is the trait reference from the given projection
|
||||
SliceOrArrayElem,
|
||||
|
||||
/// This is the trait reference from the given projection
|
||||
ProjectionWf(ty::ProjectionTy<'tcx>),
|
||||
|
||||
/// In an impl of trait X for type Y, type Y must
|
||||
/// also implement all supertraits of X.
|
||||
ItemObligation(ast::DefId),
|
||||
|
||||
/// A type like `&'a T` is WF only if `T: 'a`.
|
||||
ReferenceOutlivesReferent(Ty<'tcx>),
|
||||
|
||||
/// Obligation incurred due to an object cast.
|
||||
ObjectCastObligation(/* Object type */ Ty<'tcx>),
|
||||
|
||||
@ -124,7 +137,6 @@ pub enum ObligationCauseCode<'tcx> {
|
||||
// static items must have `Sync` type
|
||||
SharedStatic,
|
||||
|
||||
|
||||
BuiltinDerivedObligation(DerivedObligationCause<'tcx>),
|
||||
|
||||
ImplDerivedObligation(DerivedObligationCause<'tcx>),
|
||||
@ -132,7 +144,7 @@ pub enum ObligationCauseCode<'tcx> {
|
||||
CompareImplMethodObligation,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct DerivedObligationCause<'tcx> {
|
||||
/// The trait reference of the parent obligation that led to the
|
||||
/// current obligation. Note that only trait obligations lead to
|
||||
@ -516,6 +528,24 @@ impl<'tcx> ObligationCause<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// This marker is used in some caches to record whether the
|
||||
/// predicate, if it is found to be false, will yield a warning (due
|
||||
/// to RFC1214) or an error. We separate these two cases in the cache
|
||||
/// so that if we see the same predicate twice, first resulting in a
|
||||
/// warning, and next resulting in an error, we still report the
|
||||
/// error, rather than considering it a duplicate.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct RFC1214Warning(bool);
|
||||
|
||||
impl<'tcx> ObligationCauseCode<'tcx> {
|
||||
pub fn is_rfc1214(&self) -> bool {
|
||||
match *self {
|
||||
ObligationCauseCode::RFC1214(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, N> Vtable<'tcx, N> {
|
||||
pub fn nested_obligations(self) -> Vec<N> {
|
||||
match self {
|
||||
|
@ -40,7 +40,7 @@ pub enum ObjectSafetyViolation<'tcx> {
|
||||
}
|
||||
|
||||
/// Reasons a method might not be object-safe.
|
||||
#[derive(Copy,Clone,Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum MethodViolationCode {
|
||||
/// e.g., `fn foo()`
|
||||
StaticMethod,
|
||||
@ -140,6 +140,8 @@ fn supertraits_reference_self<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
.any(is_self)
|
||||
}
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::TypeOutlives(..) |
|
||||
ty::Predicate::RegionOutlives(..) |
|
||||
ty::Predicate::Equate(..) => {
|
||||
@ -181,6 +183,8 @@ fn generics_require_sized_self<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
ty::Predicate::Trait(..) |
|
||||
ty::Predicate::Equate(..) |
|
||||
ty::Predicate::RegionOutlives(..) |
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::TypeOutlives(..) => {
|
||||
false
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation
|
||||
use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch};
|
||||
use super::{ObjectCastObligation, Obligation};
|
||||
use super::TraitNotObjectSafe;
|
||||
use super::RFC1214Warning;
|
||||
use super::Selection;
|
||||
use super::SelectionResult;
|
||||
use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure,
|
||||
@ -44,6 +45,7 @@ use middle::infer::{InferCtxt, TypeFreshener};
|
||||
use middle::ty_fold::TypeFoldable;
|
||||
use middle::ty_match;
|
||||
use middle::ty_relate::TypeRelation;
|
||||
use middle::wf;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
@ -444,7 +446,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
// have been proven elsewhere. This cache only contains
|
||||
// predicates that are global in scope and hence unaffected by
|
||||
// the current environment.
|
||||
if self.tcx().fulfilled_predicates.borrow().is_duplicate(&obligation.predicate) {
|
||||
let w = RFC1214Warning(false);
|
||||
if self.tcx().fulfilled_predicates.borrow().is_duplicate(w, &obligation.predicate) {
|
||||
return EvaluatedToOk;
|
||||
}
|
||||
|
||||
@ -465,12 +468,31 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
ty::Predicate::WellFormed(ty) => {
|
||||
match wf::obligations(self.infcx, obligation.cause.body_id,
|
||||
ty, obligation.cause.span,
|
||||
obligation.cause.code.is_rfc1214()) {
|
||||
Some(obligations) =>
|
||||
self.evaluate_predicates_recursively(previous_stack, obligations.iter()),
|
||||
None =>
|
||||
EvaluatedToAmbig,
|
||||
}
|
||||
}
|
||||
|
||||
ty::Predicate::TypeOutlives(..) | ty::Predicate::RegionOutlives(..) => {
|
||||
// we do not consider region relationships when
|
||||
// evaluating trait matches
|
||||
EvaluatedToOk
|
||||
}
|
||||
|
||||
ty::Predicate::ObjectSafe(trait_def_id) => {
|
||||
if object_safety::is_object_safe(self.tcx(), trait_def_id) {
|
||||
EvaluatedToOk
|
||||
} else {
|
||||
EvaluatedToErr(Unimplemented)
|
||||
}
|
||||
}
|
||||
|
||||
ty::Predicate::Projection(ref data) => {
|
||||
self.infcx.probe(|_| {
|
||||
let project_obligation = obligation.with(data.clone());
|
||||
@ -2900,13 +2922,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
// chain. Ideally, we should have a way to configure this either
|
||||
// by using -Z verbose or just a CLI argument.
|
||||
if obligation.recursion_depth >= 0 {
|
||||
let derived_cause = DerivedObligationCause {
|
||||
parent_trait_ref: obligation.predicate.to_poly_trait_ref(),
|
||||
parent_code: Rc::new(obligation.cause.code.clone()),
|
||||
let derived_code = match obligation.cause.code {
|
||||
ObligationCauseCode::RFC1214(ref base_code) => {
|
||||
let derived_cause = DerivedObligationCause {
|
||||
parent_trait_ref: obligation.predicate.to_poly_trait_ref(),
|
||||
parent_code: base_code.clone(),
|
||||
};
|
||||
ObligationCauseCode::RFC1214(Rc::new(variant(derived_cause)))
|
||||
}
|
||||
_ => {
|
||||
let derived_cause = DerivedObligationCause {
|
||||
parent_trait_ref: obligation.predicate.to_poly_trait_ref(),
|
||||
parent_code: Rc::new(obligation.cause.code.clone())
|
||||
};
|
||||
variant(derived_cause)
|
||||
}
|
||||
};
|
||||
ObligationCause::new(obligation.cause.span,
|
||||
obligation.cause.body_id,
|
||||
variant(derived_cause))
|
||||
ObligationCause::new(obligation.cause.span, obligation.cause.body_id, derived_code)
|
||||
} else {
|
||||
obligation.cause.clone()
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
use middle::subst::Substs;
|
||||
use middle::infer::InferCtxt;
|
||||
use middle::ty::{self, Ty, ToPredicate, ToPolyTraitRef};
|
||||
use middle::ty::{self, HasTypeFlags, Ty, ToPredicate, ToPolyTraitRef};
|
||||
use std::fmt;
|
||||
use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
@ -56,6 +56,12 @@ impl<'a,'tcx> PredicateSet<'a,'tcx> {
|
||||
|
||||
ty::Predicate::Projection(ref data) =>
|
||||
ty::Predicate::Projection(self.tcx.anonymize_late_bound_regions(data)),
|
||||
|
||||
ty::Predicate::WellFormed(data) =>
|
||||
ty::Predicate::WellFormed(data),
|
||||
|
||||
ty::Predicate::ObjectSafe(data) =>
|
||||
ty::Predicate::ObjectSafe(data),
|
||||
};
|
||||
self.set.insert(normalized_pred)
|
||||
}
|
||||
@ -136,6 +142,14 @@ impl<'cx, 'tcx> Elaborator<'cx, 'tcx> {
|
||||
|
||||
self.stack.extend(predicates);
|
||||
}
|
||||
ty::Predicate::WellFormed(..) => {
|
||||
// Currently, we do not elaborate WF predicates,
|
||||
// although we easily could.
|
||||
}
|
||||
ty::Predicate::ObjectSafe(..) => {
|
||||
// Currently, we do not elaborate object-safe
|
||||
// predicates.
|
||||
}
|
||||
ty::Predicate::Equate(..) => {
|
||||
// Currently, we do not "elaborate" predicates like
|
||||
// `X == Y`, though conceivably we might. For example,
|
||||
@ -562,3 +576,9 @@ impl<'tcx> fmt::Debug for super::MismatchedProjectionTypes<'tcx> {
|
||||
write!(f, "MismatchedProjectionTypes({:?})", self.err)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T: HasTypeFlags> HasTypeFlags for Obligation<'tcx, T> {
|
||||
fn has_type_flags(&self, flags: ty::TypeFlags) -> bool {
|
||||
self.predicate.has_type_flags(flags)
|
||||
}
|
||||
}
|
||||
|
@ -1670,6 +1670,13 @@ impl Region {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn needs_infer(&self) -> bool {
|
||||
match *self {
|
||||
ty::ReInfer(..) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn escapes_depth(&self, depth: u32) -> bool {
|
||||
match *self {
|
||||
ty::ReLateBound(debruijn, _) => debruijn.depth > depth,
|
||||
@ -2410,6 +2417,12 @@ pub enum Predicate<'tcx> {
|
||||
/// where <T as TraitRef>::Name == X, approximately.
|
||||
/// See `ProjectionPredicate` struct for details.
|
||||
Projection(PolyProjectionPredicate<'tcx>),
|
||||
|
||||
/// no syntax: T WF
|
||||
WellFormed(Ty<'tcx>),
|
||||
|
||||
/// trait must be object-safe
|
||||
ObjectSafe(ast::DefId),
|
||||
}
|
||||
|
||||
impl<'tcx> Predicate<'tcx> {
|
||||
@ -2495,6 +2508,10 @@ impl<'tcx> Predicate<'tcx> {
|
||||
Predicate::TypeOutlives(ty::Binder(data.subst(tcx, substs))),
|
||||
Predicate::Projection(ty::Binder(ref data)) =>
|
||||
Predicate::Projection(ty::Binder(data.subst(tcx, substs))),
|
||||
Predicate::WellFormed(data) =>
|
||||
Predicate::WellFormed(data.subst(tcx, substs)),
|
||||
Predicate::ObjectSafe(trait_def_id) =>
|
||||
Predicate::ObjectSafe(trait_def_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2567,7 +2584,7 @@ impl<'tcx> PolyProjectionPredicate<'tcx> {
|
||||
|
||||
/// Represents the projection of an associated type. In explicit UFCS
|
||||
/// form this would be written `<T as Trait<..>>::N`.
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ProjectionTy<'tcx> {
|
||||
/// The trait reference `T as Trait<..>`.
|
||||
pub trait_ref: ty::TraitRef<'tcx>,
|
||||
@ -2682,6 +2699,12 @@ impl<'tcx> Predicate<'tcx> {
|
||||
.chain(Some(data.0.ty))
|
||||
.collect()
|
||||
}
|
||||
ty::Predicate::WellFormed(data) => {
|
||||
vec![data]
|
||||
}
|
||||
ty::Predicate::ObjectSafe(_trait_def_id) => {
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
|
||||
// The only reason to collect into a vector here is that I was
|
||||
@ -2699,6 +2722,8 @@ impl<'tcx> Predicate<'tcx> {
|
||||
Predicate::RegionOutlives(ref p) => p.has_escaping_regions(),
|
||||
Predicate::TypeOutlives(ref p) => p.has_escaping_regions(),
|
||||
Predicate::Projection(ref p) => p.has_escaping_regions(),
|
||||
Predicate::WellFormed(p) => p.has_escaping_regions(),
|
||||
Predicate::ObjectSafe(_trait_def_id) => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2710,6 +2735,8 @@ impl<'tcx> Predicate<'tcx> {
|
||||
Predicate::Projection(..) |
|
||||
Predicate::Equate(..) |
|
||||
Predicate::RegionOutlives(..) |
|
||||
Predicate::WellFormed(..) |
|
||||
Predicate::ObjectSafe(..) |
|
||||
Predicate::TypeOutlives(..) => {
|
||||
None
|
||||
}
|
||||
@ -2806,6 +2833,15 @@ pub struct ParameterEnvironment<'a, 'tcx:'a> {
|
||||
/// Caches the results of trait selection. This cache is used
|
||||
/// for things that have to do with the parameters in scope.
|
||||
pub selection_cache: traits::SelectionCache<'tcx>,
|
||||
|
||||
/// Scope that is attached to free regions for this scope. This
|
||||
/// is usually the id of the fn body, but for more abstract scopes
|
||||
/// like structs we often use the node-id of the struct.
|
||||
///
|
||||
/// FIXME(#3696). It would be nice to refactor so that free
|
||||
/// regions don't have this implicit scope and instead introduce
|
||||
/// relationships in the environment.
|
||||
pub free_id: ast::NodeId,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
|
||||
@ -2819,6 +2855,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
|
||||
implicit_region_bound: self.implicit_region_bound,
|
||||
caller_bounds: caller_bounds,
|
||||
selection_cache: traits::SelectionCache::new(),
|
||||
free_id: self.free_id,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2826,6 +2863,18 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
|
||||
match cx.map.find(id) {
|
||||
Some(ast_map::NodeImplItem(ref impl_item)) => {
|
||||
match impl_item.node {
|
||||
ast::TypeImplItem(_) => {
|
||||
// associated types don't have their own entry (for some reason),
|
||||
// so for now just grab environment for the impl
|
||||
let impl_id = cx.map.get_parent(id);
|
||||
let impl_def_id = ast_util::local_def(impl_id);
|
||||
let scheme = cx.lookup_item_type(impl_def_id);
|
||||
let predicates = cx.lookup_predicates(impl_def_id);
|
||||
cx.construct_parameter_environment(impl_item.span,
|
||||
&scheme.generics,
|
||||
&predicates,
|
||||
id)
|
||||
}
|
||||
ast::ConstImplItem(_, _) => {
|
||||
let def_id = ast_util::local_def(id);
|
||||
let scheme = cx.lookup_item_type(def_id);
|
||||
@ -2854,42 +2903,37 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::TypeImplItem(_) => {
|
||||
cx.sess.bug("ParameterEnvironment::for_item(): \
|
||||
can't create a parameter environment \
|
||||
for type impl items")
|
||||
}
|
||||
ast::MacImplItem(_) => cx.sess.bug("unexpanded macro")
|
||||
}
|
||||
}
|
||||
Some(ast_map::NodeTraitItem(trait_item)) => {
|
||||
match trait_item.node {
|
||||
ast::ConstTraitItem(_, ref default) => {
|
||||
match *default {
|
||||
Some(_) => {
|
||||
let def_id = ast_util::local_def(id);
|
||||
let scheme = cx.lookup_item_type(def_id);
|
||||
let predicates = cx.lookup_predicates(def_id);
|
||||
cx.construct_parameter_environment(trait_item.span,
|
||||
&scheme.generics,
|
||||
&predicates,
|
||||
id)
|
||||
}
|
||||
None => {
|
||||
cx.sess.bug("ParameterEnvironment::from_item(): \
|
||||
can't create a parameter environment \
|
||||
for const trait items without defaults")
|
||||
}
|
||||
}
|
||||
ast::TypeTraitItem(..) => {
|
||||
// associated types don't have their own entry (for some reason),
|
||||
// so for now just grab environment for the trait
|
||||
let trait_id = cx.map.get_parent(id);
|
||||
let trait_def_id = ast_util::local_def(trait_id);
|
||||
let trait_def = cx.lookup_trait_def(trait_def_id);
|
||||
let predicates = cx.lookup_predicates(trait_def_id);
|
||||
cx.construct_parameter_environment(trait_item.span,
|
||||
&trait_def.generics,
|
||||
&predicates,
|
||||
id)
|
||||
}
|
||||
ast::MethodTraitItem(_, None) => {
|
||||
cx.sess.span_bug(trait_item.span,
|
||||
"ParameterEnvironment::for_item():
|
||||
can't create a parameter \
|
||||
environment for required trait \
|
||||
methods")
|
||||
ast::ConstTraitItem(..) => {
|
||||
let def_id = ast_util::local_def(id);
|
||||
let scheme = cx.lookup_item_type(def_id);
|
||||
let predicates = cx.lookup_predicates(def_id);
|
||||
cx.construct_parameter_environment(trait_item.span,
|
||||
&scheme.generics,
|
||||
&predicates,
|
||||
id)
|
||||
}
|
||||
ast::MethodTraitItem(_, Some(ref body)) => {
|
||||
ast::MethodTraitItem(_, ref body) => {
|
||||
// for the body-id, use the id of the body
|
||||
// block, unless this is a trait method with
|
||||
// no default, then fallback to the method id.
|
||||
let body_id = body.as_ref().map(|b| b.id).unwrap_or(id);
|
||||
let method_def_id = ast_util::local_def(id);
|
||||
match cx.impl_or_trait_item(method_def_id) {
|
||||
MethodTraitItem(ref method_ty) => {
|
||||
@ -2899,7 +2943,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
|
||||
trait_item.span,
|
||||
method_generics,
|
||||
method_bounds,
|
||||
body.id)
|
||||
body_id)
|
||||
}
|
||||
_ => {
|
||||
cx.sess
|
||||
@ -2909,11 +2953,6 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::TypeTraitItem(..) => {
|
||||
cx.sess.bug("ParameterEnvironment::from_item(): \
|
||||
can't create a parameter environment \
|
||||
for type trait items")
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(ast_map::NodeItem(item)) => {
|
||||
@ -2942,6 +2981,15 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
|
||||
&predicates,
|
||||
id)
|
||||
}
|
||||
ast::ItemTrait(..) => {
|
||||
let def_id = ast_util::local_def(id);
|
||||
let trait_def = cx.lookup_trait_def(def_id);
|
||||
let predicates = cx.lookup_predicates(def_id);
|
||||
cx.construct_parameter_environment(item.span,
|
||||
&trait_def.generics,
|
||||
&predicates,
|
||||
id)
|
||||
}
|
||||
_ => {
|
||||
cx.sess.span_bug(item.span,
|
||||
"ParameterEnvironment::from_item():
|
||||
@ -4144,6 +4192,49 @@ impl<'tcx> TyS<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the regions directly referenced from this type (but
|
||||
/// not types reachable from this type via `walk_tys`). This
|
||||
/// ignores late-bound regions binders.
|
||||
pub fn regions(&self) -> Vec<ty::Region> {
|
||||
match self.sty {
|
||||
TyRef(region, _) => {
|
||||
vec![*region]
|
||||
}
|
||||
TyTrait(ref obj) => {
|
||||
let mut v = vec![obj.bounds.region_bound];
|
||||
v.push_all(obj.principal.skip_binder().substs.regions().as_slice());
|
||||
v
|
||||
}
|
||||
TyEnum(_, substs) |
|
||||
TyStruct(_, substs) => {
|
||||
substs.regions().as_slice().to_vec()
|
||||
}
|
||||
TyClosure(_, ref substs) => {
|
||||
substs.func_substs.regions().as_slice().to_vec()
|
||||
}
|
||||
TyProjection(ref data) => {
|
||||
data.trait_ref.substs.regions().as_slice().to_vec()
|
||||
}
|
||||
TyBareFn(..) |
|
||||
TyBool |
|
||||
TyChar |
|
||||
TyInt(_) |
|
||||
TyUint(_) |
|
||||
TyFloat(_) |
|
||||
TyBox(_) |
|
||||
TyStr |
|
||||
TyArray(_, _) |
|
||||
TySlice(_) |
|
||||
TyRawPtr(_) |
|
||||
TyTuple(_) |
|
||||
TyParam(_) |
|
||||
TyInfer(_) |
|
||||
TyError => {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Walks `ty` and any types appearing within `ty`, invoking the
|
||||
/// callback `f` on each type. If the callback returns false, then the
|
||||
/// children of the current type are ignored.
|
||||
@ -6142,13 +6233,18 @@ impl<'tcx> ctxt<'tcx> {
|
||||
/// themselves. This should really be a unique type; `FreshTy(0)` is a
|
||||
/// popular choice.
|
||||
///
|
||||
/// NB: in some cases, particularly around higher-ranked bounds,
|
||||
/// this function returns a kind of conservative approximation.
|
||||
/// That is, all regions returned by this function are definitely
|
||||
/// required, but there may be other region bounds that are not
|
||||
/// returned, as well as requirements like `for<'a> T: 'a`.
|
||||
///
|
||||
/// Requires that trait definitions have been processed so that we can
|
||||
/// elaborate predicates and walk supertraits.
|
||||
pub fn required_region_bounds(&self,
|
||||
erased_self_ty: Ty<'tcx>,
|
||||
predicates: Vec<ty::Predicate<'tcx>>)
|
||||
-> Vec<ty::Region>
|
||||
{
|
||||
-> Vec<ty::Region> {
|
||||
debug!("required_region_bounds(erased_self_ty={:?}, predicates={:?})",
|
||||
erased_self_ty,
|
||||
predicates);
|
||||
@ -6161,6 +6257,8 @@ impl<'tcx> ctxt<'tcx> {
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::Trait(..) |
|
||||
ty::Predicate::Equate(..) |
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::RegionOutlives(..) => {
|
||||
None
|
||||
}
|
||||
@ -6175,11 +6273,7 @@ impl<'tcx> ctxt<'tcx> {
|
||||
// construct such an object, but this seems
|
||||
// correct even if that code changes).
|
||||
if t == erased_self_ty && !r.has_escaping_regions() {
|
||||
if r.has_escaping_regions() {
|
||||
Some(ty::ReStatic)
|
||||
} else {
|
||||
Some(r)
|
||||
}
|
||||
Some(r)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -6515,12 +6609,19 @@ impl<'tcx> ctxt<'tcx> {
|
||||
|
||||
/// Construct a parameter environment suitable for static contexts or other contexts where there
|
||||
/// are no free type/lifetime parameters in scope.
|
||||
pub fn empty_parameter_environment<'a>(&'a self) -> ParameterEnvironment<'a,'tcx> {
|
||||
pub fn empty_parameter_environment<'a>(&'a self)
|
||||
-> ParameterEnvironment<'a,'tcx> {
|
||||
ty::ParameterEnvironment { tcx: self,
|
||||
free_substs: Substs::empty(),
|
||||
caller_bounds: Vec::new(),
|
||||
implicit_region_bound: ty::ReEmpty,
|
||||
selection_cache: traits::SelectionCache::new(), }
|
||||
selection_cache: traits::SelectionCache::new(),
|
||||
|
||||
// for an empty parameter
|
||||
// environment, there ARE no free
|
||||
// regions, so it shouldn't matter
|
||||
// what we use for the free id
|
||||
free_id: ast::DUMMY_NODE_ID }
|
||||
}
|
||||
|
||||
/// Constructs and returns a substitution that can be applied to move from
|
||||
@ -6604,6 +6705,7 @@ impl<'tcx> ctxt<'tcx> {
|
||||
implicit_region_bound: ty::ReScope(free_id_outlive.to_code_extent()),
|
||||
caller_bounds: predicates,
|
||||
selection_cache: traits::SelectionCache::new(),
|
||||
free_id: free_id,
|
||||
};
|
||||
|
||||
let cause = traits::ObligationCause::misc(span, free_id);
|
||||
@ -6662,6 +6764,8 @@ impl<'tcx> ctxt<'tcx> {
|
||||
ty::Predicate::Equate(..) |
|
||||
ty::Predicate::RegionOutlives(..) |
|
||||
ty::Predicate::TypeOutlives(..) |
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::Projection(..) => {
|
||||
// For now, assume all these where-clauses
|
||||
// may give drop implementation capabilty
|
||||
@ -6906,6 +7010,8 @@ impl<'tcx> fmt::Debug for ty::Predicate<'tcx> {
|
||||
Predicate::RegionOutlives(ref pair) => write!(f, "{:?}", pair),
|
||||
Predicate::TypeOutlives(ref pair) => write!(f, "{:?}", pair),
|
||||
Predicate::Projection(ref pair) => write!(f, "{:?}", pair),
|
||||
Predicate::WellFormed(ty) => write!(f, "WF({:?})", ty),
|
||||
Predicate::ObjectSafe(trait_def_id) => write!(f, "ObjectSafe({:?})", trait_def_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6951,6 +7057,20 @@ impl<'tcx> RegionEscape for Ty<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> RegionEscape for TraitTy<'tcx> {
|
||||
fn has_regions_escaping_depth(&self, depth: u32) -> bool {
|
||||
self.principal.has_regions_escaping_depth(depth) ||
|
||||
self.bounds.has_regions_escaping_depth(depth)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> RegionEscape for ExistentialBounds<'tcx> {
|
||||
fn has_regions_escaping_depth(&self, depth: u32) -> bool {
|
||||
self.region_bound.has_regions_escaping_depth(depth) ||
|
||||
self.projection_bounds.has_regions_escaping_depth(depth)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> RegionEscape for Substs<'tcx> {
|
||||
fn has_regions_escaping_depth(&self, depth: u32) -> bool {
|
||||
self.types.has_regions_escaping_depth(depth) ||
|
||||
@ -7016,6 +7136,8 @@ impl<'tcx> RegionEscape for Predicate<'tcx> {
|
||||
Predicate::RegionOutlives(ref data) => data.has_regions_escaping_depth(depth),
|
||||
Predicate::TypeOutlives(ref data) => data.has_regions_escaping_depth(depth),
|
||||
Predicate::Projection(ref data) => data.has_regions_escaping_depth(depth),
|
||||
Predicate::WellFormed(ty) => ty.has_regions_escaping_depth(depth),
|
||||
Predicate::ObjectSafe(_trait_def_id) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7174,6 +7296,8 @@ impl<'tcx> HasTypeFlags for Predicate<'tcx> {
|
||||
Predicate::RegionOutlives(ref data) => data.has_type_flags(flags),
|
||||
Predicate::TypeOutlives(ref data) => data.has_type_flags(flags),
|
||||
Predicate::Projection(ref data) => data.has_type_flags(flags),
|
||||
Predicate::WellFormed(data) => data.has_type_flags(flags),
|
||||
Predicate::ObjectSafe(_trait_def_id) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -394,6 +394,10 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
|
||||
ty::Predicate::TypeOutlives(binder.fold_with(folder)),
|
||||
ty::Predicate::Projection(ref binder) =>
|
||||
ty::Predicate::Projection(binder.fold_with(folder)),
|
||||
ty::Predicate::WellFormed(data) =>
|
||||
ty::Predicate::WellFormed(data.fold_with(folder)),
|
||||
ty::Predicate::ObjectSafe(trait_def_id) =>
|
||||
ty::Predicate::ObjectSafe(trait_def_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -543,6 +547,7 @@ impl<'a, 'tcx> TypeFoldable<'tcx> for ty::ParameterEnvironment<'a, 'tcx> where '
|
||||
implicit_region_bound: self.implicit_region_bound.fold_with(folder),
|
||||
caller_bounds: self.caller_bounds.fold_with(folder),
|
||||
selection_cache: traits::SelectionCache::new(),
|
||||
free_id: self.free_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
546
src/librustc/middle/wf.rs
Normal file
546
src/librustc/middle/wf.rs
Normal file
@ -0,0 +1,546 @@
|
||||
// Copyright 2012-2013 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.
|
||||
|
||||
use middle::infer::InferCtxt;
|
||||
use middle::outlives::{self, Component};
|
||||
use middle::subst::Substs;
|
||||
use middle::traits;
|
||||
use middle::ty::{self, RegionEscape, ToPredicate, Ty};
|
||||
use std::iter::once;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
use util::common::ErrorReported;
|
||||
|
||||
/// Returns the set of obligations needed to make `ty` well-formed.
|
||||
/// If `ty` contains unresolved inference variables, this may include
|
||||
/// further WF obligations. However, if `ty` IS an unresolved
|
||||
/// inference variable, returns `None`, because we are not able to
|
||||
/// make any progress at all. This is to prevent "livelock" where we
|
||||
/// say "$0 is WF if $0 is WF".
|
||||
pub fn obligations<'a,'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
body_id: ast::NodeId,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
rfc1214: bool)
|
||||
-> Option<Vec<traits::PredicateObligation<'tcx>>>
|
||||
{
|
||||
let mut wf = WfPredicates { infcx: infcx,
|
||||
body_id: body_id,
|
||||
span: span,
|
||||
out: vec![],
|
||||
rfc1214: rfc1214 };
|
||||
if wf.compute(ty) {
|
||||
debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out);
|
||||
let result = wf.normalize();
|
||||
debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result);
|
||||
Some(result)
|
||||
} else {
|
||||
None // no progress made, return None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the obligations that make this trait reference
|
||||
/// well-formed. For example, if there is a trait `Set` defined like
|
||||
/// `trait Set<K:Eq>`, then the trait reference `Foo: Set<Bar>` is WF
|
||||
/// if `Bar: Eq`.
|
||||
pub fn trait_obligations<'a,'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
body_id: ast::NodeId,
|
||||
trait_ref: &ty::TraitRef<'tcx>,
|
||||
span: Span,
|
||||
rfc1214: bool)
|
||||
-> Vec<traits::PredicateObligation<'tcx>>
|
||||
{
|
||||
let mut wf = WfPredicates { infcx: infcx, body_id: body_id, span: span,
|
||||
out: vec![], rfc1214: rfc1214 };
|
||||
wf.compute_trait_ref(trait_ref);
|
||||
wf.normalize()
|
||||
}
|
||||
|
||||
pub fn predicate_obligations<'a,'tcx>(infcx: &InferCtxt<'a, 'tcx>,
|
||||
body_id: ast::NodeId,
|
||||
predicate: &ty::Predicate<'tcx>,
|
||||
span: Span,
|
||||
rfc1214: bool)
|
||||
-> Vec<traits::PredicateObligation<'tcx>>
|
||||
{
|
||||
let mut wf = WfPredicates { infcx: infcx, body_id: body_id, span: span,
|
||||
out: vec![], rfc1214: rfc1214 };
|
||||
|
||||
// (*) ok to skip binders, because wf code is prepared for it
|
||||
match *predicate {
|
||||
ty::Predicate::Trait(ref t) => {
|
||||
wf.compute_trait_ref(&t.skip_binder().trait_ref); // (*)
|
||||
}
|
||||
ty::Predicate::Equate(ref t) => {
|
||||
wf.compute(t.skip_binder().0);
|
||||
wf.compute(t.skip_binder().1);
|
||||
}
|
||||
ty::Predicate::RegionOutlives(..) => {
|
||||
}
|
||||
ty::Predicate::TypeOutlives(ref t) => {
|
||||
wf.compute(t.skip_binder().0);
|
||||
}
|
||||
ty::Predicate::Projection(ref t) => {
|
||||
let t = t.skip_binder(); // (*)
|
||||
wf.compute_projection(t.projection_ty);
|
||||
wf.compute(t.ty);
|
||||
}
|
||||
ty::Predicate::WellFormed(t) => {
|
||||
wf.compute(t);
|
||||
}
|
||||
ty::Predicate::ObjectSafe(_) => {
|
||||
}
|
||||
}
|
||||
|
||||
wf.normalize()
|
||||
}
|
||||
|
||||
/// Implied bounds are region relationships that we deduce
|
||||
/// automatically. The idea is that (e.g.) a caller must check that a
|
||||
/// function's argument types are well-formed immediately before
|
||||
/// calling that fn, and hence the *callee* can assume that its
|
||||
/// argument types are well-formed. This may imply certain relationships
|
||||
/// between generic parameters. For example:
|
||||
///
|
||||
/// fn foo<'a,T>(x: &'a T)
|
||||
///
|
||||
/// can only be called with a `'a` and `T` such that `&'a T` is WF.
|
||||
/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
|
||||
#[derive(Debug)]
|
||||
pub enum ImpliedBound<'tcx> {
|
||||
RegionSubRegion(ty::Region, ty::Region),
|
||||
RegionSubParam(ty::Region, ty::ParamTy),
|
||||
RegionSubProjection(ty::Region, ty::ProjectionTy<'tcx>),
|
||||
}
|
||||
|
||||
/// Compute the implied bounds that a callee/impl can assume based on
|
||||
/// the fact that caller/projector has ensured that `ty` is WF. See
|
||||
/// the `ImpliedBound` type for more details.
|
||||
pub fn implied_bounds<'a,'tcx>(
|
||||
infcx: &'a InferCtxt<'a,'tcx>,
|
||||
body_id: ast::NodeId,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span)
|
||||
-> Vec<ImpliedBound<'tcx>>
|
||||
{
|
||||
// Sometimes when we ask what it takes for T: WF, we get back that
|
||||
// U: WF is required; in that case, we push U onto this stack and
|
||||
// process it next. Currently (at least) these resulting
|
||||
// predicates are always guaranteed to be a subset of the original
|
||||
// type, so we need not fear non-termination.
|
||||
let mut wf_types = vec![ty];
|
||||
|
||||
let mut implied_bounds = vec![];
|
||||
|
||||
while let Some(ty) = wf_types.pop() {
|
||||
// Compute the obligations for `ty` to be well-formed. If `ty` is
|
||||
// an unresolved inference variable, just substituted an empty set
|
||||
// -- because the return type here is going to be things we *add*
|
||||
// to the environment, it's always ok for this set to be smaller
|
||||
// than the ultimate set. (Note: normally there won't be
|
||||
// unresolved inference variables here anyway, but there might be
|
||||
// during typeck under some circumstances.)
|
||||
let obligations = obligations(infcx, body_id, ty, span, false).unwrap_or(vec![]);
|
||||
|
||||
// From the full set of obligations, just filter down to the
|
||||
// region relationships.
|
||||
implied_bounds.extend(
|
||||
obligations
|
||||
.into_iter()
|
||||
.flat_map(|obligation| {
|
||||
assert!(!obligation.has_escaping_regions());
|
||||
match obligation.predicate {
|
||||
ty::Predicate::Trait(..) |
|
||||
ty::Predicate::Equate(..) |
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::ObjectSafe(..) =>
|
||||
vec![],
|
||||
|
||||
ty::Predicate::WellFormed(subty) => {
|
||||
wf_types.push(subty);
|
||||
vec![]
|
||||
}
|
||||
|
||||
ty::Predicate::RegionOutlives(ref data) =>
|
||||
match infcx.tcx.no_late_bound_regions(data) {
|
||||
None =>
|
||||
vec![],
|
||||
Some(ty::OutlivesPredicate(r_a, r_b)) =>
|
||||
vec![ImpliedBound::RegionSubRegion(r_b, r_a)],
|
||||
},
|
||||
|
||||
ty::Predicate::TypeOutlives(ref data) =>
|
||||
match infcx.tcx.no_late_bound_regions(data) {
|
||||
None => vec![],
|
||||
Some(ty::OutlivesPredicate(ty_a, r_b)) => {
|
||||
let components = outlives::components(infcx, ty_a);
|
||||
implied_bounds_from_components(r_b, components)
|
||||
}
|
||||
},
|
||||
}}));
|
||||
}
|
||||
|
||||
implied_bounds
|
||||
}
|
||||
|
||||
/// When we have an implied bound that `T: 'a`, we can further break
|
||||
/// this down to determine what relationships would have to hold for
|
||||
/// `T: 'a` to hold. We get to assume that the caller has validated
|
||||
/// those relationships.
|
||||
fn implied_bounds_from_components<'tcx>(sub_region: ty::Region,
|
||||
sup_components: Vec<Component<'tcx>>)
|
||||
-> Vec<ImpliedBound<'tcx>>
|
||||
{
|
||||
sup_components
|
||||
.into_iter()
|
||||
.flat_map(|component| {
|
||||
match component {
|
||||
Component::Region(r) =>
|
||||
vec!(ImpliedBound::RegionSubRegion(sub_region, r)),
|
||||
Component::Param(p) =>
|
||||
vec!(ImpliedBound::RegionSubParam(sub_region, p)),
|
||||
Component::Projection(p) =>
|
||||
vec!(ImpliedBound::RegionSubProjection(sub_region, p)),
|
||||
Component::EscapingProjection(_) =>
|
||||
// If the projection has escaping regions, don't
|
||||
// try to infer any implied bounds even for its
|
||||
// free components. This is conservative, because
|
||||
// the caller will still have to prove that those
|
||||
// free components outlive `sub_region`. But the
|
||||
// idea is that the WAY that the caller proves
|
||||
// that may change in the future and we want to
|
||||
// give ourselves room to get smarter here.
|
||||
vec!(),
|
||||
Component::UnresolvedInferenceVariable(..) =>
|
||||
vec!(),
|
||||
Component::RFC1214(components) =>
|
||||
implied_bounds_from_components(sub_region, components),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
struct WfPredicates<'a,'tcx:'a> {
|
||||
infcx: &'a InferCtxt<'a, 'tcx>,
|
||||
body_id: ast::NodeId,
|
||||
span: Span,
|
||||
out: Vec<traits::PredicateObligation<'tcx>>,
|
||||
rfc1214: bool
|
||||
}
|
||||
|
||||
impl<'a,'tcx> WfPredicates<'a,'tcx> {
|
||||
fn rfc1214<R,F:FnOnce(&mut WfPredicates<'a,'tcx>) -> R>(&mut self, f: F) -> R {
|
||||
let b = mem::replace(&mut self.rfc1214, true);
|
||||
let r = f(self);
|
||||
self.rfc1214 = b;
|
||||
r
|
||||
}
|
||||
|
||||
fn cause(&mut self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> {
|
||||
if !self.rfc1214 {
|
||||
traits::ObligationCause::new(self.span, self.body_id, code)
|
||||
} else {
|
||||
let code = traits::ObligationCauseCode::RFC1214(Rc::new(code));
|
||||
traits::ObligationCause::new(self.span, self.body_id, code)
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize(&mut self) -> Vec<traits::PredicateObligation<'tcx>> {
|
||||
let cause = self.cause(traits::MiscObligation);
|
||||
let infcx = &mut self.infcx;
|
||||
self.out.iter()
|
||||
.inspect(|pred| assert!(!pred.has_escaping_regions()))
|
||||
.flat_map(|pred| {
|
||||
let mut selcx = traits::SelectionContext::new(infcx);
|
||||
let pred = traits::normalize(&mut selcx, cause.clone(), pred);
|
||||
once(pred.value).chain(pred.obligations)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn compute_rfc1214(&mut self, ty: Ty<'tcx>) {
|
||||
let b = mem::replace(&mut self.rfc1214, true);
|
||||
for subty in ty.walk().skip(1) {
|
||||
self.compute(subty);
|
||||
}
|
||||
self.rfc1214 = b;
|
||||
}
|
||||
|
||||
/// Pushes the obligations required for `trait_ref` to be WF into
|
||||
/// `self.out`.
|
||||
fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>) {
|
||||
let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs);
|
||||
self.out.extend(obligations);
|
||||
|
||||
let cause = self.cause(traits::MiscObligation);
|
||||
self.out.extend(
|
||||
trait_ref.substs.types
|
||||
.as_slice()
|
||||
.iter()
|
||||
.filter(|ty| !ty.has_escaping_regions())
|
||||
.map(|ty| traits::Obligation::new(cause.clone(),
|
||||
ty::Predicate::WellFormed(ty))));
|
||||
}
|
||||
|
||||
/// Pushes the obligations required for `trait_ref::Item` to be WF
|
||||
/// into `self.out`.
|
||||
fn compute_projection(&mut self, data: ty::ProjectionTy<'tcx>) {
|
||||
// A projection is well-formed if (a) the trait ref itself is
|
||||
// WF WF and (b) the trait-ref holds. (It may also be
|
||||
// normalizable and be WF that way.)
|
||||
|
||||
self.compute_trait_ref(&data.trait_ref);
|
||||
|
||||
if !data.has_escaping_regions() {
|
||||
let predicate = data.trait_ref.to_predicate();
|
||||
let cause = self.cause(traits::ProjectionWf(data));
|
||||
self.out.push(traits::Obligation::new(cause, predicate));
|
||||
}
|
||||
}
|
||||
|
||||
/// Push new obligations into `out`. Returns true if it was able
|
||||
/// to generate all the predicates needed to validate that `ty0`
|
||||
/// is WF. Returns false if `ty0` is an unresolved type variable,
|
||||
/// in which case we are not able to simplify at all.
|
||||
fn compute(&mut self, ty0: Ty<'tcx>) -> bool {
|
||||
let mut subtys = ty0.walk();
|
||||
while let Some(ty) = subtys.next() {
|
||||
match ty.sty {
|
||||
ty::TyBool |
|
||||
ty::TyChar |
|
||||
ty::TyInt(..) |
|
||||
ty::TyUint(..) |
|
||||
ty::TyFloat(..) |
|
||||
ty::TyError |
|
||||
ty::TyStr |
|
||||
ty::TyParam(_) => {
|
||||
// WfScalar, WfParameter, etc
|
||||
}
|
||||
|
||||
ty::TySlice(subty) |
|
||||
ty::TyArray(subty, _) => {
|
||||
self.rfc1214(|this| {
|
||||
if !subty.has_escaping_regions() {
|
||||
let cause = this.cause(traits::SliceOrArrayElem);
|
||||
match traits::trait_ref_for_builtin_bound(this.infcx.tcx,
|
||||
ty::BoundSized,
|
||||
subty) {
|
||||
Ok(trait_ref) => {
|
||||
this.out.push(
|
||||
traits::Obligation::new(cause,
|
||||
trait_ref.to_predicate()));
|
||||
}
|
||||
Err(ErrorReported) => { }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
ty::TyBox(_) |
|
||||
ty::TyTuple(_) |
|
||||
ty::TyRawPtr(_) => {
|
||||
// simple cases that are WF if their type args are WF
|
||||
}
|
||||
|
||||
ty::TyProjection(data) => {
|
||||
subtys.skip_current_subtree(); // subtree handled by compute_projection
|
||||
self.compute_projection(data);
|
||||
}
|
||||
|
||||
ty::TyEnum(def, substs) |
|
||||
ty::TyStruct(def, substs) => {
|
||||
// WfNominalType
|
||||
let obligations = self.nominal_obligations(def.did, substs);
|
||||
self.out.extend(obligations);
|
||||
}
|
||||
|
||||
ty::TyRef(r, mt) => {
|
||||
// WfReference
|
||||
if !r.has_escaping_regions() && !mt.ty.has_escaping_regions() {
|
||||
let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
|
||||
self.out.push(
|
||||
traits::Obligation::new(
|
||||
cause,
|
||||
ty::Predicate::TypeOutlives(
|
||||
ty::Binder(
|
||||
ty::OutlivesPredicate(mt.ty, *r)))));
|
||||
}
|
||||
}
|
||||
|
||||
ty::TyClosure(..) => {
|
||||
// the types in a closure are always the types of
|
||||
// local variables (or possibly references to local
|
||||
// variables), which are separately checked w/r/t
|
||||
// WFedness.
|
||||
}
|
||||
|
||||
ty::TyBareFn(..) => {
|
||||
// process the bound types; because the old implicator
|
||||
// did not do this, go into RFC1214 mode.
|
||||
subtys.skip_current_subtree();
|
||||
self.compute_rfc1214(ty);
|
||||
}
|
||||
|
||||
ty::TyTrait(ref data) => {
|
||||
// WfObject
|
||||
//
|
||||
// Here, we defer WF checking due to higher-ranked
|
||||
// regions. This is perhaps not ideal.
|
||||
self.from_object_ty(ty, data);
|
||||
|
||||
// FIXME(#27579) RFC also considers adding trait
|
||||
// obligations that don't refer to Self and
|
||||
// checking those
|
||||
|
||||
let cause = self.cause(traits::MiscObligation);
|
||||
self.out.push(
|
||||
traits::Obligation::new(
|
||||
cause,
|
||||
ty::Predicate::ObjectSafe(data.principal_def_id())));
|
||||
|
||||
// process the bound types; because the old implicator
|
||||
// did not do this, go into RFC1214 mode.
|
||||
subtys.skip_current_subtree();
|
||||
self.compute_rfc1214(ty);
|
||||
}
|
||||
|
||||
// Inference variables are the complicated case, since we don't
|
||||
// know what type they are. We do two things:
|
||||
//
|
||||
// 1. Check if they have been resolved, and if so proceed with
|
||||
// THAT type.
|
||||
// 2. If not, check whether this is the type that we
|
||||
// started with (ty0). In that case, we've made no
|
||||
// progress at all, so return false. Otherwise,
|
||||
// we've at least simplified things (i.e., we went
|
||||
// from `Vec<$0>: WF` to `$0: WF`, so we can
|
||||
// register a pending obligation and keep
|
||||
// moving. (Goal is that an "inductive hypothesis"
|
||||
// is satisfied to ensure termination.)
|
||||
ty::TyInfer(_) => {
|
||||
let ty = self.infcx.shallow_resolve(ty);
|
||||
if let ty::TyInfer(_) = ty.sty { // not yet resolved...
|
||||
if ty == ty0 { // ...this is the type we started from! no progress.
|
||||
return false;
|
||||
}
|
||||
|
||||
let cause = self.cause(traits::MiscObligation);
|
||||
self.out.push( // ...not the type we started from, so we made progress.
|
||||
traits::Obligation::new(cause, ty::Predicate::WellFormed(ty)));
|
||||
} else {
|
||||
// Yes, resolved, proceed with the
|
||||
// result. Should never return false because
|
||||
// `ty` is not a TyInfer.
|
||||
assert!(self.compute(ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we made it through that loop above, we made progress!
|
||||
return true;
|
||||
}
|
||||
|
||||
fn nominal_obligations(&mut self,
|
||||
def_id: ast::DefId,
|
||||
substs: &Substs<'tcx>)
|
||||
-> Vec<traits::PredicateObligation<'tcx>>
|
||||
{
|
||||
let predicates =
|
||||
self.infcx.tcx.lookup_predicates(def_id)
|
||||
.instantiate(self.infcx.tcx, substs);
|
||||
let cause = self.cause(traits::ItemObligation(def_id));
|
||||
predicates.predicates
|
||||
.into_iter()
|
||||
.map(|pred| traits::Obligation::new(cause.clone(), pred))
|
||||
.filter(|pred| !pred.has_escaping_regions())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn from_object_ty(&mut self, ty: Ty<'tcx>, data: &ty::TraitTy<'tcx>) {
|
||||
// Imagine a type like this:
|
||||
//
|
||||
// trait Foo { }
|
||||
// trait Bar<'c> : 'c { }
|
||||
//
|
||||
// &'b (Foo+'c+Bar<'d>)
|
||||
// ^
|
||||
//
|
||||
// In this case, the following relationships must hold:
|
||||
//
|
||||
// 'b <= 'c
|
||||
// 'd <= 'c
|
||||
//
|
||||
// The first conditions is due to the normal region pointer
|
||||
// rules, which say that a reference cannot outlive its
|
||||
// referent.
|
||||
//
|
||||
// The final condition may be a bit surprising. In particular,
|
||||
// you may expect that it would have been `'c <= 'd`, since
|
||||
// usually lifetimes of outer things are conservative
|
||||
// approximations for inner things. However, it works somewhat
|
||||
// differently with trait objects: here the idea is that if the
|
||||
// user specifies a region bound (`'c`, in this case) it is the
|
||||
// "master bound" that *implies* that bounds from other traits are
|
||||
// all met. (Remember that *all bounds* in a type like
|
||||
// `Foo+Bar+Zed` must be met, not just one, hence if we write
|
||||
// `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and
|
||||
// 'y.)
|
||||
//
|
||||
// Note: in fact we only permit builtin traits, not `Bar<'d>`, I
|
||||
// am looking forward to the future here.
|
||||
|
||||
if !data.has_escaping_regions() {
|
||||
let implicit_bounds =
|
||||
object_region_bounds(self.infcx.tcx,
|
||||
&data.principal,
|
||||
data.bounds.builtin_bounds);
|
||||
|
||||
let explicit_bound = data.bounds.region_bound;
|
||||
|
||||
for implicit_bound in implicit_bounds {
|
||||
let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
|
||||
let outlives = ty::Binder(ty::OutlivesPredicate(explicit_bound, implicit_bound));
|
||||
self.out.push(traits::Obligation::new(cause, outlives.to_predicate()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given an object type like `SomeTrait+Send`, computes the lifetime
|
||||
/// bounds that must hold on the elided self type. These are derived
|
||||
/// from the declarations of `SomeTrait`, `Send`, and friends -- if
|
||||
/// they declare `trait SomeTrait : 'static`, for example, then
|
||||
/// `'static` would appear in the list. The hard work is done by
|
||||
/// `ty::required_region_bounds`, see that for more information.
|
||||
pub fn object_region_bounds<'tcx>(
|
||||
tcx: &ty::ctxt<'tcx>,
|
||||
principal: &ty::PolyTraitRef<'tcx>,
|
||||
others: ty::BuiltinBounds)
|
||||
-> Vec<ty::Region>
|
||||
{
|
||||
// Since we don't actually *know* the self type for an object,
|
||||
// this "open(err)" serves as a kind of dummy standin -- basically
|
||||
// a skolemized type.
|
||||
let open_ty = tcx.mk_infer(ty::FreshTy(0));
|
||||
|
||||
// Note that we preserve the overall binding levels here.
|
||||
assert!(!open_ty.has_escaping_regions());
|
||||
let substs = tcx.mk_substs(principal.0.substs.with_self_ty(open_ty));
|
||||
let trait_refs = vec!(ty::Binder(ty::TraitRef::new(principal.0.def_id, substs)));
|
||||
|
||||
let mut predicates = others.to_predicates(tcx, open_ty);
|
||||
predicates.extend(trait_refs.iter().map(|t| t.to_predicate()));
|
||||
|
||||
tcx.required_region_bounds(open_ty, predicates)
|
||||
}
|
||||
|
@ -90,6 +90,13 @@ impl Session {
|
||||
}
|
||||
self.diagnostic().handler().fatal(msg)
|
||||
}
|
||||
pub fn span_err_or_warn(&self, is_warning: bool, sp: Span, msg: &str) {
|
||||
if is_warning {
|
||||
self.span_warn(sp, msg);
|
||||
} else {
|
||||
self.span_err(sp, msg);
|
||||
}
|
||||
}
|
||||
pub fn span_err(&self, sp: Span, msg: &str) {
|
||||
if self.opts.treat_err_as_bug {
|
||||
self.span_bug(sp, msg);
|
||||
@ -99,6 +106,13 @@ impl Session {
|
||||
None => self.diagnostic().span_err(sp, msg)
|
||||
}
|
||||
}
|
||||
pub fn note_rfc_1214(&self, span: Span) {
|
||||
self.span_note(
|
||||
span,
|
||||
&format!("this warning results from recent bug fixes and clarifications; \
|
||||
it will become a HARD ERROR in the next release. \
|
||||
See RFC 1214 for details."));
|
||||
}
|
||||
pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) {
|
||||
if self.opts.treat_err_as_bug {
|
||||
self.span_bug(sp, msg);
|
||||
|
@ -831,6 +831,11 @@ impl<'tcx> fmt::Display for ty::Predicate<'tcx> {
|
||||
ty::Predicate::RegionOutlives(ref predicate) => write!(f, "{}", predicate),
|
||||
ty::Predicate::TypeOutlives(ref predicate) => write!(f, "{}", predicate),
|
||||
ty::Predicate::Projection(ref predicate) => write!(f, "{}", predicate),
|
||||
ty::Predicate::WellFormed(ty) => write!(f, "{} well-formed", ty),
|
||||
ty::Predicate::ObjectSafe(trait_def_id) =>
|
||||
ty::tls::with(|tcx| {
|
||||
write!(f, "the trait `{}` is object-safe", tcx.item_path_str(trait_def_id))
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -272,7 +272,7 @@ impl<'tcx,K> UnificationTable<K>
|
||||
|
||||
impl<'tcx,K,V> UnificationTable<K>
|
||||
where K: UnifyKey<Value=Option<V>>,
|
||||
V: Clone+PartialEq,
|
||||
V: Clone+PartialEq+Debug,
|
||||
{
|
||||
pub fn unify_var_var(&mut self,
|
||||
a_id: K,
|
||||
|
@ -52,7 +52,7 @@ use middle::astconv_util::{prim_ty_to_ty, check_path_args, NO_TPS, NO_REGIONS};
|
||||
use middle::const_eval::{self, ConstVal};
|
||||
use middle::const_eval::EvalHint::UncheckedExprHint;
|
||||
use middle::def;
|
||||
use middle::implicator::object_region_bounds;
|
||||
use middle::wf::object_region_bounds;
|
||||
use middle::resolve_lifetime as rl;
|
||||
use middle::privacy::{AllPublic, LastMod};
|
||||
use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs, ParamSpace};
|
||||
@ -1523,12 +1523,13 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
|
||||
ast_ty: &ast::Ty)
|
||||
-> Ty<'tcx>
|
||||
{
|
||||
debug!("ast_ty_to_ty(ast_ty={:?})",
|
||||
ast_ty);
|
||||
debug!("ast_ty_to_ty(id={:?}, ast_ty={:?})",
|
||||
ast_ty.id, ast_ty);
|
||||
|
||||
let tcx = this.tcx();
|
||||
|
||||
if let Some(&ty) = tcx.ast_ty_to_ty_cache.borrow().get(&ast_ty.id) {
|
||||
debug!("ast_ty_to_ty: id={:?} ty={:?} (cached)", ast_ty.id, ty);
|
||||
return ty;
|
||||
}
|
||||
|
||||
@ -1667,6 +1668,7 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
|
||||
}
|
||||
};
|
||||
|
||||
debug!("ast_ty_to_ty: id={:?} ty={:?}", ast_ty.id, typ);
|
||||
tcx.ast_ty_to_ty_cache.borrow_mut().insert(ast_ty.id, typ);
|
||||
return typ;
|
||||
}
|
||||
|
@ -177,6 +177,8 @@ fn deduce_expectations_from_obligations<'a,'tcx>(
|
||||
ty::Predicate::Equate(..) => None,
|
||||
ty::Predicate::RegionOutlives(..) => None,
|
||||
ty::Predicate::TypeOutlives(..) => None,
|
||||
ty::Predicate::WellFormed(..) => None,
|
||||
ty::Predicate::ObjectSafe(..) => None,
|
||||
};
|
||||
opt_trait_ref
|
||||
.and_then(|trait_ref| self_type_matches_expected_vid(fcx, trait_ref, expected_vid))
|
||||
|
@ -254,9 +254,9 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
|
||||
/// This function is meant to by applied to the type for every
|
||||
/// expression in the program.
|
||||
pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
|
||||
typ: ty::Ty<'tcx>,
|
||||
span: Span,
|
||||
scope: region::CodeExtent) {
|
||||
typ: ty::Ty<'tcx>,
|
||||
span: Span,
|
||||
scope: region::CodeExtent) {
|
||||
debug!("check_safety_of_destructor_if_necessary typ: {:?} scope: {:?}",
|
||||
typ, scope);
|
||||
|
||||
|
@ -432,7 +432,9 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
|
||||
traits::ObligationCause::misc(self.span, self.fcx.body_id),
|
||||
method_predicates);
|
||||
|
||||
self.fcx.add_default_region_param_bounds(
|
||||
// this is a projection from a trait reference, so we have to
|
||||
// make sure that the trait reference inputs are well-formed.
|
||||
self.fcx.add_wf_bounds(
|
||||
all_substs,
|
||||
self.call_expr);
|
||||
}
|
||||
|
@ -480,6 +480,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
|
||||
ty::Predicate::Equate(..) |
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::RegionOutlives(..) |
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::TypeOutlives(..) => {
|
||||
None
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ use middle::infer;
|
||||
use middle::infer::type_variable;
|
||||
use middle::pat_util::{self, pat_id_map};
|
||||
use middle::privacy::{AllPublic, LastMod};
|
||||
use middle::region::{self, CodeExtent};
|
||||
use middle::region::{self};
|
||||
use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace, TypeSpace};
|
||||
use middle::traits::{self, report_fulfillment_errors};
|
||||
use middle::ty::{FnSig, GenericPredicates, TypeScheme};
|
||||
@ -133,7 +133,8 @@ pub mod coercion;
|
||||
pub mod demand;
|
||||
pub mod method;
|
||||
mod upvar;
|
||||
pub mod wf;
|
||||
mod wf;
|
||||
mod wfcheck;
|
||||
mod cast;
|
||||
mod closure;
|
||||
mod callee;
|
||||
@ -382,7 +383,12 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckItemBodiesVisitor<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_item_types(ccx: &CrateCtxt) {
|
||||
pub fn check_wf_old(ccx: &CrateCtxt) {
|
||||
// FIXME(#25759). The new code below is much more reliable but (for now)
|
||||
// only generates warnings. So as to ensure that we continue
|
||||
// getting errors where we used to get errors, we run the old wf
|
||||
// code first and abort if it encounters any errors. If no abort
|
||||
// comes, we run the new code and issue warnings.
|
||||
let krate = ccx.tcx.map.krate();
|
||||
let mut visit = wf::CheckTypeWellFormedVisitor::new(ccx);
|
||||
visit::walk_crate(&mut visit, krate);
|
||||
@ -390,17 +396,34 @@ pub fn check_item_types(ccx: &CrateCtxt) {
|
||||
// If types are not well-formed, it leads to all manner of errors
|
||||
// downstream, so stop reporting errors at this point.
|
||||
ccx.tcx.sess.abort_if_errors();
|
||||
}
|
||||
|
||||
let mut visit = CheckItemTypesVisitor { ccx: ccx };
|
||||
pub fn check_wf_new(ccx: &CrateCtxt) {
|
||||
let krate = ccx.tcx.map.krate();
|
||||
let mut visit = wfcheck::CheckTypeWellFormedVisitor::new(ccx);
|
||||
visit::walk_crate(&mut visit, krate);
|
||||
|
||||
// If types are not well-formed, it leads to all manner of errors
|
||||
// downstream, so stop reporting errors at this point.
|
||||
ccx.tcx.sess.abort_if_errors();
|
||||
}
|
||||
|
||||
pub fn check_item_types(ccx: &CrateCtxt) {
|
||||
let krate = ccx.tcx.map.krate();
|
||||
let mut visit = CheckItemTypesVisitor { ccx: ccx };
|
||||
visit::walk_crate(&mut visit, krate);
|
||||
ccx.tcx.sess.abort_if_errors();
|
||||
}
|
||||
|
||||
pub fn check_item_bodies(ccx: &CrateCtxt) {
|
||||
let krate = ccx.tcx.map.krate();
|
||||
let mut visit = CheckItemBodiesVisitor { ccx: ccx };
|
||||
visit::walk_crate(&mut visit, krate);
|
||||
|
||||
ccx.tcx.sess.abort_if_errors();
|
||||
}
|
||||
|
||||
pub fn check_drop_impls(ccx: &CrateCtxt) {
|
||||
for drop_method_did in ccx.tcx.destructors.borrow().iter() {
|
||||
if drop_method_did.krate == ast::LOCAL_CRATE {
|
||||
let drop_impl_did = ccx.tcx.map.get_parent_did(drop_method_did.node);
|
||||
@ -445,9 +468,8 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||
|
||||
fcx.select_all_obligations_and_apply_defaults();
|
||||
upvar::closure_analyze_fn(&fcx, fn_id, decl, body);
|
||||
fcx.select_all_obligations_or_error();
|
||||
fcx.select_obligations_where_possible();
|
||||
fcx.check_casts();
|
||||
|
||||
fcx.select_all_obligations_or_error(); // Casts can introduce new obligations.
|
||||
|
||||
regionck::regionck_fn(&fcx, fn_id, fn_span, decl, body);
|
||||
@ -587,7 +609,7 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
|
||||
|
||||
if let ty::FnConverging(ret_ty) = ret_ty {
|
||||
fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::ReturnType);
|
||||
fn_sig_tys.push(ret_ty);
|
||||
fn_sig_tys.push(ret_ty); // FIXME(#25759) just take implied bounds from the arguments
|
||||
}
|
||||
|
||||
debug!("fn-sig-map: fn_id={} fn_sig_tys={:?}",
|
||||
@ -601,6 +623,14 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
|
||||
|
||||
// Add formal parameters.
|
||||
for (arg_ty, input) in arg_tys.iter().zip(&decl.inputs) {
|
||||
// The type of the argument must be well-formed.
|
||||
//
|
||||
// NB -- this is now checked in wfcheck, but that
|
||||
// currently only results in warnings, so we issue an
|
||||
// old-style WF obligation here so that we still get the
|
||||
// errors that we used to get.
|
||||
fcx.register_old_wf_obligation(arg_ty, input.ty.span, traits::MiscObligation);
|
||||
|
||||
// Create type variables for each argument.
|
||||
pat_util::pat_bindings(
|
||||
&tcx.def_map,
|
||||
@ -1508,10 +1538,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub fn to_ty(&self, ast_t: &ast::Ty) -> Ty<'tcx> {
|
||||
let t = ast_ty_to_ty(self, self, ast_t);
|
||||
|
||||
let mut bounds_checker = wf::BoundsChecker::new(self,
|
||||
self.body_id,
|
||||
None);
|
||||
bounds_checker.check_ty(t, ast_t.span);
|
||||
// Generally speaking, we must check that types entered by the
|
||||
// user are well-formed. This is not true for `_`, since those
|
||||
// types are generated by inference. Now, you might think that
|
||||
// we could as well generate a WF obligation -- but
|
||||
// unfortunately that breaks code like `foo as *const _`,
|
||||
// because those type variables wind up being unconstrained
|
||||
// until very late. Nasty. Probably it'd be best to refactor
|
||||
// that code path, but that's tricky because of
|
||||
// defaults. Argh!
|
||||
match ast_t.node {
|
||||
ast::TyInfer => { }
|
||||
_ => { self.register_wf_obligation(t, ast_t.span, traits::MiscObligation); }
|
||||
}
|
||||
|
||||
t
|
||||
}
|
||||
@ -1630,15 +1669,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
fulfillment_cx.register_region_obligation(ty, region, cause);
|
||||
}
|
||||
|
||||
pub fn add_default_region_param_bounds(&self,
|
||||
substs: &Substs<'tcx>,
|
||||
expr: &ast::Expr)
|
||||
/// Registers an obligation for checking later, during regionck, that the type `ty` must
|
||||
/// outlive the region `r`.
|
||||
pub fn register_wf_obligation(&self,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
code: traits::ObligationCauseCode<'tcx>)
|
||||
{
|
||||
// WF obligations never themselves fail, so no real need to give a detailed cause:
|
||||
let cause = traits::ObligationCause::new(span, self.body_id, code);
|
||||
self.register_predicate(traits::Obligation::new(cause, ty::Predicate::WellFormed(ty)));
|
||||
}
|
||||
|
||||
pub fn register_old_wf_obligation(&self,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
code: traits::ObligationCauseCode<'tcx>)
|
||||
{
|
||||
// Registers an "old-style" WF obligation that uses the
|
||||
// implicator code. This is basically a buggy version of
|
||||
// `register_wf_obligation` that is being kept around
|
||||
// temporarily just to help with phasing in the newer rules.
|
||||
//
|
||||
// FIXME(#27579) all uses of this should be migrated to register_wf_obligation eventually
|
||||
let cause = traits::ObligationCause::new(span, self.body_id, code);
|
||||
self.register_region_obligation(ty, ty::ReEmpty, cause);
|
||||
}
|
||||
|
||||
/// Registers obligations that all types appearing in `substs` are well-formed.
|
||||
pub fn add_wf_bounds(&self, substs: &Substs<'tcx>, expr: &ast::Expr)
|
||||
{
|
||||
for &ty in &substs.types {
|
||||
let default_bound = ty::ReScope(CodeExtent::from_node_id(expr.id));
|
||||
let cause = traits::ObligationCause::new(expr.span, self.body_id,
|
||||
traits::MiscObligation);
|
||||
self.register_region_obligation(ty, default_bound, cause);
|
||||
self.register_wf_obligation(ty, expr.span, traits::MiscObligation);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2370,6 +2432,12 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
||||
1
|
||||
};
|
||||
|
||||
// All the input types from the fn signature must outlive the call
|
||||
// so as to validate implied bounds.
|
||||
for &fn_input_ty in fn_inputs {
|
||||
fcx.register_wf_obligation(fn_input_ty, sp, traits::MiscObligation);
|
||||
}
|
||||
|
||||
let mut expected_arg_tys = expected_arg_tys;
|
||||
let expected_arg_count = fn_inputs.len();
|
||||
let formal_tys = if tuple_arguments == TupleArguments {
|
||||
@ -2478,7 +2546,8 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
||||
Expectation::rvalue_hint(fcx.tcx(), ty)
|
||||
});
|
||||
|
||||
check_expr_with_unifier(fcx, &**arg,
|
||||
check_expr_with_unifier(fcx,
|
||||
&**arg,
|
||||
expected.unwrap_or(ExpectHasType(formal_ty)),
|
||||
NoPreference, || {
|
||||
// 2. Coerce to the most detailed type that could be coerced
|
||||
@ -3363,7 +3432,9 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
|
||||
|
||||
// We always require that the type provided as the value for
|
||||
// a type parameter outlives the moment of instantiation.
|
||||
constrain_path_type_parameters(fcx, expr);
|
||||
fcx.opt_node_ty_substs(expr.id, |item_substs| {
|
||||
fcx.add_wf_bounds(&item_substs.substs, expr);
|
||||
});
|
||||
}
|
||||
ast::ExprInlineAsm(ref ia) => {
|
||||
for &(_, ref input) in &ia.inputs {
|
||||
@ -3476,16 +3547,18 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
|
||||
}
|
||||
ast::ExprCall(ref callee, ref args) => {
|
||||
callee::check_call(fcx, expr, &**callee, &args[..], expected);
|
||||
|
||||
// we must check that return type of called functions is WF:
|
||||
let ret_ty = fcx.expr_ty(expr);
|
||||
fcx.register_wf_obligation(ret_ty, expr.span, traits::MiscObligation);
|
||||
}
|
||||
ast::ExprMethodCall(ident, ref tps, ref args) => {
|
||||
check_method_call(fcx, expr, ident, &args[..], &tps[..], expected, lvalue_pref);
|
||||
let arg_tys = args.iter().map(|a| fcx.expr_ty(&**a));
|
||||
let args_err = arg_tys.fold(false,
|
||||
|rest_err, a| {
|
||||
rest_err || a.references_error()});
|
||||
if args_err {
|
||||
fcx.write_error(id);
|
||||
}
|
||||
check_method_call(fcx, expr, ident, &args[..], &tps[..], expected, lvalue_pref);
|
||||
let arg_tys = args.iter().map(|a| fcx.expr_ty(&**a));
|
||||
let args_err = arg_tys.fold(false, |rest_err, a| rest_err || a.references_error());
|
||||
if args_err {
|
||||
fcx.write_error(id);
|
||||
}
|
||||
}
|
||||
ast::ExprCast(ref e, ref t) => {
|
||||
if let ast::TyFixedLengthVec(_, ref count_expr) = t.node {
|
||||
@ -3904,14 +3977,6 @@ pub fn resolve_ty_and_def_ufcs<'a, 'b, 'tcx>(fcx: &FnCtxt<'b, 'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
fn constrain_path_type_parameters(fcx: &FnCtxt,
|
||||
expr: &ast::Expr)
|
||||
{
|
||||
fcx.opt_node_ty_substs(expr.id, |item_substs| {
|
||||
fcx.add_default_region_param_bounds(&item_substs.substs, expr);
|
||||
});
|
||||
}
|
||||
|
||||
impl<'tcx> Expectation<'tcx> {
|
||||
/// Provide an expectation for an rvalue expression given an *optional*
|
||||
/// hint, which is not required for type safety (the resulting type might
|
||||
|
@ -86,15 +86,19 @@ use astconv::AstConv;
|
||||
use check::dropck;
|
||||
use check::FnCtxt;
|
||||
use middle::free_region::FreeRegionMap;
|
||||
use middle::implicator;
|
||||
use middle::implicator::{self, Implication};
|
||||
use middle::mem_categorization as mc;
|
||||
use middle::outlives;
|
||||
use middle::region::CodeExtent;
|
||||
use middle::subst::Substs;
|
||||
use middle::traits;
|
||||
use middle::ty::{self, ReScope, Ty, MethodCall, HasTypeFlags};
|
||||
use middle::infer::{self, GenericKind};
|
||||
use middle::ty::{self, RegionEscape, ReScope, Ty, MethodCall, HasTypeFlags};
|
||||
use middle::infer::{self, GenericKind, InferCtxt, SubregionOrigin, VerifyBound};
|
||||
use middle::pat_util;
|
||||
use middle::wf::{self, ImpliedBound};
|
||||
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use syntax::{ast, ast_util};
|
||||
use syntax::codemap::Span;
|
||||
use syntax::visit;
|
||||
@ -120,12 +124,19 @@ pub fn regionck_expr(fcx: &FnCtxt, e: &ast::Expr) {
|
||||
rcx.resolve_regions_and_report_errors();
|
||||
}
|
||||
|
||||
pub fn regionck_item(fcx: &FnCtxt, item: &ast::Item) {
|
||||
let mut rcx = Rcx::new(fcx, RepeatingScope(item.id), item.id, Subject(item.id));
|
||||
/// Region checking during the WF phase for items. `wf_tys` are the
|
||||
/// types from which we should derive implied bounds, if any.
|
||||
pub fn regionck_item<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
|
||||
item_id: ast::NodeId,
|
||||
span: Span,
|
||||
wf_tys: &[Ty<'tcx>]) {
|
||||
debug!("regionck_item(item.id={:?}, wf_tys={:?}", item_id, wf_tys);
|
||||
let mut rcx = Rcx::new(fcx, RepeatingScope(item_id), item_id, Subject(item_id));
|
||||
let tcx = fcx.tcx();
|
||||
rcx.free_region_map
|
||||
.relate_free_regions_from_predicates(tcx, &fcx.infcx().parameter_environment.caller_bounds);
|
||||
rcx.visit_region_obligations(item.id);
|
||||
rcx.relate_free_regions(wf_tys, item_id, span);
|
||||
rcx.visit_region_obligations(item_id);
|
||||
rcx.resolve_regions_and_report_errors();
|
||||
}
|
||||
|
||||
@ -154,22 +165,6 @@ pub fn regionck_fn(fcx: &FnCtxt,
|
||||
fcx.tcx().store_free_region_map(fn_id, rcx.free_region_map);
|
||||
}
|
||||
|
||||
/// Checks that the types in `component_tys` are well-formed. This will add constraints into the
|
||||
/// region graph. Does *not* run `resolve_regions_and_report_errors` and so forth.
|
||||
pub fn regionck_ensure_component_tys_wf<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
||||
span: Span,
|
||||
component_tys: &[Ty<'tcx>]) {
|
||||
let mut rcx = Rcx::new(fcx, RepeatingScope(0), 0, SubjectNode::None);
|
||||
for &component_ty in component_tys {
|
||||
// Check that each type outlives the empty region. Since the
|
||||
// empty region is a subregion of all others, this can't fail
|
||||
// unless the type does not meet the well-formedness
|
||||
// requirements.
|
||||
type_must_outlive(&mut rcx, infer::RelateParamBound(span, component_ty),
|
||||
component_ty, ty::ReEmpty);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// INTERNALS
|
||||
|
||||
@ -213,6 +208,10 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
|
||||
self.fcx.ccx.tcx
|
||||
}
|
||||
|
||||
pub fn infcx(&self) -> &InferCtxt<'a,'tcx> {
|
||||
self.fcx.infcx()
|
||||
}
|
||||
|
||||
fn set_body_id(&mut self, body_id: ast::NodeId) -> ast::NodeId {
|
||||
mem::replace(&mut self.body_id, body_id)
|
||||
}
|
||||
@ -325,11 +324,16 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
|
||||
.to_vec();
|
||||
|
||||
for r_o in ®ion_obligations {
|
||||
debug!("visit_region_obligations: r_o={:?}",
|
||||
r_o);
|
||||
debug!("visit_region_obligations: r_o={:?} cause={:?}",
|
||||
r_o, r_o.cause);
|
||||
let sup_type = self.resolve_type(r_o.sup_type);
|
||||
let origin = infer::RelateParamBound(r_o.cause.span, sup_type);
|
||||
type_must_outlive(self, origin, sup_type, r_o.sub_region);
|
||||
let origin = self.code_to_origin(r_o.cause.span, sup_type, &r_o.cause.code);
|
||||
|
||||
if r_o.sub_region != ty::ReEmpty {
|
||||
type_must_outlive(self, origin, sup_type, r_o.sub_region);
|
||||
} else {
|
||||
self.visit_old_school_wf(node_id, sup_type, origin);
|
||||
}
|
||||
}
|
||||
|
||||
// Processing the region obligations should not cause the list to grow further:
|
||||
@ -337,6 +341,62 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
|
||||
self.fcx.inh.infcx.fulfillment_cx.borrow().region_obligations(node_id).len());
|
||||
}
|
||||
|
||||
fn visit_old_school_wf(&mut self,
|
||||
body_id: ast::NodeId,
|
||||
ty: Ty<'tcx>,
|
||||
origin: infer::SubregionOrigin<'tcx>) {
|
||||
// As a weird kind of hack, we use a region of empty as a signal
|
||||
// to mean "old-school WF rules". The only reason the old-school
|
||||
// WF rules are not encoded using WF is that this leads to errors,
|
||||
// and we want to phase those in gradually.
|
||||
|
||||
// FIXME(#27579) remove this weird special case once we phase in new WF rules completely
|
||||
let implications = implicator::implications(self.infcx(),
|
||||
body_id,
|
||||
ty,
|
||||
ty::ReEmpty,
|
||||
origin.span());
|
||||
let origin_for_ty = |ty: Option<Ty<'tcx>>| match ty {
|
||||
None => origin.clone(),
|
||||
Some(ty) => infer::ReferenceOutlivesReferent(ty, origin.span()),
|
||||
};
|
||||
for implication in implications {
|
||||
match implication {
|
||||
Implication::RegionSubRegion(ty, r1, r2) => {
|
||||
self.fcx.mk_subr(origin_for_ty(ty), r1, r2);
|
||||
}
|
||||
Implication::RegionSubGeneric(ty, r1, GenericKind::Param(param_ty)) => {
|
||||
param_ty_must_outlive(self, origin_for_ty(ty), r1, param_ty);
|
||||
}
|
||||
Implication::RegionSubGeneric(ty, r1, GenericKind::Projection(proj_ty)) => {
|
||||
projection_must_outlive(self, origin_for_ty(ty), r1, proj_ty);
|
||||
}
|
||||
Implication::Predicate(def_id, predicate) => {
|
||||
let cause = traits::ObligationCause::new(origin.span(),
|
||||
body_id,
|
||||
traits::ItemObligation(def_id));
|
||||
let obligation = traits::Obligation::new(cause, predicate);
|
||||
self.fcx.register_predicate(obligation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn code_to_origin(&self,
|
||||
span: Span,
|
||||
sup_type: Ty<'tcx>,
|
||||
code: &traits::ObligationCauseCode<'tcx>)
|
||||
-> SubregionOrigin<'tcx> {
|
||||
match *code {
|
||||
traits::ObligationCauseCode::RFC1214(ref code) =>
|
||||
infer::RFC1214Subregion(Rc::new(self.code_to_origin(span, sup_type, code))),
|
||||
traits::ObligationCauseCode::ReferenceOutlivesReferent(ref_type) =>
|
||||
infer::ReferenceOutlivesReferent(ref_type, span),
|
||||
_ =>
|
||||
infer::RelateParamBound(span, sup_type),
|
||||
}
|
||||
}
|
||||
|
||||
/// This method populates the region map's `free_region_map`. It walks over the transformed
|
||||
/// argument and return types for each function just before we check the body of that function,
|
||||
/// looking for types where you have a borrowed pointer to other borrowed data (e.g., `&'a &'b
|
||||
@ -356,33 +416,28 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
|
||||
for &ty in fn_sig_tys {
|
||||
let ty = self.resolve_type(ty);
|
||||
debug!("relate_free_regions(t={:?})", ty);
|
||||
let body_scope = CodeExtent::from_node_id(body_id);
|
||||
let body_scope = ty::ReScope(body_scope);
|
||||
let implications = implicator::implications(self.fcx.infcx(), body_id,
|
||||
ty, body_scope, span);
|
||||
let implied_bounds = wf::implied_bounds(self.fcx.infcx(), body_id, ty, span);
|
||||
|
||||
// Record any relations between free regions that we observe into the free-region-map.
|
||||
self.free_region_map.relate_free_regions_from_implications(&implications);
|
||||
self.free_region_map.relate_free_regions_from_implied_bounds(&implied_bounds);
|
||||
|
||||
// But also record other relationships, such as `T:'x`,
|
||||
// that don't go into the free-region-map but which we use
|
||||
// here.
|
||||
for implication in implications {
|
||||
for implication in implied_bounds {
|
||||
debug!("implication: {:?}", implication);
|
||||
match implication {
|
||||
implicator::Implication::RegionSubRegion(_,
|
||||
ty::ReFree(free_a),
|
||||
ty::ReInfer(ty::ReVar(vid_b))) => {
|
||||
ImpliedBound::RegionSubRegion(ty::ReFree(free_a),
|
||||
ty::ReInfer(ty::ReVar(vid_b))) => {
|
||||
self.fcx.inh.infcx.add_given(free_a, vid_b);
|
||||
}
|
||||
implicator::Implication::RegionSubGeneric(_, r_a, ref generic_b) => {
|
||||
debug!("RegionSubGeneric: {:?} <= {:?}",
|
||||
r_a, generic_b);
|
||||
|
||||
self.region_bound_pairs.push((r_a, generic_b.clone()));
|
||||
ImpliedBound::RegionSubParam(r_a, param_b) => {
|
||||
self.region_bound_pairs.push((r_a, GenericKind::Param(param_b)));
|
||||
}
|
||||
implicator::Implication::RegionSubRegion(..) |
|
||||
implicator::Implication::Predicate(..) => {
|
||||
ImpliedBound::RegionSubProjection(r_a, projection_b) => {
|
||||
self.region_bound_pairs.push((r_a, GenericKind::Projection(projection_b)));
|
||||
}
|
||||
ImpliedBound::RegionSubRegion(..) => {
|
||||
// In principle, we could record (and take
|
||||
// advantage of) every relationship here, but
|
||||
// we are also free not to -- it simply means
|
||||
@ -492,12 +547,11 @@ fn constrain_bindings_in_pat(pat: &ast::Pat, rcx: &mut Rcx) {
|
||||
// that the lifetime of any regions that appear in a
|
||||
// variable's type enclose at least the variable's scope.
|
||||
|
||||
let var_region = tcx.region_maps.var_region(id);
|
||||
type_of_node_must_outlive(
|
||||
rcx, infer::BindingTypeIsNotValidAtDecl(span),
|
||||
id, var_region);
|
||||
|
||||
let var_scope = tcx.region_maps.var_scope(id);
|
||||
|
||||
let origin = infer::BindingTypeIsNotValidAtDecl(span);
|
||||
type_of_node_must_outlive(rcx, origin, id, ty::ReScope(var_scope));
|
||||
|
||||
let typ = rcx.resolve_node_type(id);
|
||||
dropck::check_safety_of_destructor_if_necessary(rcx, typ, span, var_scope);
|
||||
})
|
||||
@ -514,7 +568,29 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
|
||||
type_must_outlive(rcx, infer::ExprTypeIsNotInScope(expr_ty, expr.span),
|
||||
expr_ty, ty::ReScope(CodeExtent::from_node_id(expr.id)));
|
||||
|
||||
let has_method_map = rcx.fcx.infcx().is_method_call(expr.id);
|
||||
let method_call = MethodCall::expr(expr.id);
|
||||
let opt_method_callee = rcx.fcx.inh.tables.borrow().method_map.get(&method_call).cloned();
|
||||
let has_method_map = opt_method_callee.is_some();
|
||||
|
||||
// the region corresponding to this expression
|
||||
let expr_region = ty::ReScope(CodeExtent::from_node_id(expr.id));
|
||||
|
||||
// If we are calling a method (either explicitly or via an
|
||||
// overloaded operator), check that all of the types provided as
|
||||
// arguments for its type parameters are well-formed, and all the regions
|
||||
// provided as arguments outlive the call.
|
||||
if let Some(callee) = opt_method_callee {
|
||||
let origin = match expr.node {
|
||||
ast::ExprMethodCall(..) =>
|
||||
infer::ParameterOrigin::MethodCall,
|
||||
ast::ExprUnary(op, _) if op == ast::UnDeref =>
|
||||
infer::ParameterOrigin::OverloadedDeref,
|
||||
_ =>
|
||||
infer::ParameterOrigin::OverloadedOperator
|
||||
};
|
||||
|
||||
substs_wf_in_scope(rcx, origin, &callee.substs, expr.span, expr_region);
|
||||
}
|
||||
|
||||
// Check any autoderefs or autorefs that appear.
|
||||
let adjustment = rcx.fcx.inh.tables.borrow().adjustments.get(&expr.id).map(|a| a.clone());
|
||||
@ -585,6 +661,13 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
|
||||
}
|
||||
|
||||
match expr.node {
|
||||
ast::ExprPath(..) => {
|
||||
rcx.fcx.opt_node_ty_substs(expr.id, |item_substs| {
|
||||
let origin = infer::ParameterOrigin::Path;
|
||||
substs_wf_in_scope(rcx, origin, &item_substs.substs, expr.span, expr_region);
|
||||
});
|
||||
}
|
||||
|
||||
ast::ExprCall(ref callee, ref args) => {
|
||||
if has_method_map {
|
||||
constrain_call(rcx, expr, Some(&**callee),
|
||||
@ -895,6 +978,9 @@ fn constrain_autoderefs<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
|
||||
debug!("constrain_autoderefs: #{} is overloaded, method={:?}",
|
||||
i, method);
|
||||
|
||||
let origin = infer::ParameterOrigin::OverloadedDeref;
|
||||
substs_wf_in_scope(rcx, origin, method.substs, deref_expr.span, r_deref_expr);
|
||||
|
||||
// Treat overloaded autoderefs as if an AutoRef adjustment
|
||||
// was applied on the base type, as that is always the case.
|
||||
let fn_sig = method.ty.fn_sig();
|
||||
@ -1196,6 +1282,9 @@ fn link_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
|
||||
let mut borrow_cmt = borrow_cmt;
|
||||
let mut borrow_kind = borrow_kind;
|
||||
|
||||
let origin = infer::DataBorrowed(borrow_cmt.ty, span);
|
||||
type_must_outlive(rcx, origin, borrow_cmt.ty, *borrow_region);
|
||||
|
||||
loop {
|
||||
debug!("link_region(borrow_region={:?}, borrow_kind={:?}, borrow_cmt={:?})",
|
||||
borrow_region,
|
||||
@ -1395,74 +1484,295 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures that all borrowed data reachable via `ty` outlives `region`.
|
||||
pub fn type_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
|
||||
origin: infer::SubregionOrigin<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
region: ty::Region)
|
||||
/// Checks that the values provided for type/region arguments in a given
|
||||
/// expression are well-formed and in-scope.
|
||||
pub fn substs_wf_in_scope<'a,'tcx>(rcx: &mut Rcx<'a,'tcx>,
|
||||
origin: infer::ParameterOrigin,
|
||||
substs: &Substs<'tcx>,
|
||||
expr_span: Span,
|
||||
expr_region: ty::Region) {
|
||||
debug!("substs_wf_in_scope(substs={:?}, \
|
||||
expr_region={:?}, \
|
||||
origin={:?}, \
|
||||
expr_span={:?})",
|
||||
substs, expr_region, origin, expr_span);
|
||||
|
||||
let origin = infer::ParameterInScope(origin, expr_span);
|
||||
|
||||
for ®ion in substs.regions() {
|
||||
rcx.fcx.mk_subr(origin.clone(), expr_region, region);
|
||||
}
|
||||
|
||||
for &ty in &substs.types {
|
||||
let ty = rcx.resolve_type(ty);
|
||||
type_must_outlive(rcx, origin.clone(), ty, expr_region);
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures that type is well-formed in `region`, which implies (among
|
||||
/// other things) that all borrowed data reachable via `ty` outlives
|
||||
/// `region`.
|
||||
pub fn type_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
|
||||
origin: infer::SubregionOrigin<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
region: ty::Region)
|
||||
{
|
||||
let ty = rcx.resolve_type(ty);
|
||||
|
||||
debug!("type_must_outlive(ty={:?}, region={:?})",
|
||||
ty,
|
||||
region);
|
||||
|
||||
let implications = implicator::implications(rcx.fcx.infcx(), rcx.body_id,
|
||||
ty, region, origin.span());
|
||||
for implication in implications {
|
||||
debug!("implication: {:?}", implication);
|
||||
match implication {
|
||||
implicator::Implication::RegionSubRegion(None, r_a, r_b) => {
|
||||
rcx.fcx.mk_subr(origin.clone(), r_a, r_b);
|
||||
assert!(!ty.has_escaping_regions());
|
||||
|
||||
let components = outlives::components(rcx.infcx(), ty);
|
||||
components_must_outlive(rcx, origin, components, region);
|
||||
}
|
||||
|
||||
fn components_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
|
||||
origin: infer::SubregionOrigin<'tcx>,
|
||||
components: Vec<outlives::Component<'tcx>>,
|
||||
region: ty::Region)
|
||||
{
|
||||
for component in components {
|
||||
let origin = origin.clone();
|
||||
match component {
|
||||
outlives::Component::Region(region1) => {
|
||||
rcx.fcx.mk_subr(origin, region, region1);
|
||||
}
|
||||
implicator::Implication::RegionSubRegion(Some(ty), r_a, r_b) => {
|
||||
let o1 = infer::ReferenceOutlivesReferent(ty, origin.span());
|
||||
rcx.fcx.mk_subr(o1, r_a, r_b);
|
||||
outlives::Component::Param(param_ty) => {
|
||||
param_ty_must_outlive(rcx, origin, region, param_ty);
|
||||
}
|
||||
implicator::Implication::RegionSubGeneric(None, r_a, ref generic_b) => {
|
||||
generic_must_outlive(rcx, origin.clone(), r_a, generic_b);
|
||||
outlives::Component::Projection(projection_ty) => {
|
||||
projection_must_outlive(rcx, origin, region, projection_ty);
|
||||
}
|
||||
implicator::Implication::RegionSubGeneric(Some(ty), r_a, ref generic_b) => {
|
||||
let o1 = infer::ReferenceOutlivesReferent(ty, origin.span());
|
||||
generic_must_outlive(rcx, o1, r_a, generic_b);
|
||||
outlives::Component::EscapingProjection(subcomponents) => {
|
||||
components_must_outlive(rcx, origin, subcomponents, region);
|
||||
}
|
||||
implicator::Implication::Predicate(def_id, predicate) => {
|
||||
let cause = traits::ObligationCause::new(origin.span(),
|
||||
rcx.body_id,
|
||||
traits::ItemObligation(def_id));
|
||||
let obligation = traits::Obligation::new(cause, predicate);
|
||||
rcx.fcx.register_predicate(obligation);
|
||||
outlives::Component::UnresolvedInferenceVariable(v) => {
|
||||
// ignore this, we presume it will yield an error
|
||||
// later, since if a type variable is not resolved by
|
||||
// this point it never will be
|
||||
rcx.tcx().sess.delay_span_bug(
|
||||
origin.span(),
|
||||
&format!("unresolved inference variable in outlives: {:?}", v));
|
||||
}
|
||||
outlives::Component::RFC1214(subcomponents) => {
|
||||
let suborigin = infer::RFC1214Subregion(Rc::new(origin));
|
||||
components_must_outlive(rcx, suborigin, subcomponents, region);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generic_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
|
||||
origin: infer::SubregionOrigin<'tcx>,
|
||||
region: ty::Region,
|
||||
generic: &GenericKind<'tcx>) {
|
||||
let param_env = &rcx.fcx.inh.infcx.parameter_environment;
|
||||
fn param_ty_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
|
||||
origin: infer::SubregionOrigin<'tcx>,
|
||||
region: ty::Region,
|
||||
param_ty: ty::ParamTy) {
|
||||
debug!("param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})",
|
||||
region, param_ty, origin);
|
||||
|
||||
debug!("param_must_outlive(region={:?}, generic={:?})",
|
||||
region,
|
||||
generic);
|
||||
let verify_bound = param_bound(rcx, param_ty);
|
||||
let generic = GenericKind::Param(param_ty);
|
||||
rcx.fcx.infcx().verify_generic_bound(origin, generic, region, verify_bound);
|
||||
}
|
||||
|
||||
// To start, collect bounds from user:
|
||||
let mut param_bounds = rcx.tcx().required_region_bounds(generic.to_ty(rcx.tcx()),
|
||||
param_env.caller_bounds.clone());
|
||||
fn projection_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
|
||||
origin: infer::SubregionOrigin<'tcx>,
|
||||
region: ty::Region,
|
||||
projection_ty: ty::ProjectionTy<'tcx>)
|
||||
{
|
||||
debug!("projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})",
|
||||
region, projection_ty, origin);
|
||||
|
||||
// In the case of a projection T::Foo, we may be able to extract bounds from the trait def:
|
||||
match *generic {
|
||||
GenericKind::Param(..) => { }
|
||||
GenericKind::Projection(ref projection_ty) => {
|
||||
param_bounds.push_all(
|
||||
&projection_bounds(rcx, origin.span(), projection_ty));
|
||||
// This case is thorny for inference. The fundamental problem is
|
||||
// that there are many cases where we have choice, and inference
|
||||
// doesn't like choice (the current region inference in
|
||||
// particular). :) First off, we have to choose between using the
|
||||
// OutlivesProjectionEnv, OutlivesProjectionTraitDef, and
|
||||
// OutlivesProjectionComponent rules, any one of which is
|
||||
// sufficient. If there are no inference variables involved, it's
|
||||
// not hard to pick the right rule, but if there are, we're in a
|
||||
// bit of a catch 22: if we picked which rule we were going to
|
||||
// use, we could add constraints to the region inference graph
|
||||
// that make it apply, but if we don't add those constraints, the
|
||||
// rule might not apply (but another rule might). For now, we err
|
||||
// on the side of adding too few edges into the graph.
|
||||
|
||||
// Compute the bounds we can derive from the environment or trait
|
||||
// definition. We know that the projection outlives all the
|
||||
// regions in this list.
|
||||
let env_bounds = projection_declared_bounds(rcx, origin.span(), projection_ty);
|
||||
|
||||
debug!("projection_must_outlive: env_bounds={:?}",
|
||||
env_bounds);
|
||||
|
||||
// If we know that the projection outlives 'static, then we're
|
||||
// done here.
|
||||
if env_bounds.contains(&ty::ReStatic) {
|
||||
debug!("projection_must_outlive: 'static as declared bound");
|
||||
return;
|
||||
}
|
||||
|
||||
// If declared bounds list is empty, the only applicable rule is
|
||||
// OutlivesProjectionComponent. If there are inference variables,
|
||||
// then, we can break down the outlives into more primitive
|
||||
// components without adding unnecessary edges.
|
||||
//
|
||||
// If there are *no* inference variables, however, we COULD do
|
||||
// this, but we choose not to, because the error messages are less
|
||||
// good. For example, a requirement like `T::Item: 'r` would be
|
||||
// translated to a requirement that `T: 'r`; when this is reported
|
||||
// to the user, it will thus say "T: 'r must hold so that T::Item:
|
||||
// 'r holds". But that makes it sound like the only way to fix
|
||||
// the problem is to add `T: 'r`, which isn't true. So, if there are no
|
||||
// inference variables, we use a verify constraint instead of adding
|
||||
// edges, which winds up enforcing the same condition.
|
||||
let needs_infer = {
|
||||
projection_ty.trait_ref.substs.types.iter().any(|t| t.needs_infer()) ||
|
||||
projection_ty.trait_ref.substs.regions().iter().any(|r| r.needs_infer())
|
||||
};
|
||||
if env_bounds.is_empty() && needs_infer {
|
||||
debug!("projection_must_outlive: no declared bounds");
|
||||
|
||||
for &component_ty in &projection_ty.trait_ref.substs.types {
|
||||
type_must_outlive(rcx, origin.clone(), component_ty, region);
|
||||
}
|
||||
|
||||
for &r in projection_ty.trait_ref.substs.regions() {
|
||||
rcx.fcx.mk_subr(origin.clone(), region, r);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If we find that there is a unique declared bound `'b`, and this bound
|
||||
// appears in the trait reference, then the best action is to require that `'b:'r`,
|
||||
// so do that. This is best no matter what rule we use:
|
||||
//
|
||||
// - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to
|
||||
// the requirement that `'b:'r`
|
||||
// - OutlivesProjectionComponent: this would require `'b:'r` in addition to other conditions
|
||||
if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) {
|
||||
let unique_bound = env_bounds[0];
|
||||
debug!("projection_must_outlive: unique declared bound = {:?}", unique_bound);
|
||||
if projection_ty.trait_ref.substs.regions()
|
||||
.iter()
|
||||
.any(|r| env_bounds.contains(r))
|
||||
{
|
||||
debug!("projection_must_outlive: unique declared bound appears in trait ref");
|
||||
rcx.fcx.mk_subr(origin.clone(), region, unique_bound);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to verifying after the fact that there exists a
|
||||
// declared bound, or that all the components appearing in the
|
||||
// projection outlive; in some cases, this may add insufficient
|
||||
// edges into the inference graph, leading to inference failures
|
||||
// even though a satisfactory solution exists.
|
||||
let verify_bound = projection_bound(rcx, origin.span(), env_bounds, projection_ty);
|
||||
let generic = GenericKind::Projection(projection_ty);
|
||||
rcx.fcx.infcx().verify_generic_bound(origin, generic.clone(), region, verify_bound);
|
||||
}
|
||||
|
||||
fn type_bound<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, span: Span, ty: Ty<'tcx>) -> VerifyBound {
|
||||
match ty.sty {
|
||||
ty::TyParam(p) => {
|
||||
param_bound(rcx, p)
|
||||
}
|
||||
ty::TyProjection(data) => {
|
||||
let declared_bounds = projection_declared_bounds(rcx, span, data);
|
||||
projection_bound(rcx, span, declared_bounds, data)
|
||||
}
|
||||
_ => {
|
||||
recursive_type_bound(rcx, span, ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn param_bound<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, param_ty: ty::ParamTy) -> VerifyBound {
|
||||
let param_env = &rcx.infcx().parameter_environment;
|
||||
|
||||
debug!("param_bound(param_ty={:?})",
|
||||
param_ty);
|
||||
|
||||
let mut param_bounds = declared_generic_bounds_from_env(rcx, GenericKind::Param(param_ty));
|
||||
|
||||
// Add in the default bound of fn body that applies to all in
|
||||
// scope type parameters:
|
||||
param_bounds.push(param_env.implicit_region_bound);
|
||||
|
||||
// Finally, collect regions we scraped from the well-formedness
|
||||
VerifyBound::AnyRegion(param_bounds)
|
||||
}
|
||||
|
||||
fn projection_declared_bounds<'a, 'tcx>(rcx: &Rcx<'a,'tcx>,
|
||||
span: Span,
|
||||
projection_ty: ty::ProjectionTy<'tcx>)
|
||||
-> Vec<ty::Region>
|
||||
{
|
||||
// First assemble bounds from where clauses and traits.
|
||||
|
||||
let mut declared_bounds =
|
||||
declared_generic_bounds_from_env(rcx, GenericKind::Projection(projection_ty));
|
||||
|
||||
declared_bounds.push_all(
|
||||
&declared_projection_bounds_from_trait(rcx, span, projection_ty));
|
||||
|
||||
declared_bounds
|
||||
}
|
||||
|
||||
fn projection_bound<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
|
||||
span: Span,
|
||||
declared_bounds: Vec<ty::Region>,
|
||||
projection_ty: ty::ProjectionTy<'tcx>)
|
||||
-> VerifyBound {
|
||||
debug!("projection_bound(declared_bounds={:?}, projection_ty={:?})",
|
||||
declared_bounds, projection_ty);
|
||||
|
||||
// see the extensive comment in projection_must_outlive
|
||||
|
||||
let ty = rcx.tcx().mk_projection(projection_ty.trait_ref, projection_ty.item_name);
|
||||
let recursive_bound = recursive_type_bound(rcx, span, ty);
|
||||
|
||||
VerifyBound::AnyRegion(declared_bounds).or(recursive_bound)
|
||||
}
|
||||
|
||||
fn recursive_type_bound<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
|
||||
span: Span,
|
||||
ty: Ty<'tcx>)
|
||||
-> VerifyBound {
|
||||
let mut bounds = vec![];
|
||||
|
||||
for subty in ty.walk_shallow() {
|
||||
bounds.push(type_bound(rcx, span, subty));
|
||||
}
|
||||
|
||||
let mut regions = ty.regions();
|
||||
regions.retain(|r| !r.is_bound()); // ignore late-bound regions
|
||||
bounds.push(VerifyBound::AllRegions(regions));
|
||||
|
||||
// remove bounds that must hold, since they are not interesting
|
||||
bounds.retain(|b| !b.must_hold());
|
||||
|
||||
if bounds.len() == 1 {
|
||||
bounds.pop().unwrap()
|
||||
} else {
|
||||
VerifyBound::AllBounds(bounds)
|
||||
}
|
||||
}
|
||||
|
||||
fn declared_generic_bounds_from_env<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
|
||||
generic: GenericKind<'tcx>)
|
||||
-> Vec<ty::Region>
|
||||
{
|
||||
let param_env = &rcx.infcx().parameter_environment;
|
||||
|
||||
// To start, collect bounds from user:
|
||||
let mut param_bounds = rcx.tcx().required_region_bounds(generic.to_ty(rcx.tcx()),
|
||||
param_env.caller_bounds.clone());
|
||||
|
||||
// Next, collect regions we scraped from the well-formedness
|
||||
// constraints in the fn signature. To do that, we walk the list
|
||||
// of known relations from the fn ctxt.
|
||||
//
|
||||
@ -1473,27 +1783,22 @@ fn generic_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
|
||||
// The problem is that the type of `x` is `&'a A`. To be
|
||||
// well-formed, then, A must be lower-generic by `'a`, but we
|
||||
// don't know that this holds from first principles.
|
||||
for &(ref r, ref p) in &rcx.region_bound_pairs {
|
||||
for &(r, p) in &rcx.region_bound_pairs {
|
||||
debug!("generic={:?} p={:?}",
|
||||
generic,
|
||||
p);
|
||||
if generic == p {
|
||||
param_bounds.push(*r);
|
||||
param_bounds.push(r);
|
||||
}
|
||||
}
|
||||
|
||||
// Inform region inference that this generic must be properly
|
||||
// bounded.
|
||||
rcx.fcx.infcx().verify_generic_bound(origin,
|
||||
generic.clone(),
|
||||
region,
|
||||
param_bounds);
|
||||
param_bounds
|
||||
}
|
||||
|
||||
fn projection_bounds<'a,'tcx>(rcx: &Rcx<'a, 'tcx>,
|
||||
span: Span,
|
||||
projection_ty: &ty::ProjectionTy<'tcx>)
|
||||
-> Vec<ty::Region>
|
||||
fn declared_projection_bounds_from_trait<'a,'tcx>(rcx: &Rcx<'a, 'tcx>,
|
||||
span: Span,
|
||||
projection_ty: ty::ProjectionTy<'tcx>)
|
||||
-> Vec<ty::Region>
|
||||
{
|
||||
let fcx = rcx.fcx;
|
||||
let tcx = fcx.tcx();
|
||||
|
@ -9,7 +9,7 @@
|
||||
// except according to those terms.
|
||||
|
||||
use astconv::AstConv;
|
||||
use check::{FnCtxt, Inherited, blank_fn_ctxt, regionck};
|
||||
use check::{FnCtxt, Inherited, blank_fn_ctxt, regionck, wfcheck};
|
||||
use constrained_type_params::{identify_constrained_type_params, Parameter};
|
||||
use CrateCtxt;
|
||||
use middle::region;
|
||||
@ -23,7 +23,7 @@ use std::collections::HashSet;
|
||||
use syntax::ast;
|
||||
use syntax::ast_util::local_def;
|
||||
use syntax::codemap::{DUMMY_SP, Span};
|
||||
use syntax::parse::token::special_idents;
|
||||
use syntax::parse::token::{special_idents};
|
||||
use syntax::visit;
|
||||
use syntax::visit::Visitor;
|
||||
|
||||
@ -86,9 +86,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
|
||||
Some(ty::BoundSend) | Some(ty::BoundSync) => {}
|
||||
Some(_) | None => {
|
||||
if !ccx.tcx.trait_has_default_impl(trait_ref.def_id) {
|
||||
span_err!(ccx.tcx.sess, item.span, E0192,
|
||||
"negative impls are only allowed for traits with \
|
||||
default impls (e.g., `Send` and `Sync`)")
|
||||
wfcheck::error_192(ccx, item.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -122,9 +120,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
|
||||
reject_non_type_param_bounds(ccx.tcx, item.span, &trait_predicates);
|
||||
if ccx.tcx.trait_has_default_impl(local_def(item.id)) {
|
||||
if !items.is_empty() {
|
||||
span_err!(ccx.tcx.sess, item.span, E0380,
|
||||
"traits with default impls (`e.g. unsafe impl \
|
||||
Trait for ..`) must have no methods or associated items")
|
||||
wfcheck::error_380(ccx, item.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,7 +145,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
|
||||
let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(type_scheme.ty), item.id);
|
||||
f(self, &fcx);
|
||||
fcx.select_all_obligations_or_error();
|
||||
regionck::regionck_item(&fcx, item);
|
||||
regionck::regionck_item(&fcx, item.id, item.span, &[]);
|
||||
}
|
||||
|
||||
/// In a type definition, we check that to ensure that the types of the fields are well-formed.
|
||||
@ -182,11 +178,9 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
let field_tys: Vec<Ty> =
|
||||
variants.iter().flat_map(|v| v.fields.iter().map(|f| f.ty)).collect();
|
||||
|
||||
regionck::regionck_ensure_component_tys_wf(
|
||||
fcx, item.span, &field_tys);
|
||||
for field in variants.iter().flat_map(|v| v.fields.iter()) {
|
||||
fcx.register_old_wf_obligation(field.ty, field.span, traits::MiscObligation);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -355,8 +349,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
|
||||
span: Span,
|
||||
param_name: ast::Name)
|
||||
{
|
||||
span_err!(self.tcx().sess, span, E0392,
|
||||
"parameter `{}` is never used", param_name);
|
||||
wfcheck::error_392(self.tcx(), span, param_name);
|
||||
|
||||
let suggested_marker_id = self.tcx().lang_items.phantom_data();
|
||||
match suggested_marker_id {
|
||||
@ -420,9 +413,7 @@ fn reject_shadowing_type_parameters<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
|
||||
for method_param in generics.types.get_slice(subst::FnSpace) {
|
||||
if impl_params.contains(&method_param.name) {
|
||||
span_err!(tcx.sess, span, E0194,
|
||||
"type parameter `{}` shadows another type parameter of the same name",
|
||||
method_param.name);
|
||||
wfcheck::error_194(tcx, span, method_param.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -521,11 +512,6 @@ impl<'cx,'tcx> BoundsChecker<'cx,'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_ty(&mut self, ty: Ty<'tcx>, span: Span) {
|
||||
self.span = span;
|
||||
ty.fold_with(self);
|
||||
}
|
||||
|
||||
fn check_traits_in_ty(&mut self, ty: Ty<'tcx>, span: Span) {
|
||||
self.span = span;
|
||||
// When checking types outside of a type def'n, we ignore
|
||||
@ -709,6 +695,8 @@ fn filter_to_trait_obligations<'tcx>(bounds: ty::InstantiatedPredicates<'tcx>)
|
||||
ty::Predicate::Projection(..) => {
|
||||
result.predicates.push(space, predicate.clone())
|
||||
}
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::Equate(..) |
|
||||
ty::Predicate::TypeOutlives(..) |
|
||||
ty::Predicate::RegionOutlives(..) => {
|
||||
|
630
src/librustc_typeck/check/wfcheck.rs
Normal file
630
src/librustc_typeck/check/wfcheck.rs
Normal file
@ -0,0 +1,630 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
use astconv::AstConv;
|
||||
use check::{FnCtxt, Inherited, blank_fn_ctxt, regionck};
|
||||
use constrained_type_params::{identify_constrained_type_params, Parameter};
|
||||
use CrateCtxt;
|
||||
use middle::region::DestructionScopeData;
|
||||
use middle::subst::{self, TypeSpace, FnSpace, ParamSpace, SelfSpace};
|
||||
use middle::traits;
|
||||
use middle::ty::{self, Ty};
|
||||
use middle::ty_fold::{TypeFolder};
|
||||
use middle::wf;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::rc::Rc;
|
||||
use syntax::ast;
|
||||
use syntax::ast_util::local_def;
|
||||
use syntax::codemap::{Span};
|
||||
use syntax::parse::token::{special_idents};
|
||||
use syntax::ptr::P;
|
||||
use syntax::visit;
|
||||
use syntax::visit::Visitor;
|
||||
|
||||
pub struct CheckTypeWellFormedVisitor<'ccx, 'tcx:'ccx> {
|
||||
ccx: &'ccx CrateCtxt<'ccx, 'tcx>,
|
||||
code: traits::ObligationCauseCode<'tcx>,
|
||||
}
|
||||
|
||||
impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
|
||||
pub fn new(ccx: &'ccx CrateCtxt<'ccx, 'tcx>)
|
||||
-> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
|
||||
CheckTypeWellFormedVisitor {
|
||||
ccx: ccx,
|
||||
code: traits::ObligationCauseCode::RFC1214(
|
||||
Rc::new(traits::ObligationCauseCode::MiscObligation))
|
||||
}
|
||||
}
|
||||
|
||||
fn tcx(&self) -> &ty::ctxt<'tcx> {
|
||||
self.ccx.tcx
|
||||
}
|
||||
|
||||
/// Checks that the field types (in a struct def'n) or argument types (in an enum def'n) are
|
||||
/// well-formed, meaning that they do not require any constraints not declared in the struct
|
||||
/// definition itself. For example, this definition would be illegal:
|
||||
///
|
||||
/// struct Ref<'a, T> { x: &'a T }
|
||||
///
|
||||
/// because the type did not declare that `T:'a`.
|
||||
///
|
||||
/// We do this check as a pre-pass before checking fn bodies because if these constraints are
|
||||
/// not included it frequently leads to confusing errors in fn bodies. So it's better to check
|
||||
/// the types first.
|
||||
fn check_item_well_formed(&mut self, item: &ast::Item) {
|
||||
let ccx = self.ccx;
|
||||
debug!("check_item_well_formed(it.id={}, it.ident={})",
|
||||
item.id,
|
||||
ccx.tcx.item_path_str(local_def(item.id)));
|
||||
|
||||
match item.node {
|
||||
/// Right now we check that every default trait implementation
|
||||
/// has an implementation of itself. Basically, a case like:
|
||||
///
|
||||
/// `impl Trait for T {}`
|
||||
///
|
||||
/// has a requirement of `T: Trait` which was required for default
|
||||
/// method implementations. Although this could be improved now that
|
||||
/// there's a better infrastructure in place for this, it's being left
|
||||
/// for a follow-up work.
|
||||
///
|
||||
/// Since there's such a requirement, we need to check *just* positive
|
||||
/// implementations, otherwise things like:
|
||||
///
|
||||
/// impl !Send for T {}
|
||||
///
|
||||
/// won't be allowed unless there's an *explicit* implementation of `Send`
|
||||
/// for `T`
|
||||
ast::ItemImpl(_, ast::ImplPolarity::Positive, _,
|
||||
ref trait_ref, ref self_ty, _) => {
|
||||
self.check_impl(item, self_ty, trait_ref);
|
||||
}
|
||||
ast::ItemImpl(_, ast::ImplPolarity::Negative, _, Some(_), _, _) => {
|
||||
// FIXME(#27579) what amount of WF checking do we need for neg impls?
|
||||
|
||||
let trait_ref = ccx.tcx.impl_trait_ref(local_def(item.id)).unwrap();
|
||||
ccx.tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id);
|
||||
match ccx.tcx.lang_items.to_builtin_kind(trait_ref.def_id) {
|
||||
Some(ty::BoundSend) | Some(ty::BoundSync) => {}
|
||||
Some(_) | None => {
|
||||
if !ccx.tcx.trait_has_default_impl(trait_ref.def_id) {
|
||||
error_192(ccx, item.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::ItemFn(_, _, _, _, _, ref body) => {
|
||||
self.check_item_fn(item, body);
|
||||
}
|
||||
ast::ItemStatic(..) => {
|
||||
self.check_item_type(item);
|
||||
}
|
||||
ast::ItemConst(..) => {
|
||||
self.check_item_type(item);
|
||||
}
|
||||
ast::ItemStruct(ref struct_def, ref ast_generics) => {
|
||||
self.check_type_defn(item, |fcx| {
|
||||
vec![struct_variant(fcx, &**struct_def)]
|
||||
});
|
||||
|
||||
self.check_variances_for_type_defn(item, ast_generics);
|
||||
}
|
||||
ast::ItemEnum(ref enum_def, ref ast_generics) => {
|
||||
self.check_type_defn(item, |fcx| {
|
||||
enum_variants(fcx, enum_def)
|
||||
});
|
||||
|
||||
self.check_variances_for_type_defn(item, ast_generics);
|
||||
}
|
||||
ast::ItemTrait(_, _, _, ref items) => {
|
||||
self.check_trait(item, items);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_trait_or_impl_item(&mut self, item_id: ast::NodeId, span: Span) {
|
||||
let code = self.code.clone();
|
||||
self.with_fcx(item_id, span, |fcx, this| {
|
||||
let free_substs = &fcx.inh.infcx.parameter_environment.free_substs;
|
||||
let free_id = fcx.inh.infcx.parameter_environment.free_id;
|
||||
|
||||
let item = fcx.tcx().impl_or_trait_item(local_def(item_id));
|
||||
|
||||
let mut implied_bounds = match item.container() {
|
||||
ty::TraitContainer(_) => vec![],
|
||||
ty::ImplContainer(def_id) => impl_implied_bounds(fcx, def_id, span)
|
||||
};
|
||||
|
||||
match item {
|
||||
ty::ConstTraitItem(assoc_const) => {
|
||||
let ty = fcx.instantiate_type_scheme(span, free_substs, &assoc_const.ty);
|
||||
fcx.register_wf_obligation(ty, span, code.clone());
|
||||
}
|
||||
ty::MethodTraitItem(method) => {
|
||||
reject_shadowing_type_parameters(fcx.tcx(), span, &method.generics);
|
||||
let method_ty = fcx.instantiate_type_scheme(span, free_substs, &method.fty);
|
||||
let predicates = fcx.instantiate_bounds(span, free_substs, &method.predicates);
|
||||
this.check_fn_or_method(fcx, span, &method_ty, &predicates,
|
||||
free_id, &mut implied_bounds);
|
||||
}
|
||||
ty::TypeTraitItem(assoc_type) => {
|
||||
if let Some(ref ty) = assoc_type.ty {
|
||||
let ty = fcx.instantiate_type_scheme(span, free_substs, ty);
|
||||
fcx.register_wf_obligation(ty, span, code.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
implied_bounds
|
||||
})
|
||||
}
|
||||
|
||||
fn with_item_fcx<F>(&mut self, item: &ast::Item, f: F) where
|
||||
F: for<'fcx> FnMut(&FnCtxt<'fcx, 'tcx>,
|
||||
&mut CheckTypeWellFormedVisitor<'ccx,'tcx>) -> Vec<Ty<'tcx>>,
|
||||
{
|
||||
self.with_fcx(item.id, item.span, f)
|
||||
}
|
||||
|
||||
fn with_fcx<F>(&mut self, id: ast::NodeId, span: Span, mut f: F) where
|
||||
F: for<'fcx> FnMut(&FnCtxt<'fcx, 'tcx>,
|
||||
&mut CheckTypeWellFormedVisitor<'ccx,'tcx>) -> Vec<Ty<'tcx>>,
|
||||
{
|
||||
let ccx = self.ccx;
|
||||
let param_env = ty::ParameterEnvironment::for_item(ccx.tcx, id);
|
||||
let tables = RefCell::new(ty::Tables::empty());
|
||||
let inh = Inherited::new(ccx.tcx, &tables, param_env);
|
||||
let fcx = blank_fn_ctxt(ccx, &inh, ty::FnDiverging, id);
|
||||
let wf_tys = f(&fcx, self);
|
||||
fcx.select_all_obligations_or_error();
|
||||
regionck::regionck_item(&fcx, id, span, &wf_tys);
|
||||
}
|
||||
|
||||
/// In a type definition, we check that to ensure that the types of the fields are well-formed.
|
||||
fn check_type_defn<F>(&mut self, item: &ast::Item, mut lookup_fields: F) where
|
||||
F: for<'fcx> FnMut(&FnCtxt<'fcx, 'tcx>) -> Vec<AdtVariant<'tcx>>,
|
||||
{
|
||||
self.with_item_fcx(item, |fcx, this| {
|
||||
let variants = lookup_fields(fcx);
|
||||
|
||||
for variant in &variants {
|
||||
// For DST, all intermediate types must be sized.
|
||||
if let Some((_, fields)) = variant.fields.split_last() {
|
||||
for field in fields {
|
||||
fcx.register_builtin_bound(
|
||||
field.ty,
|
||||
ty::BoundSized,
|
||||
traits::ObligationCause::new(field.span,
|
||||
fcx.body_id,
|
||||
traits::FieldSized));
|
||||
}
|
||||
}
|
||||
|
||||
// All field types must be well-formed.
|
||||
for field in &variant.fields {
|
||||
fcx.register_wf_obligation(field.ty, field.span, this.code.clone())
|
||||
}
|
||||
}
|
||||
|
||||
let free_substs = &fcx.inh.infcx.parameter_environment.free_substs;
|
||||
let predicates = fcx.tcx().lookup_predicates(local_def(item.id));
|
||||
let predicates = fcx.instantiate_bounds(item.span, free_substs, &predicates);
|
||||
this.check_where_clauses(fcx, item.span, &predicates);
|
||||
|
||||
vec![] // no implied bounds in a struct def'n
|
||||
});
|
||||
}
|
||||
|
||||
fn check_trait(&mut self,
|
||||
item: &ast::Item,
|
||||
items: &[P<ast::TraitItem>])
|
||||
{
|
||||
let trait_def_id = local_def(item.id);
|
||||
|
||||
if self.ccx.tcx.trait_has_default_impl(trait_def_id) {
|
||||
if !items.is_empty() {
|
||||
error_380(self.ccx, item.span);
|
||||
}
|
||||
}
|
||||
|
||||
self.with_item_fcx(item, |fcx, this| {
|
||||
let free_substs = &fcx.inh.infcx.parameter_environment.free_substs;
|
||||
let predicates = fcx.tcx().lookup_predicates(trait_def_id);
|
||||
let predicates = fcx.instantiate_bounds(item.span, free_substs, &predicates);
|
||||
this.check_where_clauses(fcx, item.span, &predicates);
|
||||
vec![]
|
||||
});
|
||||
}
|
||||
|
||||
fn check_item_fn(&mut self,
|
||||
item: &ast::Item,
|
||||
body: &ast::Block)
|
||||
{
|
||||
self.with_item_fcx(item, |fcx, this| {
|
||||
let free_substs = &fcx.inh.infcx.parameter_environment.free_substs;
|
||||
let type_scheme = fcx.tcx().lookup_item_type(local_def(item.id));
|
||||
let item_ty = fcx.instantiate_type_scheme(item.span, free_substs, &type_scheme.ty);
|
||||
let bare_fn_ty = match item_ty.sty {
|
||||
ty::TyBareFn(_, ref bare_fn_ty) => bare_fn_ty,
|
||||
_ => {
|
||||
this.tcx().sess.span_bug(item.span, "Fn item without bare fn type");
|
||||
}
|
||||
};
|
||||
|
||||
let predicates = fcx.tcx().lookup_predicates(local_def(item.id));
|
||||
let predicates = fcx.instantiate_bounds(item.span, free_substs, &predicates);
|
||||
|
||||
let mut implied_bounds = vec![];
|
||||
this.check_fn_or_method(fcx, item.span, bare_fn_ty, &predicates,
|
||||
body.id, &mut implied_bounds);
|
||||
implied_bounds
|
||||
})
|
||||
}
|
||||
|
||||
fn check_item_type(&mut self,
|
||||
item: &ast::Item)
|
||||
{
|
||||
debug!("check_item_type: {:?}", item);
|
||||
|
||||
self.with_item_fcx(item, |fcx, this| {
|
||||
let type_scheme = fcx.tcx().lookup_item_type(local_def(item.id));
|
||||
let item_ty = fcx.instantiate_type_scheme(item.span,
|
||||
&fcx.inh
|
||||
.infcx
|
||||
.parameter_environment
|
||||
.free_substs,
|
||||
&type_scheme.ty);
|
||||
|
||||
fcx.register_wf_obligation(item_ty, item.span, this.code.clone());
|
||||
|
||||
vec![] // no implied bounds in a const etc
|
||||
});
|
||||
}
|
||||
|
||||
fn check_impl(&mut self,
|
||||
item: &ast::Item,
|
||||
ast_self_ty: &ast::Ty,
|
||||
ast_trait_ref: &Option<ast::TraitRef>)
|
||||
{
|
||||
debug!("check_impl: {:?}", item);
|
||||
|
||||
self.with_item_fcx(item, |fcx, this| {
|
||||
let free_substs = &fcx.inh.infcx.parameter_environment.free_substs;
|
||||
let item_def_id = local_def(item.id);
|
||||
|
||||
match *ast_trait_ref {
|
||||
Some(ref ast_trait_ref) => {
|
||||
let trait_ref = fcx.tcx().impl_trait_ref(item_def_id).unwrap();
|
||||
let trait_ref =
|
||||
fcx.instantiate_type_scheme(
|
||||
ast_trait_ref.path.span, free_substs, &trait_ref);
|
||||
let obligations =
|
||||
wf::trait_obligations(fcx.infcx(),
|
||||
fcx.body_id,
|
||||
&trait_ref,
|
||||
ast_trait_ref.path.span,
|
||||
true);
|
||||
for obligation in obligations {
|
||||
fcx.register_predicate(obligation);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let self_ty = fcx.tcx().node_id_to_type(item.id);
|
||||
let self_ty = fcx.instantiate_type_scheme(item.span, free_substs, &self_ty);
|
||||
fcx.register_wf_obligation(self_ty, ast_self_ty.span, this.code.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let predicates = fcx.tcx().lookup_predicates(item_def_id);
|
||||
let predicates = fcx.instantiate_bounds(item.span, free_substs, &predicates);
|
||||
this.check_where_clauses(fcx, item.span, &predicates);
|
||||
|
||||
impl_implied_bounds(fcx, local_def(item.id), item.span)
|
||||
});
|
||||
}
|
||||
|
||||
fn check_where_clauses<'fcx>(&mut self,
|
||||
fcx: &FnCtxt<'fcx,'tcx>,
|
||||
span: Span,
|
||||
predicates: &ty::InstantiatedPredicates<'tcx>)
|
||||
{
|
||||
let obligations =
|
||||
predicates.predicates
|
||||
.iter()
|
||||
.flat_map(|p| wf::predicate_obligations(fcx.infcx(),
|
||||
fcx.body_id,
|
||||
p,
|
||||
span,
|
||||
true));
|
||||
|
||||
for obligation in obligations {
|
||||
fcx.register_predicate(obligation);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_fn_or_method<'fcx>(&mut self,
|
||||
fcx: &FnCtxt<'fcx,'tcx>,
|
||||
span: Span,
|
||||
fty: &ty::BareFnTy<'tcx>,
|
||||
predicates: &ty::InstantiatedPredicates<'tcx>,
|
||||
free_id: ast::NodeId,
|
||||
implied_bounds: &mut Vec<Ty<'tcx>>)
|
||||
{
|
||||
let free_substs = &fcx.inh.infcx.parameter_environment.free_substs;
|
||||
let fty = fcx.instantiate_type_scheme(span, free_substs, fty);
|
||||
let free_id_outlive = DestructionScopeData::new(free_id);
|
||||
let sig = fcx.tcx().liberate_late_bound_regions(free_id_outlive, &fty.sig);
|
||||
|
||||
for &input_ty in &sig.inputs {
|
||||
fcx.register_wf_obligation(input_ty, span, self.code.clone());
|
||||
}
|
||||
implied_bounds.extend(sig.inputs);
|
||||
|
||||
match sig.output {
|
||||
ty::FnConverging(output) => {
|
||||
fcx.register_wf_obligation(output, span, self.code.clone());
|
||||
|
||||
// FIXME(#25759) return types should not be implied bounds
|
||||
implied_bounds.push(output);
|
||||
}
|
||||
ty::FnDiverging => { }
|
||||
}
|
||||
|
||||
self.check_where_clauses(fcx, span, predicates);
|
||||
}
|
||||
|
||||
fn check_variances_for_type_defn(&self,
|
||||
item: &ast::Item,
|
||||
ast_generics: &ast::Generics)
|
||||
{
|
||||
let item_def_id = local_def(item.id);
|
||||
let ty_predicates = self.tcx().lookup_predicates(item_def_id);
|
||||
let variances = self.tcx().item_variances(item_def_id);
|
||||
|
||||
let mut constrained_parameters: HashSet<_> =
|
||||
variances.types
|
||||
.iter_enumerated()
|
||||
.filter(|&(_, _, &variance)| variance != ty::Bivariant)
|
||||
.map(|(space, index, _)| self.param_ty(ast_generics, space, index))
|
||||
.map(|p| Parameter::Type(p))
|
||||
.collect();
|
||||
|
||||
identify_constrained_type_params(self.tcx(),
|
||||
ty_predicates.predicates.as_slice(),
|
||||
None,
|
||||
&mut constrained_parameters);
|
||||
|
||||
for (space, index, _) in variances.types.iter_enumerated() {
|
||||
let param_ty = self.param_ty(ast_generics, space, index);
|
||||
if constrained_parameters.contains(&Parameter::Type(param_ty)) {
|
||||
continue;
|
||||
}
|
||||
let span = self.ty_param_span(ast_generics, item, space, index);
|
||||
self.report_bivariance(span, param_ty.name);
|
||||
}
|
||||
|
||||
for (space, index, &variance) in variances.regions.iter_enumerated() {
|
||||
if variance != ty::Bivariant {
|
||||
continue;
|
||||
}
|
||||
|
||||
assert_eq!(space, TypeSpace);
|
||||
let span = ast_generics.lifetimes[index].lifetime.span;
|
||||
let name = ast_generics.lifetimes[index].lifetime.name;
|
||||
self.report_bivariance(span, name);
|
||||
}
|
||||
}
|
||||
|
||||
fn param_ty(&self,
|
||||
ast_generics: &ast::Generics,
|
||||
space: ParamSpace,
|
||||
index: usize)
|
||||
-> ty::ParamTy
|
||||
{
|
||||
let name = match space {
|
||||
TypeSpace => ast_generics.ty_params[index].ident.name,
|
||||
SelfSpace => special_idents::type_self.name,
|
||||
FnSpace => self.tcx().sess.bug("Fn space occupied?"),
|
||||
};
|
||||
|
||||
ty::ParamTy { space: space, idx: index as u32, name: name }
|
||||
}
|
||||
|
||||
fn ty_param_span(&self,
|
||||
ast_generics: &ast::Generics,
|
||||
item: &ast::Item,
|
||||
space: ParamSpace,
|
||||
index: usize)
|
||||
-> Span
|
||||
{
|
||||
match space {
|
||||
TypeSpace => ast_generics.ty_params[index].span,
|
||||
SelfSpace => item.span,
|
||||
FnSpace => self.tcx().sess.span_bug(item.span, "Fn space occupied?"),
|
||||
}
|
||||
}
|
||||
|
||||
fn report_bivariance(&self,
|
||||
span: Span,
|
||||
param_name: ast::Name)
|
||||
{
|
||||
error_392(self.tcx(), span, param_name);
|
||||
|
||||
let suggested_marker_id = self.tcx().lang_items.phantom_data();
|
||||
match suggested_marker_id {
|
||||
Some(def_id) => {
|
||||
self.tcx().sess.fileline_help(
|
||||
span,
|
||||
&format!("consider removing `{}` or using a marker such as `{}`",
|
||||
param_name,
|
||||
self.tcx().item_path_str(def_id)));
|
||||
}
|
||||
None => {
|
||||
// no lang items, no help!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reject_shadowing_type_parameters<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
span: Span,
|
||||
generics: &ty::Generics<'tcx>) {
|
||||
let impl_params = generics.types.get_slice(subst::TypeSpace).iter()
|
||||
.map(|tp| tp.name).collect::<HashSet<_>>();
|
||||
|
||||
for method_param in generics.types.get_slice(subst::FnSpace) {
|
||||
if impl_params.contains(&method_param.name) {
|
||||
error_194(tcx, span, method_param.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ccx, 'tcx, 'v> Visitor<'v> for CheckTypeWellFormedVisitor<'ccx, 'tcx> {
|
||||
fn visit_item(&mut self, i: &ast::Item) {
|
||||
debug!("visit_item: {:?}", i);
|
||||
self.check_item_well_formed(i);
|
||||
visit::walk_item(self, i);
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, trait_item: &'v ast::TraitItem) {
|
||||
debug!("visit_trait_item: {:?}", trait_item);
|
||||
self.check_trait_or_impl_item(trait_item.id, trait_item.span);
|
||||
visit::walk_trait_item(self, trait_item)
|
||||
}
|
||||
|
||||
fn visit_impl_item(&mut self, impl_item: &'v ast::ImplItem) {
|
||||
debug!("visit_impl_item: {:?}", impl_item);
|
||||
self.check_trait_or_impl_item(impl_item.id, impl_item.span);
|
||||
visit::walk_impl_item(self, impl_item)
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// ADT
|
||||
|
||||
struct AdtVariant<'tcx> {
|
||||
fields: Vec<AdtField<'tcx>>,
|
||||
}
|
||||
|
||||
struct AdtField<'tcx> {
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
fn struct_variant<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
||||
struct_def: &ast::StructDef)
|
||||
-> AdtVariant<'tcx> {
|
||||
let fields =
|
||||
struct_def.fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let field_ty = fcx.tcx().node_id_to_type(field.node.id);
|
||||
let field_ty = fcx.instantiate_type_scheme(field.span,
|
||||
&fcx.inh
|
||||
.infcx
|
||||
.parameter_environment
|
||||
.free_substs,
|
||||
&field_ty);
|
||||
AdtField { ty: field_ty, span: field.span }
|
||||
})
|
||||
.collect();
|
||||
AdtVariant { fields: fields }
|
||||
}
|
||||
|
||||
fn enum_variants<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
||||
enum_def: &ast::EnumDef)
|
||||
-> Vec<AdtVariant<'tcx>> {
|
||||
enum_def.variants.iter()
|
||||
.map(|variant| {
|
||||
match variant.node.kind {
|
||||
ast::TupleVariantKind(ref args) if !args.is_empty() => {
|
||||
let ctor_ty = fcx.tcx().node_id_to_type(variant.node.id);
|
||||
|
||||
// the regions in the argument types come from the
|
||||
// enum def'n, and hence will all be early bound
|
||||
let arg_tys = fcx.tcx().no_late_bound_regions(&ctor_ty.fn_args()).unwrap();
|
||||
AdtVariant {
|
||||
fields: args.iter().enumerate().map(|(index, arg)| {
|
||||
let arg_ty = arg_tys[index];
|
||||
let arg_ty =
|
||||
fcx.instantiate_type_scheme(variant.span,
|
||||
&fcx.inh
|
||||
.infcx
|
||||
.parameter_environment
|
||||
.free_substs,
|
||||
&arg_ty);
|
||||
AdtField {
|
||||
ty: arg_ty,
|
||||
span: arg.ty.span
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
}
|
||||
ast::TupleVariantKind(_) => {
|
||||
AdtVariant {
|
||||
fields: Vec::new()
|
||||
}
|
||||
}
|
||||
ast::StructVariantKind(ref struct_def) => {
|
||||
struct_variant(fcx, &**struct_def)
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn impl_implied_bounds<'fcx,'tcx>(fcx: &FnCtxt<'fcx, 'tcx>,
|
||||
impl_def_id: ast::DefId,
|
||||
span: Span)
|
||||
-> Vec<Ty<'tcx>>
|
||||
{
|
||||
let free_substs = &fcx.inh.infcx.parameter_environment.free_substs;
|
||||
match fcx.tcx().impl_trait_ref(impl_def_id) {
|
||||
Some(ref trait_ref) => {
|
||||
// Trait impl: take implied bounds from all types that
|
||||
// appear in the trait reference.
|
||||
let trait_ref = fcx.instantiate_type_scheme(span, free_substs, trait_ref);
|
||||
trait_ref.substs.types.as_slice().to_vec()
|
||||
}
|
||||
|
||||
None => {
|
||||
// Inherent impl: take implied bounds from the self type.
|
||||
let self_ty = fcx.tcx().lookup_item_type(impl_def_id).ty;
|
||||
let self_ty = fcx.instantiate_type_scheme(span, free_substs, &self_ty);
|
||||
vec![self_ty]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn error_192<'ccx,'tcx>(ccx: &'ccx CrateCtxt<'ccx, 'tcx>, span: Span) {
|
||||
span_err!(ccx.tcx.sess, span, E0192,
|
||||
"negative impls are only allowed for traits with \
|
||||
default impls (e.g., `Send` and `Sync`)")
|
||||
}
|
||||
|
||||
pub fn error_380<'ccx,'tcx>(ccx: &'ccx CrateCtxt<'ccx, 'tcx>, span: Span) {
|
||||
span_err!(ccx.tcx.sess, span, E0380,
|
||||
"traits with default impls (`e.g. unsafe impl \
|
||||
Trait for ..`) must have no methods or associated items")
|
||||
}
|
||||
|
||||
pub fn error_392<'tcx>(tcx: &ty::ctxt<'tcx>, span: Span, param_name: ast::Name) {
|
||||
span_err!(tcx.sess, span, E0392,
|
||||
"parameter `{}` is never used", param_name);
|
||||
}
|
||||
|
||||
pub fn error_194<'tcx>(tcx: &ty::ctxt<'tcx>, span: Span, name: ast::Name) {
|
||||
span_err!(tcx.sess, span, E0194,
|
||||
"type parameter `{}` shadows another type parameter of the same name",
|
||||
name);
|
||||
}
|
@ -200,10 +200,13 @@ impl<'cx, 'tcx,'v> visit::Visitor<'v> for OverlapChecker<'cx, 'tcx> {
|
||||
// if Trait1 is a supertrait of Trait2 or Trait2 is not object safe.
|
||||
|
||||
if !traits::is_object_safe(self.tcx, data.principal_def_id()) {
|
||||
// this just means the self-ty is illegal,
|
||||
// and probably this error should have
|
||||
// been reported elsewhere, but I'm trying to avoid
|
||||
// giving a misleading message below.
|
||||
// FIXME(#27579). This just means the
|
||||
// self-ty is illegal; WF will report this
|
||||
// error. But it will do so as a warning
|
||||
// for a release or two. For backwards
|
||||
// compat reasons, then, we continue to
|
||||
// report it here so that things which
|
||||
// were errors remain errors.
|
||||
span_err!(self.tcx.sess, self_ty.span, E0372,
|
||||
"the trait `{}` cannot be made into an object",
|
||||
self.tcx.item_path_str(data.principal_def_id()));
|
||||
|
@ -490,6 +490,8 @@ impl<'tcx> GetTypeParameterBounds<'tcx> for ty::GenericPredicates<'tcx> {
|
||||
}
|
||||
ty::Predicate::Equate(..) |
|
||||
ty::Predicate::RegionOutlives(..) |
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::Projection(..) => {
|
||||
false
|
||||
}
|
||||
|
@ -345,9 +345,23 @@ pub fn check_crate(tcx: &ty::ctxt, trait_map: ty::TraitMap) {
|
||||
time(time_passes, "coherence checking", (), |_|
|
||||
coherence::check_coherence(&ccx));
|
||||
|
||||
time(time_passes, "type checking", (), |_|
|
||||
time(time_passes, "wf checking (old)", (), |_|
|
||||
check::check_wf_old(&ccx));
|
||||
|
||||
time(time_passes, "item-types checking", (), |_|
|
||||
check::check_item_types(&ccx));
|
||||
|
||||
time(time_passes, "item-bodies checking", (), |_|
|
||||
check::check_item_bodies(&ccx));
|
||||
|
||||
time(time_passes, "drop-impl checking", (), |_|
|
||||
check::check_drop_impls(&ccx));
|
||||
|
||||
// Do this last so that if there are errors in the old code, they
|
||||
// get reported, and we don't get extra warnings.
|
||||
time(time_passes, "wf checking (new)", (), |_|
|
||||
check::check_wf_new(&ccx));
|
||||
|
||||
check_for_entry_fn(&ccx);
|
||||
tcx.sess.abort_if_errors();
|
||||
}
|
||||
|
@ -818,7 +818,9 @@ impl<'a> Clean<WherePredicate> for ty::Predicate<'a> {
|
||||
Predicate::Equate(ref pred) => pred.clean(cx),
|
||||
Predicate::RegionOutlives(ref pred) => pred.clean(cx),
|
||||
Predicate::TypeOutlives(ref pred) => pred.clean(cx),
|
||||
Predicate::Projection(ref pred) => pred.clean(cx)
|
||||
Predicate::Projection(ref pred) => pred.clean(cx),
|
||||
Predicate::WellFormed(_) => panic!("not user writable"),
|
||||
Predicate::ObjectSafe(_) => panic!("not user writable"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ pub trait Encodable {
|
||||
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error>;
|
||||
}
|
||||
|
||||
pub trait Decodable {
|
||||
pub trait Decodable: Sized {
|
||||
fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error>;
|
||||
}
|
||||
|
||||
|
@ -369,7 +369,7 @@ mod tests {
|
||||
use sync::{Arc, Mutex, StaticMutex, Condvar};
|
||||
use thread;
|
||||
|
||||
struct Packet<T: Send>(Arc<(Mutex<T>, Condvar)>);
|
||||
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
||||
|
||||
unsafe impl<T: Send> Send for Packet<T> {}
|
||||
unsafe impl<T> Sync for Packet<T> {}
|
||||
|
@ -60,7 +60,7 @@ pub use self::imp::Key as __KeyInner;
|
||||
/// });
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct LocalKey<T> {
|
||||
pub struct LocalKey<T:'static> {
|
||||
// The key itself may be tagged with #[thread_local], and this `Key` is
|
||||
// stored as a `static`, and it's not valid for a static to reference the
|
||||
// address of another thread_local static. For this reason we kinda wonkily
|
||||
|
@ -55,7 +55,7 @@ pub use self::imp::KeyInner as __KeyInner;
|
||||
#[unstable(feature = "scoped_tls",
|
||||
reason = "scoped TLS has yet to have wide enough use to fully consider \
|
||||
stabilizing its interface")]
|
||||
pub struct ScopedKey<T> { inner: fn() -> &'static imp::KeyInner<T> }
|
||||
pub struct ScopedKey<T:'static> { inner: fn() -> &'static imp::KeyInner<T> }
|
||||
|
||||
/// Declare a new scoped thread local storage key.
|
||||
///
|
||||
|
@ -475,7 +475,7 @@ pub enum WherePredicate {
|
||||
/// A lifetime predicate, e.g. `'a: 'b+'c`
|
||||
RegionPredicate(WhereRegionPredicate),
|
||||
/// An equality predicate (unsupported)
|
||||
EqPredicate(WhereEqPredicate)
|
||||
EqPredicate(WhereEqPredicate),
|
||||
}
|
||||
|
||||
/// A type bound, eg `for<'c> Foo: Send+Clone+'c`
|
||||
|
@ -30,6 +30,18 @@ macro_rules! span_err {
|
||||
})
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! span_err_or_warn {
|
||||
($is_warning:expr, $session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
|
||||
__diagnostic_used!($code);
|
||||
if $is_warning {
|
||||
$session.span_warn_with_code($span, &format!($($message)*), stringify!($code))
|
||||
} else {
|
||||
$session.span_err_with_code($span, &format!($($message)*), stringify!($code))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! span_warn {
|
||||
($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
|
||||
|
@ -72,7 +72,6 @@ pub mod testtypes {
|
||||
// Tests TyTrait
|
||||
pub trait FooTrait {
|
||||
fn foo_method(&self) -> usize;
|
||||
fn foo_static_method() -> usize;
|
||||
}
|
||||
|
||||
// Tests TyStruct
|
||||
|
38
src/test/compile-fail/associated-types-outlives.rs
Normal file
38
src/test/compile-fail/associated-types-outlives.rs
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// Regression test for issue #24622. The older associated types code
|
||||
// was erroneously assuming that all projections outlived the current
|
||||
// fn body, causing this (invalid) code to be accepted.
|
||||
|
||||
pub trait Foo<'a> {
|
||||
type Bar;
|
||||
}
|
||||
|
||||
impl<'a, T:'a> Foo<'a> for T {
|
||||
type Bar = &'a T;
|
||||
}
|
||||
|
||||
fn denormalise<'a, T>(t: &'a T) -> <T as Foo<'a>>::Bar {
|
||||
t
|
||||
}
|
||||
|
||||
pub fn free_and_use<T: for<'a> Foo<'a>,
|
||||
F: for<'a> FnOnce(<T as Foo<'a>>::Bar)>(x: T, f: F) {
|
||||
let y;
|
||||
'body: loop { // lifetime annotations added for clarity
|
||||
's: loop { y = denormalise(&x); break }
|
||||
drop(x); //~ ERROR cannot move out of `x` because it is borrowed
|
||||
return f(y);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// Check that we get an error when you use `<Self as Get>::Value` in
|
||||
// the trait definition even if there is no default method.
|
||||
|
||||
trait Get {
|
||||
type Value;
|
||||
}
|
||||
|
||||
trait Other {
|
||||
fn okay<U:Get>(&self, foo: U, bar: <Self as Get>::Value);
|
||||
//~^ ERROR E0277
|
||||
}
|
||||
|
||||
impl Get for () {
|
||||
type Value = f32;
|
||||
}
|
||||
|
||||
impl Get for f64 {
|
||||
type Value = u32;
|
||||
}
|
||||
|
||||
impl Other for () {
|
||||
fn okay<U:Get>(&self, _foo: U, _bar: <Self as Get>::Value) { }
|
||||
}
|
||||
|
||||
impl Other for f64 {
|
||||
fn okay<U:Get>(&self, _foo: U, _bar: <Self as Get>::Value) { }
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -9,12 +9,12 @@
|
||||
// except according to those terms.
|
||||
|
||||
fn main() {
|
||||
&1 as Copy;
|
||||
&1 as Send;
|
||||
//~^ ERROR cast to unsized type
|
||||
//~| HELP try casting to a reference instead:
|
||||
//~| SUGGESTION &1 as &Copy;
|
||||
Box::new(1) as Copy;
|
||||
//~| SUGGESTION &1 as &Send;
|
||||
Box::new(1) as Send;
|
||||
//~^ ERROR cast to unsized type
|
||||
//~| HELP try casting to a `Box` instead:
|
||||
//~| SUGGESTION Box::new(1) as Box<Copy>;
|
||||
//~| SUGGESTION Box::new(1) as Box<Send>;
|
||||
}
|
||||
|
@ -12,7 +12,9 @@ fn bar<F>(blk: F) where F: FnOnce() + 'static {
|
||||
}
|
||||
|
||||
fn foo(x: &()) {
|
||||
bar(|| { //~ ERROR cannot infer an appropriate lifetime
|
||||
bar(|| {
|
||||
//~^ ERROR cannot infer
|
||||
//~| ERROR does not fulfill
|
||||
let _ = x;
|
||||
})
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// Test that we give suitable error messages when the user attempts to
|
||||
// impl a trait `Trait` for its own object type.
|
||||
|
||||
// If the trait is not object-safe, we give a more tailored message
|
||||
// because we're such schnuckels:
|
||||
trait NotObjectSafe { fn eq(&self, other: Self); }
|
||||
impl NotObjectSafe for NotObjectSafe { } //~ ERROR E0372
|
||||
|
||||
fn main() { }
|
@ -24,9 +24,4 @@ impl Baz for Baz { } //~ ERROR E0371
|
||||
trait Other { }
|
||||
impl Other for Baz { } // OK, Other not a supertrait of Baz
|
||||
|
||||
// If the trait is not object-safe, we give a more tailored message
|
||||
// because we're such schnuckels:
|
||||
trait NotObjectSafe { fn eq(&self, other: Self); }
|
||||
impl NotObjectSafe for NotObjectSafe { } //~ ERROR E0372
|
||||
|
||||
fn main() { }
|
||||
|
57
src/test/compile-fail/dropck-object-cycle.rs
Normal file
57
src/test/compile-fail/dropck-object-cycle.rs
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// This test used to be part of a run-pass test, but revised outlives
|
||||
// rule means that it no longer compiles.
|
||||
|
||||
#![allow(unused_variables)]
|
||||
|
||||
trait Trait<'a> {
|
||||
fn long(&'a self) -> isize;
|
||||
fn short<'b>(&'b self) -> isize;
|
||||
}
|
||||
|
||||
fn object_invoke1<'d>(x: &'d Trait<'d>) -> (isize, isize) { loop { } }
|
||||
|
||||
trait MakerTrait {
|
||||
fn mk() -> Self;
|
||||
}
|
||||
|
||||
fn make_val<T:MakerTrait>() -> T {
|
||||
MakerTrait::mk()
|
||||
}
|
||||
|
||||
impl<'t> MakerTrait for Box<Trait<'t>+'static> {
|
||||
fn mk() -> Box<Trait<'t>+'static> { loop { } }
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let m : Box<Trait+'static> = make_val();
|
||||
assert_eq!(object_invoke1(&*m), (4,5));
|
||||
//~^ ERROR `*m` does not live long enough
|
||||
|
||||
// the problem here is that the full type of `m` is
|
||||
//
|
||||
// Box<Trait<'m>+'static>
|
||||
//
|
||||
// Here `'m` must be exactly the lifetime of the variable `m`.
|
||||
// This is because of two requirements:
|
||||
// 1. First, the basic type rules require that the
|
||||
// type of `m`'s value outlives the lifetime of `m`. This puts a lower
|
||||
// bound `'m`.
|
||||
//
|
||||
// 2. Meanwhile, the signature of `object_invoke1` requires that
|
||||
// we create a reference of type `&'d Trait<'d>` for some `'d`.
|
||||
// `'d` cannot outlive `'m`, so that forces the lifetime to be `'m`.
|
||||
//
|
||||
// This then conflicts with the dropck rules, which require that
|
||||
// the type of `m` *strictly outlives* `'m`. Hence we get an
|
||||
// error.
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
// so for now just live with it.
|
||||
// This test case was originally for issue #2258.
|
||||
|
||||
trait ToOpt {
|
||||
trait ToOpt: Sized {
|
||||
fn to_option(&self) -> Option<Self>;
|
||||
}
|
||||
|
||||
|
@ -15,10 +15,10 @@ trait ListItem<'a> {
|
||||
trait Collection { fn len(&self) -> usize; }
|
||||
|
||||
struct List<'a, T: ListItem<'a>> {
|
||||
slice: &'a [T]
|
||||
//~^ ERROR the parameter type `T` may not live long enough
|
||||
//~| HELP consider adding an explicit lifetime bound
|
||||
//~| NOTE ...so that the reference type `&'a [T]` does not outlive the data it points at
|
||||
slice: &'a [T]
|
||||
}
|
||||
impl<'a, T: ListItem<'a>> Collection for List<'a, T> {
|
||||
fn len(&self) -> usize {
|
||||
|
@ -11,10 +11,7 @@
|
||||
use std::any::Any;
|
||||
use std::any::TypeId;
|
||||
|
||||
pub trait Pt {}
|
||||
pub trait Rt {}
|
||||
|
||||
trait Private<P: Pt, R: Rt> {
|
||||
trait Private<P, R> {
|
||||
fn call(&self, p: P, r: R);
|
||||
}
|
||||
pub trait Public: Private< //~ ERROR private trait in exported type parameter bound
|
||||
|
@ -21,10 +21,11 @@ impl Foo for Thing {
|
||||
fn foo(b: &Bar) {
|
||||
b.foo(&0)
|
||||
//~^ ERROR the trait `Foo` is not implemented for the type `Bar`
|
||||
//~| ERROR E0038
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut thing = Thing;
|
||||
let test: &Bar = &mut thing; //~ ERROR cannot convert to a trait object
|
||||
let test: &Bar = &mut thing; //~ ERROR E0038
|
||||
foo(test);
|
||||
}
|
||||
|
@ -23,6 +23,6 @@ struct Bar {
|
||||
|
||||
const FOO : Foo = Foo;
|
||||
const BAR : Bar = Bar { foos: &[&FOO]};
|
||||
//~^ ERROR: cannot convert to a trait object because trait `Qiz` is not object-safe [E0038]
|
||||
//~^ ERROR E0038
|
||||
|
||||
fn main() { }
|
||||
|
@ -25,5 +25,6 @@ impl Bar for Thing { }
|
||||
fn main() {
|
||||
let mut thing = Thing;
|
||||
let test: &mut Bar = &mut thing;
|
||||
//~^ ERROR cannot convert to a trait object because trait `Bar` is not object-safe
|
||||
//~^ ERROR E0038
|
||||
//~| ERROR E0038
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ trait To {
|
||||
self //~ error: the trait `core::marker::Sized` is not implemented
|
||||
) -> <Dst as From<Self>>::Result where Dst: From<Self> {
|
||||
From::from( //~ error: the trait `core::marker::Sized` is not implemented
|
||||
//~^ ERROR E0277
|
||||
self
|
||||
)
|
||||
}
|
||||
|
@ -13,6 +13,5 @@ fn main()
|
||||
{
|
||||
fn bar(x:i32) ->i32 { 3*x };
|
||||
let b:Box<Any> = Box::new(bar as fn(_)->_);
|
||||
b.downcast_ref::<fn(_)->_>();
|
||||
//~^ ERROR cannot determine a type for this expression: unconstrained type
|
||||
b.downcast_ref::<fn(_)->_>(); //~ ERROR E0101
|
||||
}
|
||||
|
@ -13,5 +13,5 @@ fn main() {
|
||||
let a = 1;
|
||||
let b = 2;
|
||||
unsafe {swap::<&mut _>(transmute(&a), transmute(&b))};
|
||||
//~^ ERROR cannot determine a type for this expression: unconstrained type
|
||||
//~^ ERROR unable to infer enough type information about `_`
|
||||
}
|
||||
|
29
src/test/compile-fail/issue-27592.rs
Normal file
29
src/test/compile-fail/issue-27592.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// Regression test for issue #27591.
|
||||
|
||||
fn write<'a, F: ::std::ops::FnOnce()->::std::fmt::Arguments<'a> + 'a>(fcn: F) {
|
||||
use std::fmt::Write;
|
||||
let _ = match fcn() { a => write!(&mut Stream, "{}", a), };
|
||||
}
|
||||
|
||||
struct Stream;
|
||||
impl ::std::fmt::Write for Stream {
|
||||
fn write_str(&mut self, _s: &str) -> ::std::fmt::Result {
|
||||
Ok( () )
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
write(|| format_args!("{}", "Hello world"));
|
||||
//~^ ERROR borrowed value does not live long enough
|
||||
//~| ERROR borrowed value does not live long enough
|
||||
}
|
@ -17,6 +17,7 @@ struct S {
|
||||
name: isize
|
||||
}
|
||||
|
||||
fn bar(_x: Foo) {} //~ ERROR the trait `core::marker::Sized` is not implemented
|
||||
fn bar(_x: Foo) {}
|
||||
//~^ ERROR E0277
|
||||
|
||||
fn main() {}
|
||||
|
@ -31,7 +31,9 @@ fn a() {
|
||||
fn b() {
|
||||
let x: Box<_> = box 3;
|
||||
let y = &x;
|
||||
let z = &x as &Foo; //~ ERROR E0038
|
||||
let z = &x as &Foo;
|
||||
//~^ ERROR E0038
|
||||
//~| ERROR E0038
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
|
@ -23,14 +23,15 @@ trait Quux {
|
||||
|
||||
fn make_bar<T:Bar>(t: &T) -> &Bar {
|
||||
t
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~^ ERROR E0038
|
||||
//~| NOTE method `bar` has generic type parameters
|
||||
}
|
||||
|
||||
fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
|
||||
t as &Bar
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~^ ERROR E0038
|
||||
//~| NOTE method `bar` has generic type parameters
|
||||
//~| ERROR E0038
|
||||
}
|
||||
|
||||
fn make_quux<T:Quux>(t: &T) -> &Quux {
|
||||
|
@ -25,6 +25,7 @@ struct SExpr<'x> {
|
||||
impl<'x> PartialEq for SExpr<'x> {
|
||||
fn eq(&self, other:&SExpr<'x>) -> bool {
|
||||
println!("L1: {} L2: {}", self.elements.len(), other.elements.len());
|
||||
//~^ ERROR E0038
|
||||
let result = self.elements.len() == other.elements.len();
|
||||
|
||||
println!("Got compare {}", result);
|
||||
@ -43,8 +44,8 @@ impl <'x> Expr for SExpr<'x> {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a: Box<Expr> = Box::new(SExpr::new()); //~ ERROR trait `Expr` is not object-safe
|
||||
let b: Box<Expr> = Box::new(SExpr::new()); //~ ERROR trait `Expr` is not object-safe
|
||||
let a: Box<Expr> = Box::new(SExpr::new()); //~ ERROR E0038
|
||||
let b: Box<Expr> = Box::new(SExpr::new()); //~ ERROR E0038
|
||||
|
||||
assert_eq!(a , b);
|
||||
// assert_eq!(a , b);
|
||||
}
|
||||
|
@ -26,26 +26,28 @@ trait Quux {
|
||||
|
||||
fn make_bar<T:Bar>(t: &T) -> &Bar {
|
||||
t
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~^ ERROR E0038
|
||||
//~| NOTE method `bar` references the `Self` type in its arguments or return type
|
||||
}
|
||||
|
||||
fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
|
||||
t as &Bar
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~^ ERROR E0038
|
||||
//~| NOTE method `bar` references the `Self` type in its arguments or return type
|
||||
//~| ERROR E0038
|
||||
}
|
||||
|
||||
fn make_baz<T:Baz>(t: &T) -> &Baz {
|
||||
t
|
||||
//~^ ERROR `Baz` is not object-safe
|
||||
//~^ ERROR E0038
|
||||
//~| NOTE method `bar` references the `Self` type in its arguments or return type
|
||||
}
|
||||
|
||||
fn make_baz_explicit<T:Baz>(t: &T) -> &Baz {
|
||||
t as &Baz
|
||||
//~^ ERROR `Baz` is not object-safe
|
||||
//~^ ERROR E0038
|
||||
//~| NOTE method `bar` references the `Self` type in its arguments or return type
|
||||
//~| ERROR E0038
|
||||
}
|
||||
|
||||
fn make_quux<T:Quux>(t: &T) -> &Quux {
|
||||
|
@ -17,14 +17,15 @@ trait Foo {
|
||||
|
||||
fn foo_implicit<T:Foo+'static>(b: Box<T>) -> Box<Foo+'static> {
|
||||
b
|
||||
//~^ ERROR cannot convert to a trait object
|
||||
//~^ ERROR E0038
|
||||
//~| NOTE method `foo` has no receiver
|
||||
}
|
||||
|
||||
fn foo_explicit<T:Foo+'static>(b: Box<T>) -> Box<Foo+'static> {
|
||||
b as Box<Foo>
|
||||
//~^ ERROR cannot convert to a trait object
|
||||
//~^ ERROR E0038
|
||||
//~| NOTE method `foo` has no receiver
|
||||
//~| ERROR E0038
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -19,14 +19,15 @@ trait Bar
|
||||
|
||||
fn make_bar<T:Bar>(t: &T) -> &Bar {
|
||||
t
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~^ ERROR E0038
|
||||
//~| NOTE the trait cannot require that `Self : Sized`
|
||||
}
|
||||
|
||||
fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
|
||||
t as &Bar
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~^ ERROR E0038
|
||||
//~| NOTE the trait cannot require that `Self : Sized`
|
||||
//~| ERROR E0038
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -17,14 +17,15 @@ trait Bar : Sized {
|
||||
|
||||
fn make_bar<T:Bar>(t: &T) -> &Bar {
|
||||
t
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~^ ERROR E0038
|
||||
//~| NOTE the trait cannot require that `Self : Sized`
|
||||
}
|
||||
|
||||
fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
|
||||
t as &Bar
|
||||
//~^ ERROR `Bar` is not object-safe
|
||||
//~^ ERROR E0038
|
||||
//~| NOTE the trait cannot require that `Self : Sized`
|
||||
//~| ERROR E0038
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -24,7 +24,7 @@ fn make_bar<T:Bar<u32>>(t: &T) -> &Bar<u32> {
|
||||
|
||||
fn make_baz<T:Baz>(t: &T) -> &Baz {
|
||||
t
|
||||
//~^ ERROR `Baz` is not object-safe
|
||||
//~^ ERROR E0038
|
||||
//~| NOTE the trait cannot use `Self` as a type parameter in the supertrait listing
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ fn with_assoc<'a,'b>() {
|
||||
// outlive 'a. In this case, that means TheType<'b>::TheAssocType,
|
||||
// which is &'b (), must outlive 'a.
|
||||
|
||||
let _: &'a WithAssoc<TheType<'b>> = loop { }; //~ ERROR cannot infer
|
||||
let _: &'a WithAssoc<TheType<'b>> = loop { }; //~ ERROR reference has a longer lifetime
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -72,13 +72,12 @@ fn meh1<'a, T: Iter>(v: &'a T) -> Box<X+'a>
|
||||
where T::Item : Clone
|
||||
{
|
||||
// This case is kind of interesting. It's the same as `ok3` but
|
||||
// without the explicit declaration. In principle, it seems like
|
||||
// we ought to be able to infer that `T::Item : 'a` because we
|
||||
// invoked `v.as_self()` which yielded a value of type `&'a
|
||||
// T::Item`. But we're not that smart at present.
|
||||
// without the explicit declaration. This is valid because `T: 'a
|
||||
// => T::Item: 'a`, and the former we can deduce from our argument
|
||||
// of type `&'a T`.
|
||||
|
||||
let item = Clone::clone(v.as_item());
|
||||
Box::new(item) //~ ERROR associated type `<T as Iter>::Item` may not live
|
||||
Box::new(item)
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -12,7 +12,7 @@
|
||||
#![allow(warnings)]
|
||||
|
||||
trait A<T> { }
|
||||
struct B<'a, T>(&'a (A<T>+'a));
|
||||
struct B<'a, T:'a>(&'a (A<T>+'a));
|
||||
|
||||
trait X { }
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
#![feature(box_syntax)]
|
||||
|
||||
trait A<T> { }
|
||||
struct B<'a, T>(&'a (A<T>+'a));
|
||||
struct B<'a, T:'a>(&'a (A<T>+'a));
|
||||
|
||||
trait X { }
|
||||
impl<'a, T> X for B<'a, T> {}
|
||||
|
@ -12,7 +12,7 @@
|
||||
#![allow(warnings)]
|
||||
|
||||
trait A<T> { }
|
||||
struct B<'a, T>(&'a (A<T>+'a));
|
||||
struct B<'a, T:'a>(&'a (A<T>+'a));
|
||||
|
||||
trait X { }
|
||||
impl<'a, T> X for B<'a, T> {}
|
||||
|
@ -11,7 +11,7 @@
|
||||
#![feature(box_syntax)]
|
||||
|
||||
trait A<T> { }
|
||||
struct B<'a, T>(&'a (A<T>+'a));
|
||||
struct B<'a, T:'a>(&'a (A<T>+'a));
|
||||
|
||||
trait X { }
|
||||
impl<'a, T> X for B<'a, T> {}
|
||||
|
@ -16,15 +16,22 @@ trait A<T>
|
||||
fn get(&self) -> T { panic!() }
|
||||
}
|
||||
|
||||
struct B<'a, T>(&'a (A<T>+'a));
|
||||
struct B<'a, T:'a>(&'a (A<T>+'a));
|
||||
|
||||
trait X { fn foo(&self) {} }
|
||||
|
||||
impl<'a, T> X for B<'a, T> {}
|
||||
|
||||
fn f<'a, T, U>(v: Box<A<T>+'static>) -> Box<X+'static> {
|
||||
box B(&*v) as Box<X> //~ ERROR the parameter type `T` may not live long enough
|
||||
// oh dear!
|
||||
box B(&*v) as Box<X>
|
||||
//~^ ERROR the parameter type `T` may not live long enough
|
||||
//~| WARNING the parameter type `T` may not live long enough
|
||||
//~| WARNING the parameter type `T` may not live long enough
|
||||
//~| ERROR the parameter type `T` may not live long enough
|
||||
//~| WARNING the parameter type `T` may not live long enough
|
||||
//~| ERROR the parameter type `T` may not live long enough
|
||||
//~| ERROR the parameter type `T` may not live long enough
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -12,25 +12,27 @@
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
enum Ref1<'a, T> { //~ ERROR the parameter type `T` may not live long enough
|
||||
Ref1Variant1(&'a T)
|
||||
enum Ref1<'a, T> {
|
||||
Ref1Variant1(&'a T) //~ ERROR the parameter type `T` may not live long enough
|
||||
}
|
||||
|
||||
enum Ref2<'a, T> { //~ ERROR the parameter type `T` may not live long enough
|
||||
enum Ref2<'a, T> {
|
||||
Ref2Variant1,
|
||||
Ref2Variant2(isize, &'a T),
|
||||
Ref2Variant2(isize, &'a T), //~ ERROR the parameter type `T` may not live long enough
|
||||
}
|
||||
|
||||
enum RefOk<'a, T:'a> {
|
||||
RefOkVariant1(&'a T)
|
||||
}
|
||||
|
||||
enum RefIndirect<'a, T> { //~ ERROR the parameter type `T` may not live long enough
|
||||
enum RefIndirect<'a, T> {
|
||||
RefIndirectVariant1(isize, RefOk<'a,T>)
|
||||
//~^ ERROR the parameter type `T` may not live long enough
|
||||
}
|
||||
|
||||
enum RefDouble<'a, 'b, T> { //~ ERROR reference has a longer lifetime than the data
|
||||
enum RefDouble<'a, 'b, T> {
|
||||
RefDoubleVariant1(&'a &'b T)
|
||||
//~^ ERROR reference has a longer lifetime than the data
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
|
@ -0,0 +1,40 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
// Illustrates the "projection gap": in this test, even though we know
|
||||
// that `T::Foo: 'x`, that does not tell us that `T: 'x`, because
|
||||
// there might be other ways for the caller of `func` to show that
|
||||
// `T::Foo: 'x` holds (e.g., where-clause).
|
||||
|
||||
trait Trait1<'x> {
|
||||
type Foo;
|
||||
}
|
||||
|
||||
// calling this fn should trigger a check that the type argument
|
||||
// supplied is well-formed.
|
||||
fn wf<T>() { }
|
||||
|
||||
fn func<'x, T:Trait1<'x>>(t: &'x T::Foo)
|
||||
{
|
||||
wf::<&'x T>();
|
||||
//~^ ERROR the parameter type `T` may not live long enough
|
||||
}
|
||||
|
||||
fn caller2<'x, T:Trait1<'x>>(t: &'x T)
|
||||
{
|
||||
wf::<&'x T::Foo>(); // OK
|
||||
}
|
||||
|
||||
fn caller3<'x, T:Trait1<'x>>(t: &'x T::Foo)
|
||||
{
|
||||
wf::<&'x T::Foo>(); // OK
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,33 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
// Along with the other tests in this series, illustrates the
|
||||
// "projection gap": in this test, we know that `T: 'x`, and that is
|
||||
// enough to conclude that `T::Foo: 'x`.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
trait Trait1<'x> {
|
||||
type Foo;
|
||||
}
|
||||
|
||||
// calling this fn should trigger a check that the type argument
|
||||
// supplied is well-formed.
|
||||
fn wf<T>() { }
|
||||
|
||||
fn func<'x, T:Trait1<'x>>(t: &'x T)
|
||||
{
|
||||
wf::<&'x T::Foo>();
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { } //~ ERROR compilation successful
|
@ -0,0 +1,33 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
// Along with the other tests in this series, illustrates the
|
||||
// "projection gap": in this test, we know that `T::Foo: 'x`, and that
|
||||
// is (naturally) enough to conclude that `T::Foo: 'x`.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
trait Trait1<'x> {
|
||||
type Foo;
|
||||
}
|
||||
|
||||
// calling this fn should trigger a check that the type argument
|
||||
// supplied is well-formed.
|
||||
fn wf<T>() { }
|
||||
|
||||
fn func<'x, T:Trait1<'x>>(t: &'x T::Foo)
|
||||
{
|
||||
wf::<&'x T::Foo>();
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { } //~ ERROR compilation successful
|
@ -0,0 +1,33 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
// Along with the other tests in this series, illustrates the
|
||||
// "projection gap": in this test, we know that `T: 'x`, and that
|
||||
// is (naturally) enough to conclude that `T: 'x`.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
trait Trait1<'x> {
|
||||
type Foo;
|
||||
}
|
||||
|
||||
// calling this fn should trigger a check that the type argument
|
||||
// supplied is well-formed.
|
||||
fn wf<T>() { }
|
||||
|
||||
fn func<'x, T:Trait1<'x>>(t: &'x T)
|
||||
{
|
||||
wf::<&'x T>();
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { } //~ ERROR compilation successful
|
@ -0,0 +1,39 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
// The "projection gap" is particularly "fun" around higher-ranked
|
||||
// projections. This is because the current code is hard-coded to say
|
||||
// that a projection that contains escaping regions, like `<T as
|
||||
// Trait2<'y, 'z>>::Foo` where `'z` is bound, can only be found to
|
||||
// outlive a region if all components that appear free (`'y`, where)
|
||||
// outlive that region. However, we DON'T add those components to the
|
||||
// implied bounds set, but rather we treat projections with escaping
|
||||
// regions as opaque entities, just like projections without escaping
|
||||
// regions.
|
||||
|
||||
trait Trait1<T> { }
|
||||
|
||||
trait Trait2<'a, 'b> {
|
||||
type Foo;
|
||||
}
|
||||
|
||||
fn wf<T>() { }
|
||||
|
||||
// As a side-effect of the conservative process above, this argument
|
||||
// is not automatically considered well-formed, since for it to be WF,
|
||||
// we would need to know that `'y: 'x`, but we do not infer that.
|
||||
fn callee<'x, 'y, T>(
|
||||
t: &'x for<'z> Trait1< <T as Trait2<'y, 'z>>::Foo >)
|
||||
{
|
||||
wf::<&'x &'y i32>();
|
||||
//~^ ERROR reference has a longer lifetime than the data it references
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,29 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its
|
||||
// arguments (like `'a`) outlive `'b`.
|
||||
//
|
||||
// Rule OutlivesNominalType from RFC 1214.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod rev_variant_struct_region {
|
||||
struct Foo<'a> {
|
||||
x: fn(&'a i32),
|
||||
}
|
||||
enum Bar<'a,'b> {
|
||||
V(&'a Foo<'b>) //~ ERROR reference has a longer lifetime
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { }
|
@ -0,0 +1,29 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its
|
||||
// arguments (like `'a`) outlive `'b`.
|
||||
//
|
||||
// Rule OutlivesNominalType from RFC 1214.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod variant_struct_region {
|
||||
struct Foo<'a> {
|
||||
x: &'a i32,
|
||||
}
|
||||
enum Bar<'a,'b> {
|
||||
V(&'a Foo<'b>) //~ ERROR reference has a longer lifetime
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { }
|
@ -0,0 +1,29 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its
|
||||
// arguments (like `'a`) outlive `'b`.
|
||||
//
|
||||
// Rule OutlivesNominalType from RFC 1214.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod rev_variant_struct_type {
|
||||
struct Foo<T> {
|
||||
x: fn(T)
|
||||
}
|
||||
enum Bar<'a,'b> {
|
||||
V(&'a Foo<&'b i32>) //~ ERROR reference has a longer lifetime
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { }
|
@ -0,0 +1,29 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its
|
||||
// arguments (like `'a`) outlive `'b`.
|
||||
//
|
||||
// Rule OutlivesNominalType from RFC 1214.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod variant_struct_type {
|
||||
struct Foo<T> {
|
||||
x: T
|
||||
}
|
||||
enum Bar<'a,'b> {
|
||||
F(&'a Foo<&'b i32>) //~ ERROR reference has a longer lifetime
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { }
|
@ -0,0 +1,29 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its
|
||||
// arguments (like `'a`) outlive `'b`.
|
||||
//
|
||||
// Rule OutlivesNominalType from RFC 1214.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod rev_variant_struct_region {
|
||||
struct Foo<'a> {
|
||||
x: fn(&'a i32),
|
||||
}
|
||||
struct Bar<'a,'b> {
|
||||
f: &'a Foo<'b> //~ ERROR reference has a longer lifetime
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { }
|
@ -0,0 +1,29 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its
|
||||
// arguments (like `'a`) outlive `'b`.
|
||||
//
|
||||
// Rule OutlivesNominalType from RFC 1214.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod variant_struct_region {
|
||||
struct Foo<'a> {
|
||||
x: &'a i32,
|
||||
}
|
||||
struct Bar<'a,'b> {
|
||||
f: &'a Foo<'b> //~ ERROR reference has a longer lifetime
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { }
|
@ -0,0 +1,29 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its
|
||||
// arguments (like `'a`) outlive `'b`.
|
||||
//
|
||||
// Rule OutlivesNominalType from RFC 1214.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod rev_variant_struct_type {
|
||||
struct Foo<T> {
|
||||
x: fn(T)
|
||||
}
|
||||
struct Bar<'a,'b> {
|
||||
f: &'a Foo<&'b i32> //~ ERROR reference has a longer lifetime
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { }
|
@ -0,0 +1,29 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its
|
||||
// arguments (like `'a`) outlive `'b`.
|
||||
//
|
||||
// Rule OutlivesNominalType from RFC 1214.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod variant_struct_type {
|
||||
struct Foo<T> {
|
||||
x: T
|
||||
}
|
||||
struct Bar<'a,'b> {
|
||||
f: &'a Foo<&'b i32> //~ ERROR reference has a longer lifetime
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { }
|
@ -37,10 +37,10 @@ pub struct WithHrAssoc<T>
|
||||
}
|
||||
|
||||
fn with_assoc<'a,'b>() {
|
||||
// We get no error here because the where clause has a higher-ranked assoc type,
|
||||
// which could not be projected from.
|
||||
// We get an error because beacuse 'b:'a does not hold:
|
||||
|
||||
let _: &'a WithHrAssoc<TheType<'b>> = loop { };
|
||||
//~^ ERROR reference has a longer lifetime
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@ -57,12 +57,13 @@ pub struct WithHrAssocSub<T>
|
||||
}
|
||||
|
||||
fn with_assoc_sub<'a,'b>() {
|
||||
// Same here, because although the where clause is not HR, it
|
||||
// extends a trait in a HR way.
|
||||
// The error here is just because `'b:'a` must hold for the type
|
||||
// below to be well-formed, it is not related to the HR relation.
|
||||
|
||||
let _: &'a WithHrAssocSub<TheType<'b>> = loop { };
|
||||
//~^ ERROR reference has a longer lifetime
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { //~ ERROR compilation successful
|
||||
fn main() {
|
||||
}
|
@ -41,7 +41,8 @@ fn with_assoc<'a,'b>() {
|
||||
// outlive 'a. In this case, that means TheType<'b>::TheAssocType,
|
||||
// which is &'b (), must outlive 'a.
|
||||
|
||||
let _: &'a WithAssoc<TheType<'b>> = loop { }; //~ ERROR cannot infer
|
||||
let _: &'a WithAssoc<TheType<'b>> = loop { };
|
||||
//~^ ERROR reference has a longer lifetime
|
||||
}
|
||||
|
||||
fn main() {
|
@ -45,7 +45,7 @@ fn with_assoc<'a,'b>() {
|
||||
// outlive 'a. In this case, that means TheType<'b>::TheAssocType,
|
||||
// which is &'b (), must outlive 'a.
|
||||
|
||||
let _: &'a WithAssoc<TheType<'b>> = loop { }; //~ ERROR cannot infer
|
||||
let _: &'a WithAssoc<TheType<'b>> = loop { }; //~ ERROR reference has a longer lifetime
|
||||
}
|
||||
|
||||
fn with_assoc1<'a,'b>() where 'b : 'a {
|
||||
@ -59,11 +59,10 @@ fn with_assoc1<'a,'b>() where 'b : 'a {
|
||||
}
|
||||
|
||||
fn without_assoc<'a,'b>() {
|
||||
// Here there are no associated types and the `'b` appearing in
|
||||
// `TheType<'b>` is purely covariant, so there is no requirement
|
||||
// that `'b:'a` holds.
|
||||
// Here there are no associated types but there is a requirement
|
||||
// that `'b:'a` holds because the `'b` appears in `TheType<'b>`.
|
||||
|
||||
let _: &'a WithoutAssoc<TheType<'b>> = loop { };
|
||||
let _: &'a WithoutAssoc<TheType<'b>> = loop { }; //~ ERROR reference has a longer lifetime
|
||||
}
|
||||
|
||||
fn call_with_assoc<'a,'b>() {
|
||||
@ -72,13 +71,13 @@ fn call_with_assoc<'a,'b>() {
|
||||
// no data.
|
||||
|
||||
call::<&'a WithAssoc<TheType<'b>>>();
|
||||
//~^ ERROR cannot infer
|
||||
//~^ ERROR reference has a longer lifetime
|
||||
}
|
||||
|
||||
fn call_without_assoc<'a,'b>() {
|
||||
// As `without_assoc`, but in a distinct scenario.
|
||||
|
||||
call::<&'a WithoutAssoc<TheType<'b>>>();
|
||||
call::<&'a WithoutAssoc<TheType<'b>>>(); //~ ERROR reference has a longer lifetime
|
||||
}
|
||||
|
||||
fn call<T>() { }
|
36
src/test/compile-fail/regions-outlives-projection-hrtype.rs
Normal file
36
src/test/compile-fail/regions-outlives-projection-hrtype.rs
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// Test for the outlives relation when applied to a projection on a
|
||||
// type with bound regions. In this case, we are checking that
|
||||
// `<for<'r> fn(&'r T) as TheTrait>::TheType: 'a` If we're not
|
||||
// careful, we could wind up with a constraint that `'r:'a`, but since
|
||||
// `'r` is bound, that leads to badness. This test checks that
|
||||
// everything works.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
trait TheTrait {
|
||||
type TheType;
|
||||
}
|
||||
|
||||
fn wf<T>() { }
|
||||
|
||||
type FnType<T> = for<'r> fn(&'r T);
|
||||
|
||||
fn foo<'a,'b,T>()
|
||||
where FnType<T>: TheTrait
|
||||
{
|
||||
wf::< <FnType<T> as TheTrait>::TheType >();
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { } //~ ERROR compilation successful
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user