use middle::astconv_util::{check_path_args, NO_TPS, NO_REGIONS};
use middle::def;
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 util::lev_distance::lev_distance;
use std::cell::{Cell, Ref, RefCell};
+use std::collections::HashSet;
use std::mem::replace;
use std::slice;
use syntax::{self, abi, attr};
use syntax::ast::{self, DefId, Visibility};
use syntax::ast_util::{self, local_def};
use syntax::codemap::{self, Span};
+use syntax::feature_gate::emit_feature_err;
use syntax::owned_slice::OwnedSlice;
-use syntax::parse::token;
+use syntax::parse::token::{self, InternedString};
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::visit::{self, Visitor};
traits::VariableType(p.id));
debug!("Pattern binding {} is assigned to {} with type {:?}",
- token::get_ident(path1.node),
+ path1.node,
self.fcx.infcx().ty_to_string(
self.fcx.inh.locals.borrow().get(&p.id).unwrap().clone()),
var_ty);
}
ast::ItemFn(..) => {} // entirely within check_item_body
ast::ItemImpl(_, _, _, _, _, ref impl_items) => {
- debug!("ItemImpl {} with id {}", token::get_ident(it.ident), it.id);
+ debug!("ItemImpl {} with id {}", it.ident, it.id);
match ccx.tcx.impl_trait_ref(local_def(it.id)) {
Some(impl_trait_ref) => {
check_impl_items_against_trait(ccx,
check_bare_fn(ccx, &**decl, &**body, it.id, it.span, fn_pty.ty, param_env);
}
ast::ItemImpl(_, _, _, _, _, ref impl_items) => {
- debug!("ItemImpl {} with id {}", token::get_ident(it.ident), it.id);
+ debug!("ItemImpl {} with id {}", it.ident, it.id);
let impl_pty = ccx.tcx.lookup_item_type(ast_util::local_def(it.id));
Position::ArgumentNamed(s) if s == "Self" => (),
// So is `{A}` if A is a type parameter
Position::ArgumentNamed(s) => match types.iter().find(|t| {
- t.ident.as_str() == s
+ t.ident.name == s
}) {
Some(_) => (),
None => {
span_err!(ccx.tcx.sess, attr.span, E0230,
"there is no type parameter \
{} on trait {}",
- s, item.ident.as_str());
+ s, item.ident);
}
},
// `{:1}` and `{}` are not to be used
// This is checked by resolve
tcx.sess.span_bug(impl_item.span,
&format!("impl-item `{}` is not a member of `{:?}`",
- token::get_name(ty_impl_item.name()),
+ ty_impl_item.name(),
impl_trait_ref));
});
match impl_item.node {
span_err!(tcx.sess, impl_item.span, E0323,
"item `{}` is an associated const, \
which doesn't match its trait `{:?}`",
- token::get_name(impl_const.name),
+ impl_const.name,
impl_trait_ref)
}
}
span_err!(tcx.sess, impl_item.span, E0324,
"item `{}` is an associated method, \
which doesn't match its trait `{:?}`",
- token::get_name(impl_method.name),
+ impl_method.name,
impl_trait_ref)
}
}
span_err!(tcx.sess, impl_item.span, E0325,
"item `{}` is an associated type, \
which doesn't match its trait `{:?}`",
- token::get_name(impl_type.name),
+ impl_type.name,
impl_trait_ref)
}
}
span_err!(tcx.sess, impl_span, E0046,
"not all trait items implemented, missing: `{}`",
missing_items.iter()
- .map(<ast::Name>::as_str)
+ .map(|name| name.to_string())
.collect::<Vec<_>>().join("`, `"))
}
span_err!(tcx.sess, invalidator.span, E0399,
"the following trait items need to be reimplemented \
as `{}` was overridden: `{}`",
- invalidator.ident.as_str(),
+ invalidator.ident,
invalidated_items.iter()
- .map(<ast::Name>::as_str)
+ .map(|name| name.to_string())
.collect::<Vec<_>>().join("`, `"))
}
}
trait_def.associated_type_names.contains(&assoc_name)
}
- fn ty_infer(&self, _span: Span) -> Ty<'tcx> {
- self.infcx().next_ty_var()
+ fn ty_infer(&self,
+ ty_param_def: Option<ty::TypeParameterDef<'tcx>>,
+ substs: Option<&mut subst::Substs<'tcx>>,
+ space: Option<subst::ParamSpace>,
+ span: Span) -> Ty<'tcx> {
+ // Grab the default doing subsitution
+ let default = ty_param_def.and_then(|def| {
+ def.default.map(|ty| type_variable::Default {
+ ty: ty.subst_spanned(self.tcx(), substs.as_ref().unwrap(), Some(span)),
+ origin_span: span,
+ def_id: def.default_def_id
+ })
+ });
+
+ let ty_var = self.infcx().next_ty_var_with_default(default);
+
+ // Finally we add the type variable to the substs
+ match substs {
+ None => ty_var,
+ Some(substs) => { substs.types.push(space.unwrap(), ty_var); ty_var }
+ }
}
fn projected_ty_from_poly_trait_ref(&self,
}
}
- /// Apply "fallbacks" to some types
- /// ! gets replaced with (), unconstrained ints with i32, and unconstrained floats with f64.
- pub fn default_type_parameters(&self) {
- use middle::ty::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat, Neither};
- for (_, &mut ref ty) in &mut self.inh.tables.borrow_mut().node_types {
- let resolved = self.infcx().resolve_type_vars_if_possible(ty);
- if self.infcx().type_var_diverges(resolved) {
- demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil());
- } else {
- match self.infcx().type_is_unconstrained_numeric(resolved) {
- UnconstrainedInt => {
- demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32)
- },
- UnconstrainedFloat => {
- demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64)
- }
- Neither => { }
- }
- }
- }
- }
-
#[inline]
pub fn write_ty(&self, node_id: ast::NodeId, ty: Ty<'tcx>) {
debug!("write_ty({}, {:?}) in fcx {}",
}
}
+ /// Apply "fallbacks" to some types
+ /// ! gets replaced with (), unconstrained ints with i32, and unconstrained floats with f64.
+ fn default_type_parameters(&self) {
+ use middle::ty::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat, Neither};
+ for ty in &self.infcx().unsolved_variables() {
+ let resolved = self.infcx().resolve_type_vars_if_possible(ty);
+ if self.infcx().type_var_diverges(resolved) {
+ demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil());
+ } else {
+ match self.infcx().type_is_unconstrained_numeric(resolved) {
+ UnconstrainedInt => {
+ demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32)
+ },
+ UnconstrainedFloat => {
+ demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64)
+ }
+ Neither => { }
+ }
+ }
+ }
+ }
+
fn select_all_obligations_and_apply_defaults(&self) {
- debug!("select_all_obligations_and_apply_defaults");
+ if self.tcx().sess.features.borrow().default_type_parameter_fallback {
+ self.new_select_all_obligations_and_apply_defaults();
+ } else {
+ self.old_select_all_obligations_and_apply_defaults();
+ }
+ }
+ // Implements old type inference fallback algorithm
+ fn old_select_all_obligations_and_apply_defaults(&self) {
self.select_obligations_where_possible();
self.default_type_parameters();
self.select_obligations_where_possible();
}
+ fn new_select_all_obligations_and_apply_defaults(&self) {
+ use middle::ty::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat, Neither};
+
+ // For the time being this errs on the side of being memory wasteful but provides better
+ // error reporting.
+ // let type_variables = self.infcx().type_variables.clone();
+
+ // There is a possibility that this algorithm will have to run an arbitrary number of times
+ // to terminate so we bound it by the compiler's recursion limit.
+ for _ in (0..self.tcx().sess.recursion_limit.get()) {
+ // First we try to solve all obligations, it is possible that the last iteration
+ // has made it possible to make more progress.
+ self.select_obligations_where_possible();
+
+ let mut conflicts = Vec::new();
+
+ // Collect all unsolved type, integral and floating point variables.
+ let unsolved_variables = self.inh.infcx.unsolved_variables();
+
+ // We must collect the defaults *before* we do any unification. Because we have
+ // directly attached defaults to the type variables any unification that occurs
+ // will erase defaults causing conflicting defaults to be completely ignored.
+ let default_map: FnvHashMap<_, _> =
+ unsolved_variables
+ .iter()
+ .filter_map(|t| self.infcx().default(t).map(|d| (t, d)))
+ .collect();
+
+ let mut unbound_tyvars = HashSet::new();
+
+ debug!("select_all_obligations_and_apply_defaults: defaults={:?}", default_map);
+
+ // We loop over the unsolved variables, resolving them and if they are
+ // and unconstrainted numberic type we add them to the set of unbound
+ // variables. We do this so we only apply literal fallback to type
+ // variables without defaults.
+ for ty in &unsolved_variables {
+ let resolved = self.infcx().resolve_type_vars_if_possible(ty);
+ if self.infcx().type_var_diverges(resolved) {
+ demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil());
+ } else {
+ match self.infcx().type_is_unconstrained_numeric(resolved) {
+ UnconstrainedInt | UnconstrainedFloat => {
+ unbound_tyvars.insert(resolved);
+ },
+ Neither => {}
+ }
+ }
+ }
+
+ // We now remove any numeric types that also have defaults, and instead insert
+ // the type variable with a defined fallback.
+ for ty in &unsolved_variables {
+ if let Some(_default) = default_map.get(ty) {
+ let resolved = self.infcx().resolve_type_vars_if_possible(ty);
+
+ debug!("select_all_obligations_and_apply_defaults: ty: {:?} with default: {:?}",
+ ty, _default);
+
+ match resolved.sty {
+ ty::TyInfer(ty::TyVar(_)) => {
+ unbound_tyvars.insert(ty);
+ }
+
+ ty::TyInfer(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) => {
+ unbound_tyvars.insert(ty);
+ if unbound_tyvars.contains(resolved) {
+ unbound_tyvars.remove(resolved);
+ }
+ }
+
+ _ => {}
+ }
+ }
+ }
+
+ // If there are no more fallbacks to apply at this point we have applied all possible
+ // defaults and type inference will procede as normal.
+ if unbound_tyvars.is_empty() {
+ break;
+ }
+
+ // Finally we go through each of the unbound type variables and unify them with
+ // the proper fallback, reporting a conflicting default error if any of the
+ // unifications fail. We know it must be a conflicting default because the
+ // variable would only be in `unbound_tyvars` and have a concrete value if
+ // it had been solved by previously applying a default.
+
+ // We wrap this in a transaction for error reporting, if we detect a conflict
+ // we will rollback the inference context to its prior state so we can probe
+ // for conflicts and correctly report them.
+
+
+ let _ = self.infcx().commit_if_ok(|_: &infer::CombinedSnapshot| {
+ for ty in &unbound_tyvars {
+ if self.infcx().type_var_diverges(ty) {
+ demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil());
+ } else {
+ match self.infcx().type_is_unconstrained_numeric(ty) {
+ UnconstrainedInt => {
+ demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32)
+ },
+ UnconstrainedFloat => {
+ demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64)
+ }
+ Neither => {
+ if let Some(default) = default_map.get(ty) {
+ let default = default.clone();
+ match infer::mk_eqty(self.infcx(), false,
+ infer::Misc(default.origin_span),
+ ty, default.ty) {
+ Ok(()) => {}
+ Err(_) => {
+ conflicts.push((*ty, default));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // If there are conflicts we rollback, otherwise commit
+ if conflicts.len() > 0 {
+ Err(())
+ } else {
+ Ok(())
+ }
+ });
+
+ if conflicts.len() > 0 {
+ // Loop through each conflicting default, figuring out the default that caused
+ // a unification failure and then report an error for each.
+ for (conflict, default) in conflicts {
+ let conflicting_default =
+ self.find_conflicting_default(&unbound_tyvars, &default_map, conflict)
+ .unwrap_or(type_variable::Default {
+ ty: self.infcx().next_ty_var(),
+ origin_span: codemap::DUMMY_SP,
+ def_id: local_def(0) // what do I put here?
+ });
+
+ // This is to ensure that we elimnate any non-determinism from the error
+ // reporting by fixing an order, it doesn't matter what order we choose
+ // just that it is consistent.
+ let (first_default, second_default) =
+ if default.def_id < conflicting_default.def_id {
+ (default, conflicting_default)
+ } else {
+ (conflicting_default, default)
+ };
+
+
+ self.infcx().report_conflicting_default_types(
+ first_default.origin_span,
+ first_default,
+ second_default)
+ }
+ }
+ }
+
+ self.select_obligations_where_possible();
+ }
+
+ // For use in error handling related to default type parameter fallback. We explicitly
+ // apply the default that caused conflict first to a local version of the type variable
+ // table then apply defaults until we find a conflict. That default must be the one
+ // that caused conflict earlier.
+ fn find_conflicting_default(&self,
+ unbound_vars: &HashSet<Ty<'tcx>>,
+ default_map: &FnvHashMap<&Ty<'tcx>, type_variable::Default<'tcx>>,
+ conflict: Ty<'tcx>)
+ -> Option<type_variable::Default<'tcx>> {
+ use middle::ty::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat, Neither};
+
+ // Ensure that we apply the conflicting default first
+ let mut unbound_tyvars = Vec::with_capacity(unbound_vars.len() + 1);
+ unbound_tyvars.push(conflict);
+ unbound_tyvars.extend(unbound_vars.iter());
+
+ let mut result = None;
+ // We run the same code as above applying defaults in order, this time when
+ // we find the conflict we just return it for error reporting above.
+
+ // We also run this inside snapshot that never commits so we can do error
+ // reporting for more then one conflict.
+ for ty in &unbound_tyvars {
+ if self.infcx().type_var_diverges(ty) {
+ demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil());
+ } else {
+ match self.infcx().type_is_unconstrained_numeric(ty) {
+ UnconstrainedInt => {
+ demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32)
+ },
+ UnconstrainedFloat => {
+ demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64)
+ },
+ Neither => {
+ if let Some(default) = default_map.get(ty) {
+ let default = default.clone();
+ match infer::mk_eqty(self.infcx(), false,
+ infer::Misc(default.origin_span),
+ ty, default.ty) {
+ Ok(()) => {}
+ Err(_) => {
+ result = Some(default);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
fn select_all_obligations_or_error(&self) {
debug!("select_all_obligations_or_error");
assert!(self.inh.deferred_call_resolutions.borrow().is_empty());
self.select_all_obligations_and_apply_defaults();
+
let mut fulfillment_cx = self.inh.infcx.fulfillment_cx.borrow_mut();
match fulfillment_cx.select_all_or_error(self.infcx()) {
Ok(()) => { }
// The special-cased logic below has three functions:
// 1. Provide as good of an expected type as possible.
let expected = expected_arg_tys.get(i).map(|&ty| {
- Expectation::rvalue_hint(ty)
+ Expectation::rvalue_hint(fcx.tcx(), ty)
});
check_expr_with_unifier(fcx, &**arg,
let tcx = fcx.tcx();
let ity = tcx.lookup_item_type(did);
- let (n_tps, rps, raw_ty) =
- (ity.generics.types.len(subst::TypeSpace),
+ let (tps, rps, raw_ty) =
+ (ity.generics.types.get_slice(subst::TypeSpace),
ity.generics.regions.get_slice(subst::TypeSpace),
ity.ty);
+ debug!("impl_self_ty: tps={:?} rps={:?} raw_ty={:?}", tps, rps, raw_ty);
+
let rps = fcx.inh.infcx.region_vars_for_defs(span, rps);
- let tps = fcx.inh.infcx.next_ty_vars(n_tps);
- let substs = subst::Substs::new_type(tps, rps);
+ let mut substs = subst::Substs::new(
+ VecPerParamSpace::empty(),
+ VecPerParamSpace::new(rps, Vec::new(), Vec::new()));
+ fcx.inh.infcx.type_vars_for_defs(span, ParamSpace::TypeSpace, &mut substs, tps);
let substd_ty = fcx.instantiate_type_scheme(span, &substs, &raw_ty);
TypeAndSubsts { substs: substs, ty: substd_ty }
field.span,
|actual| {
format!("attempted to take value of method `{}` on type \
- `{}`", token::get_ident(field.node), actual)
+ `{}`", field.node, actual)
},
expr_t, None);
format!("attempted access of field `{}` on \
type `{}`, but no field with that \
name was found",
- token::get_ident(field.node),
+ field.node,
actual)
},
expr_t, None);
fn suggest_field_names<'tcx>(id : DefId,
field : &ast::SpannedIdent,
tcx : &ty::ctxt<'tcx>,
- skip : Vec<&str>) {
- let ident = token::get_ident(field.node);
- let name = &ident;
+ skip : Vec<InternedString>) {
+ let name = field.node.name.as_str();
// only find fits with at least one matching letter
let mut best_dist = name.len();
let fields = tcx.lookup_struct_fields(id);
for elem in &fields {
let n = elem.name.as_str();
// ignore already set fields
- if skip.iter().any(|&x| x == n) {
+ if skip.iter().any(|x| *x == n) {
continue;
}
// ignore private fields from non-local crates
if id.krate != ast::LOCAL_CRATE && elem.vis != Visibility::Public {
continue;
}
- let dist = lev_distance(n, name);
+ let dist = lev_distance(&n, &name);
if dist < best_dist {
best = Some(n);
best_dist = dist;
class_id);
format!("struct variant `{}::{}` has no field named `{}`",
actual, variant_type.name.as_str(),
- token::get_ident(field.ident.node))
+ field.ident.node)
}
None => {
format!("structure `{}` has no field named `{}`",
actual,
- token::get_ident(field.ident.node))
+ field.ident.node)
}
},
struct_ty,
Some((_, true)) => {
span_err!(fcx.tcx().sess, field.ident.span, E0062,
"field `{}` specified more than once",
- token::get_ident(field.ident.node));
+ field.ident.node);
error_happened = true;
}
Some((field_id, false)) => {
let (_, seen) = *class_field_map.get(&name).unwrap();
if !seen {
missing_fields.push(
- format!("`{}`", &token::get_name(name)))
+ format!("`{}`", name))
}
}
match unop {
ast::UnUniq => match ty.sty {
ty::TyBox(ty) => {
- Expectation::rvalue_hint(ty)
+ Expectation::rvalue_hint(tcx, ty)
}
_ => {
NoExpectation
// the last field of a struct can be unsized.
ExpectHasType(mt.ty)
} else {
- Expectation::rvalue_hint(mt.ty)
+ Expectation::rvalue_hint(tcx, mt.ty)
}
}
_ => NoExpectation
let tcx = fcx.tcx();
if !tcx.expr_is_lval(&**lhs) {
span_err!(tcx.sess, expr.span, E0070,
- "illegal left-hand side expression");
+ "invalid left-hand side expression");
}
let lhs_ty = fcx.expr_ty(&**lhs);
/// which still is useful, because it informs integer literals and the like.
/// See the test case `test/run-pass/coerce-expect-unsized.rs` and #20169
/// for examples of where this comes up,.
- fn rvalue_hint(ty: Ty<'tcx>) -> Expectation<'tcx> {
- match ty.sty {
+ fn rvalue_hint(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> {
+ match tcx.struct_tail(ty).sty {
ty::TySlice(_) | ty::TyTrait(..) => {
ExpectRvalueLikeUnsized(ty)
}
/// Checks whether a type can be represented in memory. In particular, it
/// identifies types that contain themselves without indirection through a
-/// pointer, which would mean their size is unbounded. This is different from
-/// the question of whether a type can be instantiated. See the definition of
-/// `check_instantiable`.
+/// pointer, which would mean their size is unbounded.
pub fn check_representable(tcx: &ty::ctxt,
sp: Span,
item_id: ast::NodeId,
// caught by case 1.
match rty.is_representable(tcx, sp) {
ty::SelfRecursive => {
- span_err!(tcx.sess, sp, E0072,
- "illegal recursive {} type; \
- wrap the inner value in a box to make it representable",
- designation);
+ span_err!(tcx.sess, sp, E0072, "invalid recursive {} type", designation);
+ tcx.sess.fileline_help(sp, "wrap the inner value in a box to make it representable");
return false
}
ty::Representable | ty::ContainsRecursive => (),
return true
}
-/// Checks whether a type can be created without an instance of itself.
-/// This is similar but different from the question of whether a type
-/// can be represented. For example, the following type:
-///
-/// enum foo { None, Some(foo) }
-///
-/// is instantiable but is not representable. Similarly, the type
-///
-/// enum foo { Some(@foo) }
-///
-/// is representable, but not instantiable.
+/// Checks whether a type can be constructed at runtime without
+/// an existing instance of that type.
pub fn check_instantiable(tcx: &ty::ctxt,
sp: Span,
- item_id: ast::NodeId)
- -> bool {
+ item_id: ast::NodeId) {
let item_ty = tcx.node_id_to_type(item_id);
- if !item_ty.is_instantiable(tcx) {
- span_err!(tcx.sess, sp, E0073,
- "this type cannot be instantiated without an \
- instance of itself");
- fileline_help!(tcx.sess, sp, "consider using `Option<{:?}>`",
- item_ty);
- false
- } else {
- true
+ if !item_ty.is_instantiable(tcx) &&
+ !tcx.sess.features.borrow().static_recursion {
+ emit_feature_err(&tcx.sess.parse_sess.span_diagnostic,
+ "static_recursion",
+ sp,
+ "this type cannot be instantiated at runtime \
+ without an instance of itself");
}
}
do_check(ccx, vs, id, hint);
check_representable(ccx.tcx, sp, id, "enum");
-
- // Check that it is possible to instantiate this enum:
- //
- // This *sounds* like the same that as representable, but it's
- // not. See def'n of `check_instantiable()` for details.
check_instantiable(ccx.tcx, sp, id);
}
// variables. If the user provided some types, we may still need
// to add defaults. If the user provided *too many* types, that's
// a problem.
- for &space in &ParamSpace::all() {
+ for &space in &[subst::SelfSpace, subst::TypeSpace, subst::FnSpace] {
adjust_type_parameters(fcx, span, space, type_defs,
require_type_space, &mut substs);
assert_eq!(substs.types.len(space), type_defs.len(space));
// Nothing specified at all: supply inference variables for
// everything.
if provided_len == 0 && !(require_type_space && space == subst::TypeSpace) {
- substs.types.replace(space, fcx.infcx().next_ty_vars(desired.len()));
+ substs.types.replace(space, Vec::new());
+ fcx.infcx().type_vars_for_defs(span, space, substs, &desired[..]);
return;
}
if !*b {
span_err!(ccx.tcx.sess, span, E0091,
"type parameter `{}` is unused",
- token::get_ident(tps[i].ident));
+ tps[i].ident);
}
}
}
}
let tcx = ccx.tcx;
- let name = token::get_ident(it.ident);
+ let name = it.ident.name.as_str();
let (n_tps, inputs, output) = if name.starts_with("atomic_") {
let split : Vec<&str> = name.split('_').collect();
assert!(split.len() >= 2, "Atomic intrinsic not correct format");