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:
bors 2015-08-14 15:26:09 +00:00
commit e7261f3ab6
155 changed files with 4730 additions and 649 deletions

View File

@ -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>;

View File

@ -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.
}

View File

@ -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.

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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;
}

View File

@ -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)
}
}

View File

@ -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));
}
}
}

View File

@ -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>;
}

View File

@ -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),

View File

@ -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
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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)),
}
}
}

View 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));
}
}
}

View File

@ -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(

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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()
}

View File

@ -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)
}
}

View File

@ -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,
}
}
}

View File

@ -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
View 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)
}

View File

@ -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);

View File

@ -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))
}),
}
}
}

View File

@ -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,

View File

@ -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;
}

View File

@ -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))

View File

@ -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);

View File

@ -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);
}

View File

@ -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
}

View File

@ -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

View File

@ -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 &region_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 &region 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();

View File

@ -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(..) => {

View 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);
}

View File

@ -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()));

View File

@ -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
}

View File

@ -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();
}

View File

@ -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"),
}
}
}

View File

@ -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>;
}

View File

@ -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> {}

View File

@ -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

View File

@ -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.
///

View File

@ -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`

View File

@ -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)*) => ({

View File

@ -72,7 +72,6 @@ pub mod testtypes {
// Tests TyTrait
pub trait FooTrait {
fn foo_method(&self) -> usize;
fn foo_static_method() -> usize;
}
// Tests TyStruct

View 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() {
}

View File

@ -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() { }

View File

@ -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>;
}

View File

@ -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;
})
}

View File

@ -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() { }

View File

@ -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() { }

View 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.
}

View File

@ -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>;
}

View File

@ -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 {

View File

@ -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

View File

@ -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);
}

View File

@ -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() { }

View File

@ -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
}

View File

@ -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
)
}

View File

@ -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
}

View File

@ -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 `_`
}

View 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
}

View File

@ -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() {}

View File

@ -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() { }

View File

@ -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 {

View File

@ -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);
}

View File

@ -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 {

View File

@ -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() {

View File

@ -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() {

View File

@ -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() {

View File

@ -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
}

View File

@ -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() {

View File

@ -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() {}

View File

@ -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 { }

View File

@ -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> {}

View File

@ -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> {}

View File

@ -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> {}

View File

@ -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() {}

View File

@ -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() { }

View File

@ -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() { }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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() { }

View File

@ -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() { }

View File

@ -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() { }

View File

@ -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() { }

View File

@ -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() { }

View File

@ -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() { }

View File

@ -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() { }

View File

@ -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() { }

View File

@ -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() { }

View File

@ -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() {
}

View File

@ -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() {

View File

@ -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>() { }

View 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