diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index e9869e2a00e5..d84cbe1f879e 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -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); } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 2ff40b3590d2..f8235ace3dd6 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -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 } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 86d6d404fb08..df8a80792a34 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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 diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index aeac38dab904..925ea1169012 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -86,15 +86,19 @@ use astconv::AstConv; use check::dropck; use check::FnCtxt; use middle::free_region::FreeRegionMap; -use middle::implicator; +use middle::implicator::{self, Implication}; use middle::mem_categorization as mc; +use middle::outlives; use middle::region::CodeExtent; +use middle::subst::Substs; use middle::traits; -use middle::ty::{self, ReScope, Ty, MethodCall, HasTypeFlags}; -use middle::infer::{self, GenericKind}; +use middle::ty::{self, RegionEscape, ReScope, Ty, MethodCall, HasTypeFlags}; +use middle::infer::{self, GenericKind, InferCtxt, SubregionOrigin, VerifyBound}; use middle::pat_util; +use middle::wf::{self, ImpliedBound}; use std::mem; +use std::rc::Rc; use syntax::{ast, ast_util}; use syntax::codemap::Span; use syntax::visit; @@ -120,12 +124,19 @@ pub fn regionck_expr(fcx: &FnCtxt, e: &ast::Expr) { rcx.resolve_regions_and_report_errors(); } -pub fn regionck_item(fcx: &FnCtxt, item: &ast::Item) { - let mut rcx = Rcx::new(fcx, RepeatingScope(item.id), item.id, Subject(item.id)); +/// Region checking during the WF phase for items. `wf_tys` are the +/// types from which we should derive implied bounds, if any. +pub fn regionck_item<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + item_id: ast::NodeId, + span: Span, + wf_tys: &[Ty<'tcx>]) { + debug!("regionck_item(item.id={:?}, wf_tys={:?}", item_id, wf_tys); + let mut rcx = Rcx::new(fcx, RepeatingScope(item_id), item_id, Subject(item_id)); let tcx = fcx.tcx(); rcx.free_region_map .relate_free_regions_from_predicates(tcx, &fcx.infcx().parameter_environment.caller_bounds); - rcx.visit_region_obligations(item.id); + rcx.relate_free_regions(wf_tys, item_id, span); + rcx.visit_region_obligations(item_id); rcx.resolve_regions_and_report_errors(); } @@ -154,22 +165,6 @@ pub fn regionck_fn(fcx: &FnCtxt, fcx.tcx().store_free_region_map(fn_id, rcx.free_region_map); } -/// Checks that the types in `component_tys` are well-formed. This will add constraints into the -/// region graph. Does *not* run `resolve_regions_and_report_errors` and so forth. -pub fn regionck_ensure_component_tys_wf<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - span: Span, - component_tys: &[Ty<'tcx>]) { - let mut rcx = Rcx::new(fcx, RepeatingScope(0), 0, SubjectNode::None); - for &component_ty in component_tys { - // Check that each type outlives the empty region. Since the - // empty region is a subregion of all others, this can't fail - // unless the type does not meet the well-formedness - // requirements. - type_must_outlive(&mut rcx, infer::RelateParamBound(span, component_ty), - component_ty, ty::ReEmpty); - } -} - /////////////////////////////////////////////////////////////////////////// // INTERNALS @@ -213,6 +208,10 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { self.fcx.ccx.tcx } + pub fn infcx(&self) -> &InferCtxt<'a,'tcx> { + self.fcx.infcx() + } + fn set_body_id(&mut self, body_id: ast::NodeId) -> ast::NodeId { mem::replace(&mut self.body_id, body_id) } @@ -325,11 +324,16 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { .to_vec(); for r_o in ®ion_obligations { - debug!("visit_region_obligations: r_o={:?}", - r_o); + debug!("visit_region_obligations: r_o={:?} cause={:?}", + r_o, r_o.cause); let sup_type = self.resolve_type(r_o.sup_type); - let origin = infer::RelateParamBound(r_o.cause.span, sup_type); - type_must_outlive(self, origin, sup_type, r_o.sub_region); + let origin = self.code_to_origin(r_o.cause.span, sup_type, &r_o.cause.code); + + if r_o.sub_region != ty::ReEmpty { + type_must_outlive(self, origin, sup_type, r_o.sub_region); + } else { + self.visit_old_school_wf(node_id, sup_type, origin); + } } // Processing the region obligations should not cause the list to grow further: @@ -337,6 +341,62 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { self.fcx.inh.infcx.fulfillment_cx.borrow().region_obligations(node_id).len()); } + fn visit_old_school_wf(&mut self, + body_id: ast::NodeId, + ty: Ty<'tcx>, + origin: infer::SubregionOrigin<'tcx>) { + // As a weird kind of hack, we use a region of empty as a signal + // to mean "old-school WF rules". The only reason the old-school + // WF rules are not encoded using WF is that this leads to errors, + // and we want to phase those in gradually. + + // FIXME(#27579) remove this weird special case once we phase in new WF rules completely + let implications = implicator::implications(self.infcx(), + body_id, + ty, + ty::ReEmpty, + origin.span()); + let origin_for_ty = |ty: Option>| 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); }) diff --git a/src/librustc_typeck/check/wf.rs b/src/librustc_typeck/check/wf.rs index 7790a29db12f..21f48d37799e 100644 --- a/src/librustc_typeck/check/wf.rs +++ b/src/librustc_typeck/check/wf.rs @@ -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 = 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(..) => { diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs new file mode 100644 index 000000000000..03e6ae2dd154 --- /dev/null +++ b/src/librustc_typeck/check/wfcheck.rs @@ -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 or the MIT license +// , 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(&mut self, item: &ast::Item, f: F) where + F: for<'fcx> FnMut(&FnCtxt<'fcx, 'tcx>, + &mut CheckTypeWellFormedVisitor<'ccx,'tcx>) -> Vec>, + { + self.with_fcx(item.id, item.span, f) + } + + fn with_fcx(&mut self, id: ast::NodeId, span: Span, mut f: F) where + F: for<'fcx> FnMut(&FnCtxt<'fcx, 'tcx>, + &mut CheckTypeWellFormedVisitor<'ccx,'tcx>) -> Vec>, + { + 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(&mut self, item: &ast::Item, mut lookup_fields: F) where + F: for<'fcx> FnMut(&FnCtxt<'fcx, 'tcx>) -> Vec>, + { + 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]) + { + 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) + { + 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>) + { + 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::>(); + + 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>, +} + +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> { + 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> +{ + 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); +} diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index 42c6bcbfbb99..1652c67c531d 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -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())); diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 5d7822a71bb5..e6824c811c58 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -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(); } diff --git a/src/test/compile-fail/where-clauses-not-parameter.rs b/src/test/compile-fail/where-clauses-not-parameter.rs deleted file mode 100644 index 7968cc37090a..000000000000 --- a/src/test/compile-fail/where-clauses-not-parameter.rs +++ /dev/null @@ -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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn equal(_: &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() -> bool where Option : Eq {} - -// This should be rejected as well. -fn test2() -> bool where Option : Eq {} -//~^ ERROR cannot bound type `core::option::Option`, where clause bounds may - -#[derive(PartialEq)] -//~^ ERROR cannot bound type `isize`, where clause bounds -enum Foo where isize : Eq { MkFoo(T) } -//~^ ERROR cannot bound type `isize`, where clause bounds - -fn test3() -> bool where Option> : Eq {} - -fn test4() -> bool where Option> : Eq {} -//~^ ERROR cannot bound type `core::option::Option>`, where clause bounds - -trait Baz 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 for isize where isize : Eq { - //~^ ERROR cannot bound type `isize`, where clause bounds - fn baz() where String : Eq {} -} - -fn main() { - equal(&0, &0); -}