Implement a new wfcheck to replace the old wf; this new code only issues

warnings. It also checks more conditions than the old code.  Keep the
old wf code around unchanged so that we can continue to issue errors for
the cases where we used to report errors.

As part of this, remove the where-clauses-must-reference-parameter rule,
which is easily circumvented.
This commit is contained in:
Niko Matsakis 2015-08-07 09:39:54 -04:00
parent ad47bd8a0f
commit 8d98877112
9 changed files with 854 additions and 145 deletions

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);
@ -586,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={:?}",
@ -600,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,
@ -1507,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
}
@ -1629,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);
}
}
@ -2477,7 +2540,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
@ -3362,7 +3426,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 {
@ -3903,14 +3969,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);
})

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.
@ -185,8 +181,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_ty in &field_tys {
fcx.register_wf_obligation(field_ty, item.span, traits::MiscObligation);
}
});
}
@ -355,8 +352,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 +416,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 +515,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 +698,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

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

@ -1,45 +0,0 @@
// 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.
fn equal<T>(_: &T, _: &T) -> bool where isize : Eq {
true //~^ ERROR cannot bound type `isize`, where clause bounds may only be attached
}
// This should be fine involves a type parameter.
fn test<T: Eq>() -> bool where Option<T> : Eq {}
// This should be rejected as well.
fn test2() -> bool where Option<isize> : Eq {}
//~^ ERROR cannot bound type `core::option::Option<isize>`, where clause bounds may
#[derive(PartialEq)]
//~^ ERROR cannot bound type `isize`, where clause bounds
enum Foo<T> where isize : Eq { MkFoo(T) }
//~^ ERROR cannot bound type `isize`, where clause bounds
fn test3<T: Eq>() -> bool where Option<Foo<T>> : Eq {}
fn test4() -> bool where Option<Foo<isize>> : Eq {}
//~^ ERROR cannot bound type `core::option::Option<Foo<isize>>`, where clause bounds
trait Baz<T> where isize : Eq {
//~^ ERROR cannot bound type `isize`, where clause bounds may only
fn baz(&self, t: T) where String : Eq; //~ ERROR cannot bound type `collections::string::String`
//~^ ERROR cannot bound type `isize`, where clause
}
impl Baz<isize> for isize where isize : Eq {
//~^ ERROR cannot bound type `isize`, where clause bounds
fn baz() where String : Eq {}
}
fn main() {
equal(&0, &0);
}