From 1ca467d4a1ee8036a91250650c4b785048b77988 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Aug 2018 19:19:49 -0400 Subject: [PATCH 1/3] change `make_query_outlives` to take an iterator --- src/librustc/infer/canonical/query_result.rs | 19 +++++++++++-------- src/librustc/traits/query/type_op/custom.rs | 9 +++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/librustc/infer/canonical/query_result.rs b/src/librustc/infer/canonical/query_result.rs index f0b6d25e9dae8..cfaa6188bb7f9 100644 --- a/src/librustc/infer/canonical/query_result.rs +++ b/src/librustc/infer/canonical/query_result.rs @@ -24,19 +24,18 @@ use infer::canonical::{ }; use infer::region_constraints::{Constraint, RegionConstraintData}; use infer::InferCtxtBuilder; -use infer::{InferCtxt, InferOk, InferResult, RegionObligation}; +use infer::{InferCtxt, InferOk, InferResult}; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::sync::Lrc; use std::fmt::Debug; -use syntax::ast; use syntax_pos::DUMMY_SP; use traits::query::{Fallible, NoSolution}; use traits::{FulfillmentContext, TraitEngine}; use traits::{Obligation, ObligationCause, PredicateObligation}; use ty::fold::TypeFoldable; use ty::subst::{Kind, UnpackedKind}; -use ty::{self, CanonicalVar, Lift, TyCtxt}; +use ty::{self, CanonicalVar, Lift, Ty, TyCtxt}; impl<'cx, 'gcx, 'tcx> InferCtxtBuilder<'cx, 'gcx, 'tcx> { /// The "main method" for a canonicalized trait query. Given the @@ -157,7 +156,12 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { let region_obligations = self.take_registered_region_obligations(); let region_constraints = self.with_region_constraints(|region_constraints| { - make_query_outlives(tcx, region_obligations, region_constraints) + make_query_outlives( + tcx, + region_obligations + .iter() + .map(|(_, r_o)| (r_o.sup_type, r_o.sub_region)), + region_constraints) }); let certainty = if ambig_errors.is_empty() { @@ -567,7 +571,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// creates query region constraints. pub fn make_query_outlives<'tcx>( tcx: TyCtxt<'_, '_, 'tcx>, - region_obligations: Vec<(ast::NodeId, RegionObligation<'tcx>)>, + outlives_obligations: impl Iterator, ty::Region<'tcx>)>, region_constraints: &RegionConstraintData<'tcx>, ) -> Vec> { let RegionConstraintData { @@ -600,9 +604,8 @@ pub fn make_query_outlives<'tcx>( .collect(); outlives.extend( - region_obligations - .into_iter() - .map(|(_, r_o)| ty::OutlivesPredicate(r_o.sup_type.into(), r_o.sub_region)) + outlives_obligations + .map(|(ty, r)| ty::OutlivesPredicate(ty.into(), r)) .map(ty::Binder::dummy), // no bound regions in the code above ); diff --git a/src/librustc/traits/query/type_op/custom.rs b/src/librustc/traits/query/type_op/custom.rs index 737388ad41101..5cf7064b0c297 100644 --- a/src/librustc/traits/query/type_op/custom.rs +++ b/src/librustc/traits/query/type_op/custom.rs @@ -102,8 +102,13 @@ fn scrape_region_constraints<'gcx, 'tcx, R>( let region_constraint_data = infcx.take_and_reset_region_constraints(); - let outlives = - query_result::make_query_outlives(infcx.tcx, region_obligations, ®ion_constraint_data); + let outlives = query_result::make_query_outlives( + infcx.tcx, + region_obligations + .iter() + .map(|(_, r_o)| (r_o.sup_type, r_o.sub_region)), + ®ion_constraint_data, + ); if outlives.is_empty() { Ok((value, None)) From 89574a6fc7c660172030724a5d1e8c0ef83094a4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Aug 2018 19:23:30 -0400 Subject: [PATCH 2/3] resolve type variables in the custom type op pathway --- src/librustc/traits/query/type_op/custom.rs | 3 +- src/test/ui/issue-53568.rs | 61 +++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/issue-53568.rs diff --git a/src/librustc/traits/query/type_op/custom.rs b/src/librustc/traits/query/type_op/custom.rs index 5cf7064b0c297..6a5ef75a660ba 100644 --- a/src/librustc/traits/query/type_op/custom.rs +++ b/src/librustc/traits/query/type_op/custom.rs @@ -106,7 +106,8 @@ fn scrape_region_constraints<'gcx, 'tcx, R>( infcx.tcx, region_obligations .iter() - .map(|(_, r_o)| (r_o.sup_type, r_o.sub_region)), + .map(|(_, r_o)| (r_o.sup_type, r_o.sub_region)) + .map(|(ty, r)| (infcx.resolve_type_vars_if_possible(&ty), r)), ®ion_constraint_data, ); diff --git a/src/test/ui/issue-53568.rs b/src/test/ui/issue-53568.rs new file mode 100644 index 0000000000000..6b479f7517244 --- /dev/null +++ b/src/test/ui/issue-53568.rs @@ -0,0 +1,61 @@ +// Copyright 2018 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. + +// Regression test for an NLL-related ICE (#53568) -- we failed to +// resolve inference variables in "custom type-ops". +// +// compile-pass + +#![feature(nll)] +#![allow(dead_code)] + +trait Future { + type Item; +} + +impl Future for F +where F: Fn() -> T +{ + type Item = T; +} + +trait Connect {} + +struct Connector { + handler: H, +} + +impl Connect for Connector +where + T: 'static, + H: Future +{ +} + +struct Client { + connector: C, +} + +fn build(_connector: C) -> Client { + unimplemented!() +} + +fn client(handler: H) -> Client +where H: Fn() + Copy +{ + let connector = Connector { + handler, + }; + let client = build(connector); + client +} + +fn main() { } + From a59584a6ff381ad701e80723db743ed0771ddad8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 21 Aug 2018 20:15:01 -0400 Subject: [PATCH 3/3] use `TypeOp` machinery for `outlives_bounds` Fixes #52992 --- .../query/type_op/implied_outlives_bounds.rs | 80 +++++++++++++++++++ src/librustc/traits/query/type_op/mod.rs | 1 + .../nll/type_check/free_region_relations.rs | 29 +++---- .../borrow_check/nll/type_check/mod.rs | 1 - src/test/ui/issue-52992.rs | 37 +++++++++ 5 files changed, 131 insertions(+), 17 deletions(-) create mode 100644 src/librustc/traits/query/type_op/implied_outlives_bounds.rs create mode 100644 src/test/ui/issue-52992.rs diff --git a/src/librustc/traits/query/type_op/implied_outlives_bounds.rs b/src/librustc/traits/query/type_op/implied_outlives_bounds.rs new file mode 100644 index 0000000000000..27534bc8c3cf7 --- /dev/null +++ b/src/librustc/traits/query/type_op/implied_outlives_bounds.rs @@ -0,0 +1,80 @@ +// Copyright 2016 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 infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResult, QueryResult}; +use traits::query::outlives_bounds::OutlivesBound; +use traits::query::Fallible; +use ty::{ParamEnvAnd, Ty, TyCtxt}; + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct ImpliedOutlivesBounds<'tcx> { + pub ty: Ty<'tcx>, +} + +impl<'tcx> ImpliedOutlivesBounds<'tcx> { + pub fn new(ty: Ty<'tcx>) -> Self { + ImpliedOutlivesBounds { ty } + } +} + +impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for ImpliedOutlivesBounds<'tcx> { + type QueryResult = Vec>; + + fn try_fast_path( + _tcx: TyCtxt<'_, 'gcx, 'tcx>, + _key: &ParamEnvAnd<'tcx, Self>, + ) -> Option { + None + } + + fn perform_query( + tcx: TyCtxt<'_, 'gcx, 'tcx>, + canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible> { + // FIXME the query should take a `ImpliedOutlivesBounds` + let Canonical { + variables, + value: + ParamEnvAnd { + param_env, + value: ImpliedOutlivesBounds { ty }, + }, + } = canonicalized; + let canonicalized = Canonical { + variables, + value: param_env.and(ty), + }; + + tcx.implied_outlives_bounds(canonicalized) + } + + fn shrink_to_tcx_lifetime( + v: &'a CanonicalizedQueryResult<'gcx, Self::QueryResult>, + ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self::QueryResult>> { + v + } +} + +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ImpliedOutlivesBounds<'tcx> { + ty, + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for ImpliedOutlivesBounds<'a> { + type Lifted = ImpliedOutlivesBounds<'tcx>; + ty, + } +} + +impl_stable_hash_for! { + struct ImpliedOutlivesBounds<'tcx> { ty } +} diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index be5e2838963ee..2f57981e7e1fe 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -21,6 +21,7 @@ use ty::{Lift, ParamEnvAnd, TyCtxt}; pub mod custom; pub mod eq; +pub mod implied_outlives_bounds; pub mod normalize; pub mod outlives; pub mod prove_predicate; diff --git a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs index e4b1aacd34f71..e21c490622c08 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs @@ -14,7 +14,7 @@ use borrow_check::nll::type_check::constraint_conversion; use borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints}; use borrow_check::nll::universal_regions::UniversalRegions; use borrow_check::nll::ToRegionVid; -use rustc::hir::def_id::DefId; +use rustc::infer::canonical::QueryRegionConstraint; use rustc::infer::outlives::free_region_map::FreeRegionRelations; use rustc::infer::region_constraints::GenericKind; use rustc::infer::InferCtxt; @@ -23,7 +23,6 @@ use rustc::traits::query::type_op::{self, TypeOp}; use rustc::ty::{self, RegionVid, Ty}; use rustc_data_structures::transitive_relation::TransitiveRelation; use std::rc::Rc; -use syntax::ast; #[derive(Debug)] crate struct UniversalRegionRelations<'tcx> { @@ -67,7 +66,6 @@ crate struct CreateResult<'tcx> { crate fn create( infcx: &InferCtxt<'_, '_, 'tcx>, - mir_def_id: DefId, param_env: ty::ParamEnv<'tcx>, location_table: &LocationTable, implicit_region_bound: Option>, @@ -75,11 +73,8 @@ crate fn create( constraints: &mut MirTypeckRegionConstraints<'tcx>, all_facts: &mut Option, ) -> CreateResult<'tcx> { - let mir_node_id = infcx.tcx.hir.as_local_node_id(mir_def_id).unwrap(); UniversalRegionRelationsBuilder { infcx, - mir_def_id, - mir_node_id, param_env, implicit_region_bound, constraints, @@ -212,8 +207,6 @@ impl UniversalRegionRelations<'tcx> { struct UniversalRegionRelationsBuilder<'this, 'gcx: 'tcx, 'tcx: 'this> { infcx: &'this InferCtxt<'this, 'gcx, 'tcx>, - mir_def_id: DefId, - mir_node_id: ast::NodeId, param_env: ty::ParamEnv<'tcx>, location_table: &'this LocationTable, universal_regions: Rc>, @@ -248,14 +241,16 @@ impl UniversalRegionRelationsBuilder<'cx, 'gcx, 'tcx> { let constraint_sets: Vec<_> = unnormalized_input_output_tys .flat_map(|ty| { debug!("build: input_or_output={:?}", ty); - let (ty, constraints) = self + let (ty, constraints1) = self .param_env .and(type_op::normalize::Normalize::new(ty)) .fully_perform(self.infcx) .unwrap_or_else(|_| bug!("failed to normalize {:?}", ty)); - self.add_implied_bounds(ty); + let constraints2 = self.add_implied_bounds(ty); normalized_inputs_and_output.push(ty); - constraints + constraints1 + .into_iter() + .chain(constraints2) }) .collect(); @@ -306,13 +301,15 @@ impl UniversalRegionRelationsBuilder<'cx, 'gcx, 'tcx> { /// either the return type of the MIR or one of its arguments. At /// the same time, compute and add any implied bounds that come /// from this local. - fn add_implied_bounds(&mut self, ty: Ty<'tcx>) { + fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option>>> { debug!("add_implied_bounds(ty={:?})", ty); - let span = self.infcx.tcx.def_span(self.mir_def_id); - let bounds = self - .infcx - .implied_outlives_bounds(self.param_env, self.mir_node_id, ty, span); + let (bounds, constraints) = + self.param_env + .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty }) + .fully_perform(self.infcx) + .unwrap_or_else(|_| bug!("failed to compute implied bounds {:?}", ty)); self.add_outlives_bounds(bounds); + constraints } /// Registers the `OutlivesBound` items from `outlives_bounds` in diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index ab83cfe25b590..69b2cfea1bd0c 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -135,7 +135,6 @@ pub(crate) fn type_check<'gcx, 'tcx>( normalized_inputs_and_output, } = free_region_relations::create( infcx, - mir_def_id, param_env, location_table, Some(implicit_region_bound), diff --git a/src/test/ui/issue-52992.rs b/src/test/ui/issue-52992.rs new file mode 100644 index 0000000000000..2ece0ee9feeaf --- /dev/null +++ b/src/test/ui/issue-52992.rs @@ -0,0 +1,37 @@ +// Copyright 2018 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. + +// Regression test for an NLL-related ICE (#52992) -- computing +// implied bounds was causing outlives relations that were not +// properly handled. +// +// compile-pass + +#![feature(nll)] + +fn main() {} + +fn fail<'a>() -> Struct<'a, Generic<()>> { + Struct(&Generic(())) +} + +struct Struct<'a, T>(&'a T) where + T: Trait + 'a, + T::AT: 'a; // only fails with this bound + +struct Generic(T); + +trait Trait { + type AT; +} + +impl Trait for Generic { + type AT = T; // only fails with a generic AT +}