Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NLL feature complete (adds feature(nll))! #46862

Merged
merged 39 commits into from
Dec 21, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f3335c6
simplify `AnonTypeDecl` in the impl trait code
nikomatsakis Dec 6, 2017
f6741d0
region_infer/values.rs: rustfmt
nikomatsakis Dec 6, 2017
e447b54
Add tracking of causes for nll
Nashenas88 Nov 21, 2017
39b0e49
rustfmt: borrow_check/mod.rs
nikomatsakis Dec 14, 2017
594c386
dump out causal information for "free region" errors
nikomatsakis Dec 7, 2017
741ef41
use Rc to store nonlexical_regioncx in Borrows
nikomatsakis Dec 7, 2017
0e64a75
integrate -Znll-dump-cause into borrowck
nikomatsakis Dec 7, 2017
fe89f4b
get the `DefiningTy` from the `body_owner_kind` not type
nikomatsakis Dec 8, 2017
4a967c9
propagate `region_bound_pairs` into MIR type-check
nikomatsakis Dec 8, 2017
e96f4be
extract `instantiate_anon_types` to the `InferCtxt`
nikomatsakis Dec 9, 2017
8e64ba8
extract `constrain_anon_types` to the `InferCtxt`
nikomatsakis Dec 9, 2017
7f50e7c
extract the writeback code for anon types into InferCtxt
nikomatsakis Dec 9, 2017
a66c651
pass `UniversalRegions` to MIR type-checker instead of fields
nikomatsakis Dec 10, 2017
da63aaa
extract `input_output` code into its own module
nikomatsakis Dec 10, 2017
93afb1a
connect NLL type checker to the impl trait code
nikomatsakis Dec 10, 2017
58b0506
Move MirVisitable to visit.rs
spastorino Dec 12, 2017
6d2987c
Move categorize logic out of visit_local function
nikomatsakis Dec 20, 2017
3a185a5
Add three point error handling to borrowck
spastorino Dec 12, 2017
e28d03f
only dump causes if we have nothing better
nikomatsakis Dec 14, 2017
4089d14
move nice-region-error reporting into its own module
nikomatsakis Dec 12, 2017
93498e0
make `util` fns private to nice_region_error
nikomatsakis Dec 12, 2017
3720242
extract `find_anon_type` into its own module
nikomatsakis Dec 12, 2017
a28ab84
nice_region_error: rustfmt
nikomatsakis Dec 12, 2017
cba4732
introduce a `NiceRegionError` type and define methods on that
nikomatsakis Dec 12, 2017
de56308
use `Option<ErrorReported>` instead of `bool`
nikomatsakis Dec 12, 2017
94e7072
give precedence to `try_report_named_anon_conflict` method
nikomatsakis Dec 12, 2017
6b39781
connect NLL machinery to the `NiceRegionError` code
nikomatsakis Dec 12, 2017
3788f42
refactor `report_generic_bound_failure` to be usable by NLL code
nikomatsakis Dec 19, 2017
508a831
use `report_generic_bound_failure` when we can in the compiler
nikomatsakis Dec 19, 2017
95b6148
Add nll_dump_cause helper to Session
spastorino Dec 19, 2017
0b2db1e
Add nll feature and make nll imply nll_dump_cause
spastorino Dec 19, 2017
2019d69
feature nll implies two-phase-borrows
spastorino Dec 19, 2017
e980fb8
feature nll implies borrowck=mir
spastorino Dec 19, 2017
cfa4ffa
document and tweak the nll, use_mir, etc helpers
nikomatsakis Dec 20, 2017
80c510e
when using feature(nll), don't warn about AST-based region errors
nikomatsakis Dec 20, 2017
cba8256
add some run-pass tests for NLL showing that things work as expected
nikomatsakis Dec 7, 2017
3f490ca
convert region-liveness-drop{-,-no-}may-dangle.rs into ui tests
nikomatsakis Dec 20, 2017
4f549fe
improve comment about instantiating anon types
nikomatsakis Dec 20, 2017
d925f4d
fix truncated comment
nikomatsakis Dec 20, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 42 additions & 5 deletions src/librustc/infer/anon_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,34 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
/// Moreover, it returns a `AnonTypeMap` that would map `?0` to
/// info about the `impl Iterator<..>` type and `?1` to info about
/// the `impl Debug` type.
///
/// # Parameters
///
/// - `parent_def_id` -- we will only instantiate anonymous types
/// with this parent. This is typically the def-id of the function
/// in whose return type anon types are being instantiated.
/// - `body_id` -- the body-id with which the resulting obligations should
/// be associated
/// - `param_env` -- the in-scope parameter environment to be used for
/// obligations
/// - `value` -- the value within which we are instantiating anon types
pub fn instantiate_anon_types<T: TypeFoldable<'tcx>>(
&self,
parent_def_id: DefId,
body_id: ast::NodeId,
param_env: ty::ParamEnv<'tcx>,
value: &T,
) -> InferOk<'tcx, (T, AnonTypeMap<'tcx>)> {
debug!(
"instantiate_anon_types(value={:?}, body_id={:?}, param_env={:?})",
"instantiate_anon_types(value={:?}, parent_def_id={:?}, body_id={:?}, param_env={:?})",
value,
parent_def_id,
body_id,
param_env,
);
let mut instantiator = Instantiator {
infcx: self,
parent_def_id,
body_id,
param_env,
anon_types: DefIdMap(),
Expand Down Expand Up @@ -480,6 +494,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {

struct Instantiator<'a, 'gcx: 'tcx, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
parent_def_id: DefId,
body_id: ast::NodeId,
param_env: ty::ParamEnv<'tcx>,
anon_types: AnonTypeMap<'tcx>,
Expand All @@ -489,11 +504,33 @@ struct Instantiator<'a, 'gcx: 'tcx, 'tcx: 'a> {
impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
fn instantiate_anon_types_in_map<T: TypeFoldable<'tcx>>(&mut self, value: &T) -> T {
debug!("instantiate_anon_types_in_map(value={:?})", value);
let tcx = self.infcx.tcx;
value.fold_with(&mut BottomUpFolder {
tcx: self.infcx.tcx,
fldop: |ty| if let ty::TyAnon(def_id, substs) = ty.sty {
self.fold_anon_ty(ty, def_id, substs)
} else {
tcx,
fldop: |ty| {
if let ty::TyAnon(def_id, substs) = ty.sty {
// Check that this is `impl Trait` type is declared by
// `parent_def_id`. During the first phase of type-check, this
// is true, but during NLL type-check, we sometimes encounter
// `impl Trait` types in e.g. inferred closure signatures that
// are not 'local' to the current function and hence which
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is confusing.

If we had typeof or impl Trait associated types, this could just as well happen within the signature of a function. The thing is, we want to make a distinction between "syntactic" impl Trait types that are "bound to our function" and "free" impl Trait types that are not.

Aka if we have this:

mod some_mod {
    type Foo = impl Iterator; // not sure about the exact syntax
}

mod my_mod {
    fn foo(x: ::some_mod::Foo) -> impl Iterator { x }
}

Then in foo, ::some_mod::Foo is a "free" TyAnon while the return type is a "bound" TyAnon. Luckily for us, uses of TyAnon can't be recursive (for now?), so if a TyAnon has our own DefId then it is definitely bound to our function.

I'm not sure this is the best way to express this, but that's how things are set up for now and we are not going to refactor the world for NLL.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case you give, we are still returning an impl Iterator, and it would still be local to foo() -- that is, when this function executes, we would have effectively desugared to:

mod my_mod {
    abstract type FooReturn: Iterator;
    fn foo(x: ::some_mod::Foo) -> FooReturn { x }

and hence we would be "instantiating" FooReturn (whose value would then get inferred to a TyAnon representing some_mod::Foo).

In fact, this is basically exactly the case that this code is designed to handle in NLL: you can construct a very similar scenario with closures and return type inference today.

I'm not sure this is the best way to express this, but that's how things are set up for now and we are not going to refactor the world for NLL.

I'm not really sure what this quite means. It doesn't seem like NLL requires any particular refactoring of the world here -- it's just that NLL exposes us to more complex cases than we had to handle before, but it's not like it's a problem to handle them. I can try to reword the comment, anyhow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed a second attempt! =)

// ought not to be instantiated.
if let Some(anon_node_id) = tcx.hir.as_local_node_id(def_id) {
let anon_parent_node_id = tcx.hir.get_parent(anon_node_id);
let anon_parent_def_id = tcx.hir.local_def_id(anon_parent_node_id);
if self.parent_def_id == anon_parent_def_id {
return self.fold_anon_ty(ty, def_id, substs);
}

debug!("instantiate_anon_types_in_map: \
encountered anon with wrong parent \
def_id={:?} \
anon_parent_def_id={:?}",
def_id,
anon_parent_def_id);
}
}

ty
},
})
Expand Down
18 changes: 11 additions & 7 deletions src/librustc/util/ppaux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -865,13 +865,17 @@ impl fmt::Debug for ty::RegionVid {
define_print! {
() ty::InferTy, (self, f, cx) {
display {
match *self {
ty::TyVar(_) => write!(f, "_"),
ty::IntVar(_) => write!(f, "{}", "{integer}"),
ty::FloatVar(_) => write!(f, "{}", "{float}"),
ty::FreshTy(v) => write!(f, "FreshTy({})", v),
ty::FreshIntTy(v) => write!(f, "FreshIntTy({})", v),
ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v)
if cx.is_verbose {
print!(f, cx, print_debug(self))
} else {
match *self {
ty::TyVar(_) => write!(f, "_"),
ty::IntVar(_) => write!(f, "{}", "{integer}"),
ty::FloatVar(_) => write!(f, "{}", "{float}"),
ty::FreshTy(v) => write!(f, "FreshTy({})", v),
ty::FreshIntTy(v) => write!(f, "FreshIntTy({})", v),
ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v)
}
}
}
debug {
Expand Down
3 changes: 1 addition & 2 deletions src/librustc_mir/borrow_check/nll/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,12 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
Option<ClosureRegionRequirements<'gcx>>,
) {
// Run the MIR type-checker.
let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
let liveness = &LivenessResults::compute(mir);
let constraint_sets = &type_check::type_check(
infcx,
mir_node_id,
param_env,
mir,
def_id,
&universal_regions,
&liveness,
flow_inits,
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_mir/borrow_check/nll/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
.map(|origin| RegionDefinition::new(origin))
.collect();

let nll_dump_cause = ty::tls::with(|tcx| tcx.sess.opts.debugging_opts.nll_dump_cause);

let mut result = Self {
definitions,
elements: elements.clone(),
liveness_constraints: RegionValues::new(
elements,
num_region_variables,
TrackCauses(true),
TrackCauses(nll_dump_cause),
),
inferred_values: None,
constraints: Vec::new(),
Expand Down
34 changes: 22 additions & 12 deletions src/librustc_mir/borrow_check/nll/renumber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,44 @@ use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};

/// Replaces all free regions appearing in the MIR with fresh
/// inference variables, returning the number of variables created.
pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &mut Mir<'tcx>) {
pub fn renumber_mir<'tcx>(infcx: &InferCtxt<'_, '_, 'tcx>, mir: &mut Mir<'tcx>) {
debug!("renumber_mir()");
debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count);

let mut visitor = NLLVisitor { infcx };
visitor.visit_mir(mir);
}

/// Replaces all regions appearing in `value` with fresh inference
/// variables.
pub fn renumber_regions<'tcx, T>(
infcx: &InferCtxt<'_, '_, 'tcx>,
ty_context: TyContext,
value: &T,
) -> T
where
T: TypeFoldable<'tcx>,
{
debug!("renumber_regions(value={:?})", value);

infcx
.tcx
.fold_regions(value, &mut false, |_region, _depth| {
let origin = NLLRegionVariableOrigin::Inferred(ty_context);
infcx.next_nll_region_var(origin)
})
}

struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
}

impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
/// Replaces all regions appearing in `value` with fresh inference
/// variables. This is what we do for almost the entire MIR, with
/// the exception of the declared types of our arguments.
fn renumber_regions<T>(&mut self, ty_context: TyContext, value: &T) -> T
where
T: TypeFoldable<'tcx>,
{
debug!("renumber_regions(value={:?})", value);

self.infcx
.tcx
.fold_regions(value, &mut false, |_region, _depth| {
let origin = NLLRegionVariableOrigin::Inferred(ty_context);
self.infcx.next_nll_region_var(origin)
})
renumber_regions(self.infcx, ty_context, value)
}
}

Expand Down
109 changes: 107 additions & 2 deletions src/librustc_mir/borrow_check/nll/type_check/input_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,15 @@
//! `RETURN_PLACE` the MIR arguments) are always fully normalize (and
//! contain revealed `impl Trait` values).

use borrow_check::nll::renumber;
use borrow_check::nll::universal_regions::UniversalRegions;
use rustc::hir::def_id::DefId;
use rustc::infer::InferOk;
use rustc::ty::Ty;
use rustc::ty::subst::Subst;
use rustc::mir::*;
use rustc::mir::visit::TyContext;
use rustc::traits::PredicateObligations;

use rustc_data_structures::indexed_vec::Idx;

Expand All @@ -29,13 +35,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
pub(super) fn equate_inputs_and_outputs(
&mut self,
mir: &Mir<'tcx>,
mir_def_id: DefId,
universal_regions: &UniversalRegions<'tcx>,
) {
let tcx = self.infcx.tcx;

let &UniversalRegions {
unnormalized_output_ty,
unnormalized_input_tys,
..
} = universal_regions;
let infcx = self.infcx;

let start_position = Location {
block: START_BLOCK,
Expand All @@ -52,10 +62,88 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {

// Return types are a bit more complex. They may contain existential `impl Trait`
// types.

debug!(
"equate_inputs_and_outputs: unnormalized_output_ty={:?}",
unnormalized_output_ty
);
let output_ty = self.normalize(&unnormalized_output_ty, start_position);
debug!(
"equate_inputs_and_outputs: normalized output_ty={:?}",
output_ty
);
let mir_output_ty = mir.local_decls[RETURN_PLACE].ty;
self.equate_normalized_input_or_output(start_position, output_ty, mir_output_ty);
let anon_type_map = self.fully_perform_op(start_position.at_self(), |cx| {
let mut obligations = ObligationAccumulator::default();

let (output_ty, anon_type_map) = obligations.add(infcx.instantiate_anon_types(
mir_def_id,
cx.body_id,
cx.param_env,
&output_ty,
));
debug!(
"equate_inputs_and_outputs: instantiated output_ty={:?}",
output_ty
);
debug!(
"equate_inputs_and_outputs: anon_type_map={:#?}",
anon_type_map
);

debug!(
"equate_inputs_and_outputs: mir_output_ty={:?}",
mir_output_ty
);
obligations.add(infcx
.at(&cx.misc(cx.last_span), cx.param_env)
.eq(output_ty, mir_output_ty)?);

for (&anon_def_id, anon_decl) in &anon_type_map {
let anon_defn_ty = tcx.type_of(anon_def_id);
let anon_defn_ty = anon_defn_ty.subst(tcx, anon_decl.substs);
let anon_defn_ty = renumber::renumber_regions(
cx.infcx,
TyContext::Location(start_position),
&anon_defn_ty,
);
debug!(
"equate_inputs_and_outputs: concrete_ty={:?}",
anon_decl.concrete_ty
);
debug!("equate_inputs_and_outputs: anon_defn_ty={:?}", anon_defn_ty);
obligations.add(infcx
.at(&cx.misc(cx.last_span), cx.param_env)
.eq(anon_decl.concrete_ty, anon_defn_ty)?);
}

debug!("equate_inputs_and_outputs: equated");

Ok(InferOk {
value: Some(anon_type_map),
obligations: obligations.into_vec(),
})
}).unwrap_or_else(|terr| {
span_mirbug!(
self,
start_position,
"equate_inputs_and_outputs: `{:?}=={:?}` failed with `{:?}`",
output_ty,
mir_output_ty,
terr
);
None
});

// Finally
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

truncated comment?

if let Some(anon_type_map) = anon_type_map {
self.fully_perform_op(start_position.at_self(), |_cx| {
infcx.constrain_anon_types(&anon_type_map, universal_regions);
Ok(InferOk {
value: (),
obligations: vec![],
})
}).unwrap();
}
}

fn equate_normalized_input_or_output(&mut self, location: Location, a: Ty<'tcx>, b: Ty<'tcx>) {
Expand All @@ -73,3 +161,20 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
}
}

#[derive(Debug, Default)]
struct ObligationAccumulator<'tcx> {
obligations: PredicateObligations<'tcx>,
}

impl<'tcx> ObligationAccumulator<'tcx> {
fn add<T>(&mut self, value: InferOk<'tcx, T>) -> T {
let InferOk { value, obligations } = value;
self.obligations.extend(obligations);
value
}

fn into_vec(self) -> PredicateObligations<'tcx> {
self.obligations
}
}
8 changes: 5 additions & 3 deletions src/librustc_mir/borrow_check/nll/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use borrow_check::nll::universal_regions::UniversalRegions;
use dataflow::FlowAtLocation;
use dataflow::MaybeInitializedLvals;
use dataflow::move_paths::MoveData;
use rustc::hir::def_id::DefId;
use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
use rustc::infer::region_constraints::{GenericKind, RegionConstraintData};
use rustc::traits::{self, FulfillmentContext};
Expand Down Expand Up @@ -77,9 +78,9 @@ mod input_output;
/// # Parameters
///
/// - `infcx` -- inference context to use
/// - `body_id` -- body-id of the MIR being checked
/// - `param_env` -- parameter environment to use for trait solving
/// - `mir` -- MIR to type-check
/// - `mir_def_id` -- DefId from which the MIR is derived (must be local)
/// - `region_bound_pairs` -- the implied outlives obligations between type parameters
/// and lifetimes (e.g., `&'a T` implies `T: 'a`)
/// - `implicit_region_bound` -- a region which all generic parameters are assumed
Expand All @@ -94,14 +95,15 @@ mod input_output;
/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis
pub(crate) fn type_check<'gcx, 'tcx>(
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
body_id: ast::NodeId,
param_env: ty::ParamEnv<'gcx>,
mir: &Mir<'tcx>,
mir_def_id: DefId,
universal_regions: &UniversalRegions<'tcx>,
liveness: &LivenessResults,
flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
) -> MirTypeckRegionConstraints<'tcx> {
let body_id = infcx.tcx.hir.as_local_node_id(mir_def_id).unwrap();
let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body));
type_check_internal(
infcx,
Expand All @@ -113,7 +115,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
&mut |cx| {
liveness::generate(cx, mir, liveness, flow_inits, move_data);

cx.equate_inputs_and_outputs(mir, universal_regions);
cx.equate_inputs_and_outputs(mir, mir_def_id, universal_regions);
},
)
}
Expand Down
Loading