Skip to content

Commit

Permalink
Auto merge of #26870 - jroesch:default-typaram-fallback, r=nikomatsakis
Browse files Browse the repository at this point in the history
This PR completes [RFC 213](https://github.com/rust-lang/rfcs/blob/master/text/0213-defaulted-type-params.md) by allowing default type parameters to influence inference. This is almost certainly a breaking change due to interactions between default type parameters and the old fallback algorithm used for integral and floating point literals.

The error messages still require polish but I wanted to get early review and feedback from others on the the changes, error messages, and test cases. I also imagine we will want to run anywhere from 1-3 versions of this on crater and evaluate the impact, and it would be best to get that ball rolling. 

The only outstanding issue I'm aware of is that type alias defaults don't work. It seems this may require significant restructuring, since during inference type aliases have already been expanded. @nikomatsakis might be able to provide some clarity here.

r? @nikomatsakis 

cc @eddyb @gankro @aturon @brson
  • Loading branch information
bors committed Jul 26, 2015
2 parents 9a196aa + 5ad36cb commit a5c12f4
Show file tree
Hide file tree
Showing 31 changed files with 949 additions and 117 deletions.
2 changes: 2 additions & 0 deletions src/doc/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2368,6 +2368,8 @@ The currently implemented features of the reference compiler are:
internally without imposing on callers
(i.e. making them behave like function calls in
terms of encapsulation).
* - `default_type_parameter_fallback` - Allows type parameter defaults to
influence type inference.

If a feature is promoted to a language feature, then all existing programs will
start to receive compilation warnings about `#![feature]` directives which enabled
Expand Down
21 changes: 19 additions & 2 deletions src/librustc/ast_map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ pub enum Node<'ast> {
NodeStructCtor(&'ast StructDef),

NodeLifetime(&'ast Lifetime),
NodeTyParam(&'ast TyParam)
}

/// Represents an entry and its parent NodeID.
Expand All @@ -142,6 +143,7 @@ enum MapEntry<'ast> {
EntryBlock(NodeId, &'ast Block),
EntryStructCtor(NodeId, &'ast StructDef),
EntryLifetime(NodeId, &'ast Lifetime),
EntryTyParam(NodeId, &'ast TyParam),

/// Roots for node trees.
RootCrate,
Expand Down Expand Up @@ -175,7 +177,8 @@ impl<'ast> MapEntry<'ast> {
NodePat(n) => EntryPat(p, n),
NodeBlock(n) => EntryBlock(p, n),
NodeStructCtor(n) => EntryStructCtor(p, n),
NodeLifetime(n) => EntryLifetime(p, n)
NodeLifetime(n) => EntryLifetime(p, n),
NodeTyParam(n) => EntryTyParam(p, n),
}
}

Expand All @@ -194,6 +197,7 @@ impl<'ast> MapEntry<'ast> {
EntryBlock(id, _) => id,
EntryStructCtor(id, _) => id,
EntryLifetime(id, _) => id,
EntryTyParam(id, _) => id,
_ => return None
})
}
Expand All @@ -213,6 +217,7 @@ impl<'ast> MapEntry<'ast> {
EntryBlock(_, n) => NodeBlock(n),
EntryStructCtor(_, n) => NodeStructCtor(n),
EntryLifetime(_, n) => NodeLifetime(n),
EntryTyParam(_, n) => NodeTyParam(n),
_ => return None
})
}
Expand Down Expand Up @@ -573,6 +578,7 @@ impl<'ast> Map<'ast> {
Some(NodePat(pat)) => pat.span,
Some(NodeBlock(block)) => block.span,
Some(NodeStructCtor(_)) => self.expect_item(self.get_parent(id)).span,
Some(NodeTyParam(ty_param)) => ty_param.span,
_ => return None,
};
Some(sp)
Expand Down Expand Up @@ -815,6 +821,14 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> {
self.parent_node = parent_node;
}

fn visit_generics(&mut self, generics: &'ast Generics) {
for ty_param in generics.ty_params.iter() {
self.insert(ty_param.id, NodeTyParam(ty_param));
}

visit::walk_generics(self, generics);
}

fn visit_trait_item(&mut self, ti: &'ast TraitItem) {
let parent_node = self.parent_node;
self.parent_node = ti.id;
Expand Down Expand Up @@ -1015,7 +1029,7 @@ impl<'a> NodePrinter for pprust::State<'a> {
NodePat(a) => self.print_pat(&*a),
NodeBlock(a) => self.print_block(&*a),
NodeLifetime(a) => self.print_lifetime(&*a),

NodeTyParam(_) => panic!("cannot print TyParam"),
// these cases do not carry enough information in the
// ast_map to reconstruct their full structure for pretty
// printing.
Expand Down Expand Up @@ -1123,6 +1137,9 @@ fn node_id_to_string(map: &Map, id: NodeId, include_id: bool) -> String {
format!("lifetime {}{}",
pprust::lifetime_to_string(&**l), id_str)
}
Some(NodeTyParam(ref ty_param)) => {
format!("typaram {:?}{}", ty_param, id_str)
}
None => {
format!("unknown node{}", id_str)
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#![feature(slice_splits)]
#![feature(slice_patterns)]
#![feature(slice_position_elem)]
#![feature(slice_concat_ext)]
#![feature(staged_api)]
#![feature(str_char)]
#![feature(str_match_indices)]
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/metadata/tydecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,7 @@ fn parse_type_param_def_<'a, 'tcx, F>(st: &mut PState<'a, 'tcx>, conv: &mut F)
assert_eq!(next(st), '|');
let index = parse_u32(st);
assert_eq!(next(st), '|');
let default_def_id = parse_def_(st, NominalType, conv);
let default = parse_opt(st, |st| parse_ty_(st, conv));
let object_lifetime_default = parse_object_lifetime_default(st, conv);

Expand All @@ -841,6 +842,7 @@ fn parse_type_param_def_<'a, 'tcx, F>(st: &mut PState<'a, 'tcx>, conv: &mut F)
def_id: def_id,
space: space,
index: index,
default_def_id: default_def_id,
default: default,
object_lifetime_default: object_lifetime_default,
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/metadata/tyencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,9 +409,9 @@ pub fn enc_region_bounds<'a, 'tcx>(w: &mut Encoder,

pub fn enc_type_param_def<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>,
v: &ty::TypeParameterDef<'tcx>) {
mywrite!(w, "{}:{}|{}|{}|",
mywrite!(w, "{}:{}|{}|{}|{}|",
token::get_name(v.name), (cx.ds)(v.def_id),
v.space.to_uint(), v.index);
v.space.to_uint(), v.index, (cx.ds)(v.default_def_id));
enc_opt(w, v.default, |w, t| enc_ty(w, cx, t));
enc_object_lifetime_default(w, cx, v.object_lifetime_default);
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/infer/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -893,8 +893,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
self.report_inference_failure(vo.clone());
}
self.give_suggestion(same_regions);
for &(ref trace, terr) in trace_origins {
self.report_and_explain_type_error(trace.clone(), &terr);
for &(ref trace, ref terr) in trace_origins {
self.report_and_explain_type_error(trace.clone(), terr);
}
}

Expand Down
127 changes: 119 additions & 8 deletions src/librustc/middle/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,50 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
}

/// Returns a type variable's default fallback if any exists. A default
/// must be attached to the variable when created, if it is created
/// without a default, this will return None.
///
/// This code does not apply to integral or floating point variables,
/// only to use declared defaults.
///
/// See `new_ty_var_with_default` to create a type variable with a default.
/// See `type_variable::Default` for details about what a default entails.
pub fn default(&self, ty: Ty<'tcx>) -> Option<type_variable::Default<'tcx>> {
match ty.sty {
ty::TyInfer(ty::TyVar(vid)) => self.type_variables.borrow().default(vid),
_ => None
}
}

pub fn unsolved_variables(&self) -> Vec<ty::Ty<'tcx>> {
let mut variables = Vec::new();

let unbound_ty_vars = self.type_variables
.borrow()
.unsolved_variables()
.into_iter()
.map(|t| self.tcx.mk_var(t));

let unbound_int_vars = self.int_unification_table
.borrow_mut()
.unsolved_variables()
.into_iter()
.map(|v| self.tcx.mk_int_var(v));

let unbound_float_vars = self.float_unification_table
.borrow_mut()
.unsolved_variables()
.into_iter()
.map(|v| self.tcx.mk_float_var(v));

variables.extend(unbound_ty_vars);
variables.extend(unbound_int_vars);
variables.extend(unbound_float_vars);

return variables;
}

fn combine_fields(&'a self, a_is_expected: bool, trace: TypeTrace<'tcx>)
-> CombineFields<'a, 'tcx> {
CombineFields {infcx: self,
Expand Down Expand Up @@ -956,13 +1000,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
pub fn next_ty_var_id(&self, diverging: bool) -> TyVid {
self.type_variables
.borrow_mut()
.new_var(diverging)
.new_var(diverging, None)
}

pub fn next_ty_var(&self) -> Ty<'tcx> {
self.tcx.mk_var(self.next_ty_var_id(false))
}

pub fn next_ty_var_with_default(&self,
default: Option<type_variable::Default<'tcx>>) -> Ty<'tcx> {
let ty_var_id = self.type_variables
.borrow_mut()
.new_var(false, default);

self.tcx.mk_var(ty_var_id)
}

pub fn next_diverging_ty_var(&self) -> Ty<'tcx> {
self.tcx.mk_var(self.next_ty_var_id(true))
}
Expand Down Expand Up @@ -996,20 +1049,55 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
.collect()
}

// We have to take `&mut Substs` in order to provide the correct substitutions for defaults
// along the way, for this reason we don't return them.
pub fn type_vars_for_defs(&self,
span: Span,
space: subst::ParamSpace,
substs: &mut Substs<'tcx>,
defs: &[ty::TypeParameterDef<'tcx>]) {

let mut vars = Vec::with_capacity(defs.len());

for def in defs.iter() {
let default = def.default.map(|default| {
type_variable::Default {
ty: default.subst_spanned(self.tcx, substs, Some(span)),
origin_span: span,
def_id: def.default_def_id
}
});

let ty_var = self.next_ty_var_with_default(default);
substs.types.push(space, ty_var);
vars.push(ty_var)
}
}

/// Given a set of generics defined on a type or impl, returns a substitution mapping each
/// type/region parameter to a fresh inference variable.
pub fn fresh_substs_for_generics(&self,
span: Span,
generics: &ty::Generics<'tcx>)
-> subst::Substs<'tcx>
{
let type_params =
generics.types.map(
|_| self.next_ty_var());
let type_params = subst::VecPerParamSpace::empty();

let region_params =
generics.regions.map(
|d| self.next_region_var(EarlyBoundRegion(span, d.name)));
subst::Substs::new(type_params, region_params)

let mut substs = subst::Substs::new(type_params, region_params);

for space in subst::ParamSpace::all().iter() {
self.type_vars_for_defs(
span,
*space,
&mut substs,
generics.types.get_slice(*space));
}

return substs;
}

/// Given a set of generics defined on a trait, returns a substitution mapping each output
Expand All @@ -1027,13 +1115,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
assert!(generics.regions.len(subst::SelfSpace) == 0);
assert!(generics.regions.len(subst::FnSpace) == 0);

let type_parameter_count = generics.types.len(subst::TypeSpace);
let type_parameters = self.next_ty_vars(type_parameter_count);
let type_params = Vec::new();

let region_param_defs = generics.regions.get_slice(subst::TypeSpace);
let regions = self.region_vars_for_defs(span, region_param_defs);

subst::Substs::new_trait(type_parameters, regions, self_ty)
let mut substs = subst::Substs::new_trait(type_params, regions, self_ty);

let type_parameter_defs = generics.types.get_slice(subst::TypeSpace);
self.type_vars_for_defs(span, subst::TypeSpace, &mut substs, type_parameter_defs);

return substs;
}

pub fn fresh_bound_region(&self, debruijn: ty::DebruijnIndex) -> ty::Region {
Expand Down Expand Up @@ -1268,6 +1360,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
self.report_and_explain_type_error(trace, err);
}

pub fn report_conflicting_default_types(&self,
span: Span,
expected: type_variable::Default<'tcx>,
actual: type_variable::Default<'tcx>) {
let trace = TypeTrace {
origin: Misc(span),
values: Types(ty::ExpectedFound {
expected: expected.ty,
found: actual.ty
})
};

self.report_and_explain_type_error(trace,
&TypeError::TyParamDefaultMismatch(ty::ExpectedFound {
expected: expected,
found: actual
}));
}

pub fn replace_late_bound_regions_with_fresh_var<T>(
&self,
span: Span,
Expand Down
Loading

0 comments on commit a5c12f4

Please sign in to comment.