diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index c392d92a83f..601a9a73c3d 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -44,11 +44,9 @@ register_diagnostics!( E0025, E0026, E0027, - E0028, E0029, E0030, E0031, - E0032, E0033, E0034, E0035, diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index e1205ae1f76..fd71e4f7b1f 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -673,9 +673,14 @@ pub fn get_enum_variants(intr: Rc, cdata: Cmd, id: ast::NodeId, let ctor_ty = item_type(ast::DefId { krate: cdata.cnum, node: id}, item, tcx, cdata); let name = item_name(&*intr, item); - let arg_tys = match ty::get(ctor_ty).sty { - ty::ty_bare_fn(ref f) => f.sig.inputs.clone(), - _ => Vec::new(), // Nullary enum variant. + let (ctor_ty, arg_tys) = match ty::get(ctor_ty).sty { + ty::ty_bare_fn(ref f) => + (Some(ctor_ty), f.sig.inputs.clone()), + _ => // Nullary or struct enum variant. + (None, get_struct_fields(intr.clone(), cdata, did.node) + .iter() + .map(|field_ty| get_type(cdata, field_ty.id.node, tcx).ty) + .collect()) }; match variant_disr_val(item) { Some(val) => { disr_val = val; } diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 7f429bef129..2256bd71e65 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -667,21 +667,12 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> { let struct_type = ty::lookup_item_type(self.tcx, id).ty; let struct_desc = match ty::get(struct_type).sty { - ty::ty_struct(_, _) => format!("struct `{}`", ty::item_path_str(self.tcx, id)), - ty::ty_bare_fn(ty::BareFnTy { sig: ty::FnSig { output, .. }, .. }) => { - // Struct `id` is really a struct variant of an enum, - // and we're really looking at the variant's constructor - // function. So get the return type for a detailed error - // message. - let enum_id = match ty::get(output).sty { - ty::ty_enum(id, _) => id, - _ => self.tcx.sess.span_bug(span, "enum variant doesn't \ - belong to an enum") - }; + ty::ty_struct(_, _) => + format!("struct `{}`", ty::item_path_str(self.tcx, id)), + ty::ty_enum(enum_id, _) => format!("variant `{}` of enum `{}`", ty::with_path(self.tcx, id, |mut p| p.last().unwrap()), - ty::item_path_str(self.tcx, enum_id)) - } + ty::item_path_str(self.tcx, enum_id)), _ => self.tcx.sess.span_bug(span, "can't find struct for field") }; let msg = match name { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index c54f2aec12d..8c602548f33 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -50,7 +50,7 @@ use syntax::ast::{CrateNum, DefId, FnStyle, Ident, ItemTrait, LOCAL_CRATE}; use syntax::ast::{MutImmutable, MutMutable, Name, NamedField, NodeId}; use syntax::ast::{Onceness, StmtExpr, StmtSemi, StructField, UnnamedField}; use syntax::ast::{Visibility}; -use syntax::ast_util::{mod, PostExpansionMethod, is_local, lit_is_str}; +use syntax::ast_util::{mod, is_local, lit_is_str, local_def, PostExpansionMethod}; use syntax::attr::{mod, AttrMetaMethods}; use syntax::codemap::Span; use syntax::parse::token::{mod, InternedString}; @@ -4221,7 +4221,7 @@ pub fn ty_to_def_id(ty: t) -> Option { pub struct VariantInfo { pub args: Vec, pub arg_names: Option >, - pub ctor_ty: t, + pub ctor_ty: Option, pub name: ast::Name, pub id: ast::DefId, pub disr_val: Disr, @@ -4249,7 +4249,7 @@ impl VariantInfo { return VariantInfo { args: arg_tys, arg_names: None, - ctor_ty: ctor_ty, + ctor_ty: Some(ctor_ty), name: ast_variant.node.name.name, id: ast_util::local_def(ast_variant.node.id), disr_val: discriminant, @@ -4262,7 +4262,8 @@ impl VariantInfo { assert!(fields.len() > 0); - let arg_tys = ty_fn_args(ctor_ty).iter().map(|a| *a).collect(); + let arg_tys = struct_def.fields.iter() + .map(|field| node_id_to_type(cx, field.node.id)).collect(); let arg_names = fields.iter().map(|field| { match field.node.kind { NamedField(ident, _) => ident, @@ -4274,7 +4275,7 @@ impl VariantInfo { return VariantInfo { args: arg_tys, arg_names: Some(arg_names), - ctor_ty: ctor_ty, + ctor_ty: None, name: ast_variant.node.name.name, id: ast_util::local_def(ast_variant.node.id), disr_val: discriminant, diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index 94ae9561990..14725c581c8 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -8,29 +8,229 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![allow(non_camel_case_types)] - use middle::def; use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding, pat_is_const}; -use middle::subst; -use middle::subst::Subst; +use middle::subst::{Subst, Substs}; use middle::ty; -use middle::typeck::check::demand; -use middle::typeck::check::{check_expr, check_expr_has_type, FnCtxt}; -use middle::typeck::check::{instantiate_path, lookup_def}; -use middle::typeck::check::{structure_of, valid_range_bounds}; -use middle::typeck::infer; +use middle::typeck::check::{check_expr, check_expr_has_type, demand, FnCtxt}; +use middle::typeck::check::{instantiate_path, structurally_resolved_type, valid_range_bounds}; +use middle::typeck::infer::{mod, resolve}; use middle::typeck::require_same_types; -use util::ppaux; -use std::collections::{HashMap, HashSet}; +use std::cmp; +use std::collections::HashMap; +use std::collections::hashmap::{Occupied, Vacant}; use syntax::ast; use syntax::ast_util; -use syntax::parse::token; use syntax::codemap::{Span, Spanned}; +use syntax::parse::token; use syntax::print::pprust; use syntax::ptr::P; +pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) { + let fcx = pcx.fcx; + let tcx = pcx.fcx.ccx.tcx; + + match pat.node { + ast::PatWild(_) => { + fcx.write_ty(pat.id, expected); + } + ast::PatLit(ref lt) => { + check_expr(fcx, &**lt); + let expr_ty = fcx.expr_ty(&**lt); + fcx.write_ty(pat.id, expr_ty); + demand::suptype(fcx, pat.span, expected, expr_ty); + } + ast::PatRange(ref begin, ref end) => { + check_expr(fcx, &**begin); + check_expr(fcx, &**end); + + let lhs_ty = fcx.expr_ty(&**begin); + let rhs_ty = fcx.expr_ty(&**end); + if require_same_types( + tcx, Some(fcx.infcx()), false, pat.span, lhs_ty, rhs_ty, + || "mismatched types in range".to_string()) + && (ty::type_is_numeric(lhs_ty) || ty::type_is_char(rhs_ty)) { + match valid_range_bounds(fcx.ccx, &**begin, &**end) { + Some(false) => { + span_err!(tcx.sess, begin.span, E0030, + "lower range bound must be less than upper"); + }, + None => { + span_err!(tcx.sess, begin.span, E0031, + "mismatched types in range"); + }, + Some(true) => {} + } + } else { + span_err!(tcx.sess, begin.span, E0029, + "only char and numeric types are allowed in range"); + } + + fcx.write_ty(pat.id, lhs_ty); + demand::eqtype(fcx, pat.span, expected, lhs_ty); + } + ast::PatEnum(..) | ast::PatIdent(..) if pat_is_const(&tcx.def_map, pat) => { + let const_did = tcx.def_map.borrow().get_copy(&pat.id).def_id(); + let const_pty = ty::lookup_item_type(tcx, const_did); + fcx.write_ty(pat.id, const_pty.ty); + demand::eqtype(fcx, pat.span, expected, const_pty.ty); + } + ast::PatIdent(bm, ref path, ref sub) if pat_is_binding(&tcx.def_map, pat) => { + let typ = fcx.local_ty(pat.span, pat.id); + match bm { + ast::BindByRef(mutbl) => { + // if the binding is like + // ref x | ref const x | ref mut x + // then the type of x is &M T where M is the mutability + // and T is the expected type + let region_var = fcx.infcx().next_region_var(infer::PatternRegion(pat.span)); + let mt = ty::mt { ty: expected, mutbl: mutbl }; + let region_ty = ty::mk_rptr(tcx, region_var, mt); + demand::eqtype(fcx, pat.span, region_ty, typ); + } + // otherwise the type of x is the expected type T + ast::BindByValue(_) => { + demand::eqtype(fcx, pat.span, expected, typ); + } + } + fcx.write_ty(pat.id, typ); + + let canon_id = pcx.map[path.node]; + if canon_id != pat.id { + let ct = fcx.local_ty(pat.span, canon_id); + demand::eqtype(fcx, pat.span, ct, typ); + } + + if let Some(ref p) = *sub { + check_pat(pcx, &**p, expected); + } + } + ast::PatIdent(_, ref path, _) => { + let path = ast_util::ident_to_path(path.span, path.node); + check_pat_enum(pcx, pat, &path, &Some(vec![]), expected); + } + ast::PatEnum(ref path, ref subpats) => { + check_pat_enum(pcx, pat, path, subpats, expected); + } + ast::PatStruct(ref path, ref fields, etc) => { + check_pat_struct(pcx, pat, path, fields.as_slice(), etc, expected); + } + ast::PatTup(ref elements) => { + let element_tys = Vec::from_fn(elements.len(), |_| fcx.infcx().next_ty_var()); + let pat_ty = ty::mk_tup(tcx, element_tys.clone()); + fcx.write_ty(pat.id, pat_ty); + demand::eqtype(fcx, pat.span, expected, pat_ty); + for (element_pat, element_ty) in elements.iter().zip(element_tys.into_iter()) { + check_pat(pcx, &**element_pat, element_ty); + } + } + ast::PatBox(ref inner) => { + let inner_ty = fcx.infcx().next_ty_var(); + let uniq_ty = ty::mk_uniq(tcx, inner_ty); + + if check_dereferencable(pcx, pat.span, expected, &**inner) { + demand::suptype(fcx, pat.span, expected, uniq_ty); + fcx.write_ty(pat.id, uniq_ty); + check_pat(pcx, &**inner, inner_ty); + } else { + fcx.write_error(pat.id); + check_pat(pcx, &**inner, ty::mk_err()); + } + } + ast::PatRegion(ref inner) => { + let inner_ty = fcx.infcx().next_ty_var(); + + let mutbl = infer::resolve_type( + fcx.infcx(), Some(pat.span), + expected, resolve::try_resolve_tvar_shallow) + .ok() + .and_then(|t| ty::deref(t, true)) + .map_or(ast::MutImmutable, |mt| mt.mutbl); + + let mt = ty::mt { ty: inner_ty, mutbl: mutbl }; + let region = fcx.infcx().next_region_var(infer::PatternRegion(pat.span)); + let rptr_ty = ty::mk_rptr(tcx, region, mt); + + if check_dereferencable(pcx, pat.span, expected, &**inner) { + demand::suptype(fcx, pat.span, expected, rptr_ty); + fcx.write_ty(pat.id, rptr_ty); + check_pat(pcx, &**inner, inner_ty); + } else { + fcx.write_error(pat.id); + check_pat(pcx, &**inner, ty::mk_err()); + } + } + ast::PatVec(ref before, ref slice, ref after) => { + let expected_ty = structurally_resolved_type(fcx, pat.span, expected); + let inner_ty = fcx.infcx().next_ty_var(); + let pat_ty = match ty::get(expected_ty).sty { + ty::ty_vec(_, Some(size)) => ty::mk_vec(tcx, inner_ty, Some({ + let min_len = before.len() + after.len(); + match *slice { + Some(_) => cmp::max(min_len, size), + None => min_len + } + })), + _ => { + let region = fcx.infcx().next_region_var(infer::PatternRegion(pat.span)); + ty::mk_slice(tcx, region, ty::mt { + ty: inner_ty, + mutbl: ty::deref(expected_ty, true) + .map_or(ast::MutImmutable, |mt| mt.mutbl) + }) + } + }; + + fcx.write_ty(pat.id, pat_ty); + demand::suptype(fcx, pat.span, expected, pat_ty); + + for elt in before.iter() { + check_pat(pcx, &**elt, inner_ty); + } + if let Some(ref slice) = *slice { + let region = fcx.infcx().next_region_var(infer::PatternRegion(pat.span)); + let mutbl = ty::deref(expected_ty, true) + .map_or(ast::MutImmutable, |mt| mt.mutbl); + + let slice_ty = ty::mk_slice(tcx, region, ty::mt { + ty: inner_ty, + mutbl: mutbl + }); + check_pat(pcx, &**slice, slice_ty); + } + for elt in after.iter() { + check_pat(pcx, &**elt, inner_ty); + } + } + ast::PatMac(_) => tcx.sess.bug("unexpanded macro") + } +} + +pub fn check_dereferencable(pcx: &pat_ctxt, span: Span, expected: ty::t, + inner: &ast::Pat) -> bool { + let fcx = pcx.fcx; + let tcx = pcx.fcx.ccx.tcx; + match infer::resolve_type( + fcx.infcx(), Some(span), + expected, resolve::try_resolve_tvar_shallow) { + Ok(t) if pat_is_binding(&tcx.def_map, inner) => { + ty::deref(t, true).map_or(true, |mt| match ty::get(mt.ty).sty { + ty::ty_trait(_) => { + // This is "x = SomeTrait" being reduced from + // "let &x = &SomeTrait" or "let box x = Box", an error. + span_err!(tcx.sess, span, E0033, + "type `{}` cannot be dereferenced", + fcx.infcx().ty_to_string(t)); + false + } + _ => true + }) + } + _ => true + } +} + pub fn check_match(fcx: &FnCtxt, expr: &ast::Expr, discrim: &ast::Expr, @@ -47,10 +247,13 @@ pub fn check_match(fcx: &FnCtxt, fcx: fcx, map: pat_id_map(&tcx.def_map, &*arm.pats[0]), }; - - for p in arm.pats.iter() { check_pat(&mut pcx, &**p, discrim_ty);} + for p in arm.pats.iter() { + check_pat(&mut pcx, &**p, discrim_ty); + } } + // Now typecheck the blocks. + // // The result of the match is the common supertype of all the // arms. Start out the value as bottom, since it's the, well, // bottom the type lattice, and we'll be moving up the lattice as @@ -58,51 +261,26 @@ pub fn check_match(fcx: &FnCtxt, // on any empty type and is therefore unreachable; should the flow // of execution reach it, we will fail, so bottom is an appropriate // type in that case) - let mut result_ty = ty::mk_bot(); - - // Now typecheck the blocks. - let mut saw_err = ty::type_is_error(discrim_ty); - for arm in arms.iter() { - let mut guard_err = false; - let mut guard_bot = false; - match arm.guard { - Some(ref e) => { - check_expr_has_type(fcx, &**e, ty::mk_bool()); - let e_ty = fcx.expr_ty(&**e); - if ty::type_is_error(e_ty) { - guard_err = true; - } - else if ty::type_is_bot(e_ty) { - guard_bot = true; - } - }, - None => () - } + let result_ty = arms.iter().fold(ty::mk_bot(), |result_ty, arm| { check_expr(fcx, &*arm.body); let bty = fcx.node_ty(arm.body.id); - saw_err = saw_err || ty::type_is_error(bty); - if guard_err { - fcx.write_error(arm.body.id); - saw_err = true; - } - else if guard_bot { - fcx.write_bot(arm.body.id); + + if let Some(ref e) = arm.guard { + check_expr_has_type(fcx, &**e, ty::mk_bool()); } - result_ty = + if ty::type_is_error(result_ty) || ty::type_is_error(bty) { + ty::mk_err() + } else { infer::common_supertype( fcx.infcx(), infer::MatchExpressionArm(expr.span, arm.body.span), true, // result_ty is "expected" here result_ty, - bty); - } - - if saw_err { - result_ty = ty::mk_err(); - } else if ty::type_is_bot(discrim_ty) { - result_ty = ty::mk_bot(); - } + bty + ) + } + }); fcx.write_ty(expr.id, result_ty); } @@ -112,173 +290,120 @@ pub struct pat_ctxt<'a, 'tcx: 'a> { pub map: PatIdMap, } -pub fn check_pat_variant(pcx: &pat_ctxt, pat: &ast::Pat, path: &ast::Path, - subpats: &Option>>, expected: ty::t) { +pub fn check_pat_struct(pcx: &pat_ctxt, pat: &ast::Pat, + path: &ast::Path, fields: &[Spanned], + etc: bool, expected: ty::t) { + let fcx = pcx.fcx; + let tcx = pcx.fcx.ccx.tcx; + + let def = tcx.def_map.borrow().get_copy(&pat.id); + let def_type = ty::lookup_item_type(tcx, def.def_id()); + let (enum_def_id, variant_def_id) = match ty::get(def_type.ty).sty { + ty::ty_struct(struct_def_id, _) => + (struct_def_id, struct_def_id), + ty::ty_enum(enum_def_id, _) if def == def::DefVariant(enum_def_id, def.def_id(), true) => + (enum_def_id, def.def_id()), + _ => { + let name = pprust::path_to_string(path); + span_err!(tcx.sess, pat.span, E0163, + "`{}` does not name a struct or a struct variant", name); + fcx.write_error(pat.id); + + for field in fields.iter() { + check_pat(pcx, &*field.node.pat, ty::mk_err()); + } + return; + } + }; + + instantiate_path(pcx.fcx, path, ty::lookup_item_type(tcx, enum_def_id), + def, pat.span, pat.id); + + let pat_ty = fcx.node_ty(pat.id); + demand::eqtype(fcx, pat.span, expected, pat_ty); + + let item_substs = fcx + .item_substs() + .find(&pat.id) + .map(|substs| substs.substs.clone()) + .unwrap_or_else(|| Substs::empty()); + + let struct_fields = ty::struct_fields(tcx, variant_def_id, &item_substs); + check_struct_pat_fields(pcx, pat.span, fields, struct_fields.as_slice(), + variant_def_id, etc); +} + +pub fn check_pat_enum(pcx: &pat_ctxt, pat: &ast::Pat, + path: &ast::Path, subpats: &Option>>, + expected: ty::t) { // Typecheck the path. let fcx = pcx.fcx; let tcx = pcx.fcx.ccx.tcx; - let arg_types: Vec ; - let kind_name; + let def = tcx.def_map.borrow().get_copy(&pat.id); + let enum_def = def.variant_def_ids() + .map_or_else(|| def.def_id(), |(enum_def, _)| enum_def); - // structure_of requires type variables to be resolved. - // So when we pass in , it's an error if it - // contains type variables. + let ctor_pty = ty::lookup_item_type(tcx, enum_def); + let path_ty = if ty::is_fn_ty(ctor_pty.ty) { + ty::Polytype { ty: ty::ty_fn_ret(ctor_pty.ty), ..ctor_pty } + } else { + ctor_pty + }; + instantiate_path(pcx.fcx, path, path_ty, def, pat.span, pat.id); - // Check to see whether this is an enum or a struct. - match *structure_of(pcx.fcx, pat.span, expected) { - ty::ty_enum(expected_def_id, ref expected_substs) => { - // Lookup the enum and variant def ids: - let v_def = lookup_def(pcx.fcx, pat.span, pat.id); - match v_def.variant_def_ids() { - Some((enm, var)) => { - // Assign the pattern the type of the *enum*, not the variant. - let enum_pty = ty::lookup_item_type(tcx, enm); - instantiate_path(pcx.fcx, - path, - enum_pty, - v_def, - pat.span, - pat.id); + let pat_ty = fcx.node_ty(pat.id); + demand::eqtype(fcx, pat.span, expected, pat_ty); - // check that the type of the value being matched is a subtype - // of the type of the pattern: - let pat_ty = fcx.node_ty(pat.id); - demand::subtype(fcx, pat.span, expected, pat_ty); - - // Get the expected types of the arguments. - arg_types = { - let vinfo = - ty::enum_variant_with_id(tcx, enm, var); - if enm == expected_def_id { - vinfo.args.iter() - .map(|t| t.subst(tcx, expected_substs)) - .collect() - } else { - vinfo.args.iter() - .map(|_| ty::mk_err()) - .collect() - } - }; - - kind_name = "variant"; - } - None => { - // See [Note-Type-error-reporting] in middle/typeck/infer/mod.rs - fcx.infcx().type_error_message_str_with_expected(pat.span, - |expected, actual| { - expected.map_or("".to_string(), |e| { - format!("mismatched types: expected `{}`, found {}", - e, actual) - })}, - Some(expected), - "a structure pattern".to_string(), - None); - fcx.write_error(pat.id); - kind_name = "[error]"; - arg_types = subpats.clone() - .unwrap_or_default() - .into_iter() - .map(|_| ty::mk_err()) - .collect(); - } - } + let real_path_ty = fcx.node_ty(pat.id); + let (arg_tys, kind_name) = match ty::get(real_path_ty).sty { + ty::ty_enum(enum_def_id, ref expected_substs) => { + let variant = ty::enum_variant_with_id(tcx, enum_def_id, def.def_id()); + (variant.args.iter().map(|t| t.subst(tcx, expected_substs)).collect::>(), + "variant") } ty::ty_struct(struct_def_id, ref expected_substs) => { - // Lookup the struct ctor def id - let s_def = lookup_def(pcx.fcx, pat.span, pat.id); - let s_def_id = s_def.def_id(); - - // Assign the pattern the type of the struct. - let ctor_pty = ty::lookup_item_type(tcx, s_def_id); - let struct_pty = if ty::is_fn_ty(ctor_pty.ty) { - ty::Polytype {ty: ty::ty_fn_ret(ctor_pty.ty), - ..ctor_pty} - } else { - ctor_pty - }; - instantiate_path(pcx.fcx, - path, - struct_pty, - s_def, - pat.span, - pat.id); - - // Check that the type of the value being matched is a subtype of - // the type of the pattern. - let pat_ty = fcx.node_ty(pat.id); - demand::subtype(fcx, pat.span, expected, pat_ty); - - // Get the expected types of the arguments. - let class_fields = ty::struct_fields( - tcx, struct_def_id, expected_substs); - arg_types = class_fields.iter().map(|field| field.mt.ty).collect(); - - kind_name = "structure"; + let struct_fields = ty::struct_fields(tcx, struct_def_id, expected_substs); + (struct_fields.iter().map(|field| field.mt.ty).collect::>(), + "struct") } _ => { - // See [Note-Type-error-reporting] in middle/typeck/infer/mod.rs - fcx.infcx().type_error_message_str_with_expected(pat.span, - |expected, actual| { - expected.map_or("".to_string(), - |e| { - format!("mismatched types: expected `{}`, found {}", - e, actual) - }) - }, - Some(expected), - "an enum or structure pattern".to_string(), - None); + let name = pprust::path_to_string(path); + span_err!(tcx.sess, pat.span, E0164, + "`{}` does not name a variant or a tuple struct", name); fcx.write_error(pat.id); - kind_name = "[error]"; - arg_types = subpats.clone() - .unwrap_or_default() - .iter() - .map(|_| ty::mk_err()) - .collect(); - } - } - let arg_len = arg_types.len(); - - // Count the number of subpatterns. - let subpats_len; - match *subpats { - None => subpats_len = arg_len, - Some(ref subpats) => subpats_len = subpats.len() - } - - let mut error_happened = false; - - if arg_len > 0 { - // N-ary variant. - if arg_len != subpats_len { - span_err!(tcx.sess, pat.span, E0023, - "this pattern has {} field{}, but the corresponding {} has {} field{}", - subpats_len, if subpats_len == 1 {""} else {"s"}, - kind_name, arg_len, if arg_len == 1 {""} else {"s"}); - error_happened = true; - } - - if !error_happened { - for pats in subpats.iter() { - for (subpat, arg_ty) in pats.iter().zip(arg_types.iter()) { - check_pat(pcx, &**subpat, *arg_ty); + if let Some(ref subpats) = *subpats { + for pat in subpats.iter() { + check_pat(pcx, &**pat, ty::mk_err()); } } + return; } - } else if subpats_len > 0 { - span_err!(tcx.sess, pat.span, E0024, - "this pattern has {} field{}, but the corresponding {} has no fields", - subpats_len, if subpats_len == 1 {""} else {"s"}, - kind_name); - error_happened = true; - } + }; - if error_happened { - for pats in subpats.iter() { - for pat in pats.iter() { + if let Some(ref subpats) = *subpats { + if subpats.len() == arg_tys.len() { + for (subpat, arg_ty) in subpats.iter().zip(arg_tys.iter()) { + check_pat(pcx, &**subpat, *arg_ty); + } + } else if arg_tys.len() == 0 { + span_err!(tcx.sess, pat.span, E0024, + "this pattern has {} field{}, but the corresponding {} has no fields", + subpats.len(), if subpats.len() == 1 {""} else {"s"}, kind_name); + + for pat in subpats.iter() { + check_pat(pcx, &**pat, ty::mk_err()); + } + } else { + span_err!(tcx.sess, pat.span, E0023, + "this pattern has {} field{}, but the corresponding {} has {} field{}", + subpats.len(), if subpats.len() == 1 {""} else {"s"}, + kind_name, + arg_tys.len(), if arg_tys.len() == 1 {""} else {"s"}); + + for pat in subpats.iter() { check_pat(pcx, &**pat, ty::mk_err()); } } @@ -287,515 +412,62 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: &ast::Pat, path: &ast::Path, /// `path` is the AST path item naming the type of this struct. /// `fields` is the field patterns of the struct pattern. -/// `class_fields` describes the type of each field of the struct. -/// `class_id` is the ID of the struct. -/// `substitutions` are the type substitutions applied to this struct type -/// (e.g. K,V in HashMap). +/// `struct_fields` describes the type of each field of the struct. +/// `struct_id` is the ID of the struct. /// `etc` is true if the pattern said '...' and false otherwise. pub fn check_struct_pat_fields(pcx: &pat_ctxt, span: Span, fields: &[Spanned], - class_fields: Vec, - class_id: ast::DefId, - substitutions: &subst::Substs, + struct_fields: &[ty::field], + struct_id: ast::DefId, etc: bool) { let tcx = pcx.fcx.ccx.tcx; - // Index the class fields. The second argument in the tuple is whether the - // field has been bound yet or not. - let mut field_map = HashMap::new(); - for (i, class_field) in class_fields.iter().enumerate() { - field_map.insert(class_field.name, (i, false)); - } + // Index the struct fields' types. + let field_type_map = struct_fields + .iter() + .map(|field| (field.name, field.mt.ty)) + .collect::>(); + + // Keep track of which fields have already appeared in the pattern. + let mut used_fields = HashMap::new(); // Typecheck each field. - let mut found_fields = HashSet::new(); for &Spanned { node: ref field, span } in fields.iter() { - match field_map.find_mut(&field.ident.name) { - Some(&(_, true)) => { - // Check the pattern anyway, so that attempts to look - // up its type won't fail - check_pat(pcx, &*field.pat, ty::mk_err()); + let field_type = match used_fields.entry(field.ident.name) { + Occupied(occupied) => { span_err!(tcx.sess, span, E0025, - "field `{}` bound twice in pattern", + "field `{}` bound multiple times in the pattern", token::get_ident(field.ident)); - } - Some(&(index, ref mut used)) => { - *used = true; - let class_field = class_fields[index].clone(); - let field_type = ty::lookup_field_type(tcx, - class_id, - class_field.id, - substitutions); - check_pat(pcx, &*field.pat, field_type); - found_fields.insert(index); - } - None => { - // Check the pattern anyway, so that attempts to look - // up its type won't fail - check_pat(pcx, &*field.pat, ty::mk_err()); - span_err!(tcx.sess, span, E0026, - "struct `{}` does not have a field named `{}`", - ty::item_path_str(tcx, class_id), + span_note!(tcx.sess, *occupied.get(), + "field `{}` previously bound here", token::get_ident(field.ident)); + ty::mk_err() } - } + Vacant(vacant) => { + vacant.set(span); + field_type_map.find_copy(&field.ident.name) + .unwrap_or_else(|| { + span_err!(tcx.sess, span, E0026, + "struct `{}` does not have a field named `{}`", + ty::item_path_str(tcx, struct_id), + token::get_ident(field.ident)); + ty::mk_err() + }) + } + }; + + check_pat(pcx, &*field.pat, field_type); } // Report an error if not all the fields were specified. if !etc { - for (i, field) in class_fields.iter().enumerate() { - if found_fields.contains(&i) { - continue; - } + for field in struct_fields + .iter() + .filter(|field| !used_fields.contains_key(&field.name)) { span_err!(tcx.sess, span, E0027, "pattern does not mention field `{}`", token::get_name(field.name)); } } } - -pub fn check_struct_pat(pcx: &pat_ctxt, span: Span, - fields: &[Spanned], etc: bool, - struct_id: ast::DefId, - substitutions: &subst::Substs) { - let _fcx = pcx.fcx; - let tcx = pcx.fcx.ccx.tcx; - - let class_fields = ty::lookup_struct_fields(tcx, struct_id); - - check_struct_pat_fields(pcx, span, fields, class_fields, struct_id, - substitutions, etc); -} - -pub fn check_struct_like_enum_variant_pat(pcx: &pat_ctxt, - pat_id: ast::NodeId, - span: Span, - expected: ty::t, - path: &ast::Path, - fields: &[Spanned], - etc: bool, - enum_id: ast::DefId, - substitutions: &subst::Substs) { - let fcx = pcx.fcx; - let tcx = pcx.fcx.ccx.tcx; - - // Find the variant that was specified. - match tcx.def_map.borrow().find(&pat_id) { - Some(&def::DefVariant(found_enum_id, variant_id, true)) - if found_enum_id == enum_id => { - // Get the struct fields from this struct-like enum variant. - let struct_fields = ty::lookup_struct_fields(tcx, variant_id); - check_struct_pat_fields(pcx, span, fields, struct_fields, - variant_id, substitutions, etc); - fcx.write_ty(pat_id, expected); - } - Some(&def::DefVariant(_, _, false)) => { - let name = pprust::path_to_string(path); - span_err!(tcx.sess, span, E0163, - "`{}` does not name a struct variant", name); - fcx.write_error(pat_id); - } - Some(&def::DefVariant(_, _, true)) => { - let name = pprust::path_to_string(path); - span_err!(tcx.sess, span, E0164, - "`{}` does not name a variant of the type being matched against", name); - fcx.write_error(pat_id); - } - Some(&def::DefStruct(..)) | - Some(&def::DefTy(..)) => { - let name = pprust::path_to_string(path); - span_err!(tcx.sess, span, E0028, - "`{}` does not name a variant", name); - fcx.write_error(pat_id); - } - _ => { - tcx.sess.span_bug(span, "resolve didn't write in variant"); - } - } - - if ty::type_is_error(fcx.node_ty(pat_id)) { - for field in fields.iter() { - check_pat(pcx, &*field.node.pat, ty::mk_err()); - } - } -} - -// Pattern checking is top-down rather than bottom-up so that bindings get -// their types immediately. -pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) { - let fcx = pcx.fcx; - let tcx = pcx.fcx.ccx.tcx; - - match pat.node { - ast::PatWild(_) => { - fcx.write_ty(pat.id, expected); - } - ast::PatLit(ref lt) => { - check_expr_has_type(fcx, &**lt, expected); - fcx.write_ty(pat.id, fcx.expr_ty(&**lt)); - } - ast::PatRange(ref begin, ref end) => { - check_expr_has_type(fcx, &**begin, expected); - check_expr_has_type(fcx, &**end, expected); - let b_ty = - fcx.infcx().resolve_type_vars_if_possible(fcx.expr_ty(&**begin)); - let e_ty = - fcx.infcx().resolve_type_vars_if_possible(fcx.expr_ty(&**end)); - debug!("pat_range beginning type: {}", b_ty); - debug!("pat_range ending type: {}", e_ty); - if !require_same_types( - tcx, Some(fcx.infcx()), false, pat.span, b_ty, e_ty, - || "mismatched types in range".to_string()) - { - // no-op - } else if !ty::type_is_numeric(b_ty) && !ty::type_is_char(b_ty) { - span_err!(tcx.sess, begin.span, E0029, - "only char and numeric types are allowed in range"); - } else { - match valid_range_bounds(fcx.ccx, &**begin, &**end) { - Some(false) => { - span_err!(tcx.sess, begin.span, E0030, - "lower range bound must be less than upper"); - }, - None => { - span_err!(tcx.sess, begin.span, E0031, - "mismatched types in range"); - }, - _ => { }, - } - } - fcx.write_ty(pat.id, b_ty); - } - ast::PatEnum(..) | - ast::PatIdent(..) if pat_is_const(&tcx.def_map, pat) => { - let const_did = tcx.def_map.borrow().get_copy(&pat.id).def_id(); - let const_pty = ty::lookup_item_type(tcx, const_did); - demand::suptype(fcx, pat.span, expected, const_pty.ty); - fcx.write_ty(pat.id, const_pty.ty); - } - ast::PatIdent(bm, ref path1, ref sub) if pat_is_binding(&tcx.def_map, pat) => { - let typ = fcx.local_ty(pat.span, pat.id); - - match bm { - ast::BindByRef(mutbl) => { - // if the binding is like - // ref x | ref const x | ref mut x - // then the type of x is &M T where M is the mutability - // and T is the expected type - let region_var = - fcx.infcx().next_region_var( - infer::PatternRegion(pat.span)); - let mt = ty::mt {ty: expected, mutbl: mutbl}; - let region_ty = ty::mk_rptr(tcx, region_var, mt); - demand::eqtype(fcx, pat.span, region_ty, typ); - } - // otherwise the type of x is the expected type T - ast::BindByValue(_) => { - demand::eqtype(fcx, pat.span, expected, typ); - } - } - - let canon_id = pcx.map[path1.node]; - if canon_id != pat.id { - let ct = fcx.local_ty(pat.span, canon_id); - demand::eqtype(fcx, pat.span, ct, typ); - } - fcx.write_ty(pat.id, typ); - - debug!("(checking match) writing type {} (expected {}) for pat id {}", - ppaux::ty_to_string(tcx, typ), - ppaux::ty_to_string(tcx, expected), - pat.id); - - match *sub { - Some(ref p) => check_pat(pcx, &**p, expected), - _ => () - } - } - // it's not a binding, it's an enum in disguise: - ast::PatIdent(_, ref path1, _) => { - let path = ast_util::ident_to_path(path1.span,path1.node); - check_pat_variant(pcx, pat, &path, &Some(Vec::new()), expected); - } - ast::PatEnum(ref path, ref subpats) => { - check_pat_variant(pcx, pat, path, subpats, expected); - } - ast::PatStruct(ref path, ref fields, etc) => { - // Grab the class data that we care about. - let structure = structure_of(fcx, pat.span, expected); - let mut error_happened = false; - match *structure { - ty::ty_struct(cid, ref substs) => { - // Verify that the pattern named the right structure. - let item_did = (*tcx.def_map.borrow())[pat.id].def_id(); - match ty::ty_to_def_id(ty::lookup_item_type(tcx, item_did).ty) { - Some(struct_did) if struct_did != cid => { - span_err!(tcx.sess, path.span, E0032, - "`{}` does not name the structure `{}`", - pprust::path_to_string(path), - fcx.infcx().ty_to_string(expected)); - }, - Some(_) => {}, - None => { - tcx.sess.span_bug( - path.span, - format!("This shouldn't happen: failed to lookup structure. \ - item_did = {}", item_did).as_slice()) - }, - } - - check_struct_pat(pcx, pat.span, fields.as_slice(), etc, cid, substs); - } - ty::ty_enum(eid, ref substs) => { - check_struct_like_enum_variant_pat(pcx, - pat.id, - pat.span, - expected, - path, - fields.as_slice(), - etc, - eid, - substs); - } - _ => { - // See [Note-Type-error-reporting] in middle/typeck/infer/mod.rs - fcx.infcx().type_error_message_str_with_expected(pat.span, - |expected, actual| { - expected.map_or("".to_string(), - |e| { - format!("mismatched types: expected \ - `{}`, found {}", e, actual) - })}, - Some(expected), - "a structure pattern".to_string(), - None); - match tcx.def_map.borrow().find(&pat.id) { - Some(def) => { - let struct_ty = fcx.instantiate_item_type(pat.span, def.def_id()); - check_struct_pat(pcx, pat.span, fields.as_slice(), - etc, def.def_id(), &struct_ty.substs); - } - None => { - tcx.sess.span_bug(pat.span, - "whoops, looks like resolve didn't \ - write a def in here") - } - } - error_happened = true; - } - } - - // Finally, write in the type. - if error_happened { - fcx.write_error(pat.id); - } else { - fcx.write_ty(pat.id, expected); - } - } - ast::PatTup(ref elts) => { - let s = structure_of(fcx, pat.span, expected); - let e_count = elts.len(); - match *s { - ty::ty_tup(ref ex_elts) if e_count == ex_elts.len() => { - for (i, elt) in elts.iter().enumerate() { - check_pat(pcx, &**elt, ex_elts[i]); - } - fcx.write_ty(pat.id, expected); - } - _ => { - for elt in elts.iter() { - check_pat(pcx, &**elt, ty::mk_err()); - } - // use terr_tuple_size if both types are tuples - let type_error = match *s { - ty::ty_tup(ref ex_elts) => { - ty::terr_tuple_size(ty::expected_found { - expected: ex_elts.len(), - found: e_count - }) - } - _ => ty::terr_mismatch - }; - // See [Note-Type-error-reporting] in middle/typeck/infer/mod.rs - fcx.infcx().type_error_message_str_with_expected(pat.span, - |expected, - actual| { - expected.map_or("".to_string(), |e| { - format!("mismatched types: expected `{}`, \ - found {}", e, actual) - } - )}, - Some(expected), - "tuple".to_string(), - Some(&type_error)); - fcx.write_error(pat.id); - } - } - } - ast::PatBox(ref inner) => { - check_pointer_pat(pcx, Send, &**inner, pat.id, pat.span, expected); - } - ast::PatRegion(ref inner) => { - check_pointer_pat(pcx, Borrowed, &**inner, pat.id, pat.span, expected); - } - ast::PatVec(ref before, ref slice, ref after) => { - let default_region_var = - fcx.infcx().next_region_var( - infer::PatternRegion(pat.span)); - - let check_err = |found: String| { - for elt in before.iter() { - check_pat(pcx, &**elt, ty::mk_err()); - } - for elt in slice.iter() { - check_pat(pcx, &**elt, ty::mk_err()); - } - for elt in after.iter() { - check_pat(pcx, &**elt, ty::mk_err()); - } - // See [Note-Type-error-reporting] in middle/typeck/infer/mod.rs - fcx.infcx().type_error_message_str_with_expected( - pat.span, - |expected, actual| { - expected.map_or("".to_string(), - |e| { - format!("mismatched types: expected `{}`, found {}", - e, actual) - }) - }, - Some(expected), - found, - None); - fcx.write_error(pat.id); - }; - - let (elt_type, region_var, mutbl, fixed) = match *structure_of(fcx, - pat.span, - expected) { - ty::ty_vec(ty, Some(fixed)) => - (ty, default_region_var, ast::MutImmutable, Some(fixed)), - ty::ty_uniq(t) => match ty::get(t).sty { - ty::ty_vec(ty, None) => { - fcx.type_error_message(pat.span, - |_| { - "unique vector patterns are no \ - longer supported".to_string() - }, - expected, - None); - (ty, default_region_var, ast::MutImmutable, None) - } - _ => { - check_err("an array pattern".to_string()); - return; - } - }, - ty::ty_rptr(r, mt) => match ty::get(mt.ty).sty { - ty::ty_vec(ty, None) => (ty, r, mt.mutbl, None), - _ => { - check_err("an array pattern".to_string()); - return; - } - }, - _ => { - check_err("an array pattern".to_string()); - return; - } - }; - - let min_len = before.len() + after.len(); - fixed.and_then(|count| match *slice { - Some(_) if count < min_len => - Some(format!("a fixed array pattern of size at least {}", min_len)), - - None if count != min_len => - Some(format!("a fixed array pattern of size {}", min_len)), - - _ => None - }).map(check_err); - - for elt in before.iter() { - check_pat(pcx, &**elt, elt_type); - } - match *slice { - Some(ref slice_pat) => { - let slice_ty = ty::mk_slice(tcx, - region_var, - ty::mt {ty: elt_type, mutbl: mutbl}); - check_pat(pcx, &**slice_pat, slice_ty); - } - None => () - } - for elt in after.iter() { - check_pat(pcx, &**elt, elt_type); - } - fcx.write_ty(pat.id, expected); - } - - ast::PatMac(_) => tcx.sess.bug("unexpanded macro"), - } -} - -// Helper function to check gc, box and & patterns -fn check_pointer_pat(pcx: &pat_ctxt, - pointer_kind: PointerKind, - inner: &ast::Pat, - pat_id: ast::NodeId, - span: Span, - expected: ty::t) { - let fcx = pcx.fcx; - let tcx = fcx.ccx.tcx; - let check_inner: |ty::t| = |e_inner| { - match ty::get(e_inner).sty { - ty::ty_trait(_) if pat_is_binding(&tcx.def_map, inner) => { - // This is "x = SomeTrait" being reduced from - // "let &x = &SomeTrait" or "let box x = Box", an error. - check_pat(pcx, inner, ty::mk_err()); - span_err!(tcx.sess, span, E0033, - "type `{}` cannot be dereferenced", - fcx.infcx().ty_to_string(expected)); - fcx.write_error(pat_id); - } - _ => { - check_pat(pcx, inner, e_inner); - fcx.write_ty(pat_id, expected); - } - } - }; - - match *structure_of(fcx, span, expected) { - ty::ty_uniq(e_inner) if pointer_kind == Send => { - check_inner(e_inner); - } - ty::ty_rptr(_, e_inner) if pointer_kind == Borrowed => { - check_inner(e_inner.ty); - } - _ => { - check_pat(pcx, inner, ty::mk_err()); - // See [Note-Type-error-reporting] in middle/typeck/infer/mod.rs - fcx.infcx().type_error_message_str_with_expected( - span, - |expected, actual| { - expected.map_or("".to_string(), |e| { - format!("mismatched types: expected `{}`, found {}", - e, actual) - }) - }, - Some(expected), - format!("{} pattern", match pointer_kind { - Send => "a box", - Borrowed => "an `&`-pointer", - }), - None); - fcx.write_error(pat_id); - } - } -} - -#[deriving(PartialEq)] -pub enum PointerKind { - Send, - Borrowed, -} - diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 2755c4e2f78..feab2bacbb8 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -117,7 +117,7 @@ use util::ppaux; use util::ppaux::{UserString, Repr}; use util::nodemap::{DefIdMap, FnvHashMap, NodeMap}; -use std::cell::{Cell, RefCell}; +use std::cell::{Cell, Ref, RefCell}; use std::collections::HashMap; use std::collections::hashmap::{Occupied, Vacant}; use std::mem::replace; @@ -1815,6 +1815,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + pub fn item_substs<'a>(&'a self) -> Ref<'a, NodeMap> { + self.inh.item_substs.borrow() + } + pub fn opt_node_ty_substs(&self, id: ast::NodeId, f: |&ty::ItemSubsts|) { diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index a3339f217ce..1f4b80b360b 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -236,10 +236,7 @@ pub fn get_enum_variant_types(ccx: &CrateCtxt, }; convert_struct(ccx, &**struct_def, pty, variant.node.id); - - let input_tys: Vec<_> = struct_def.fields.iter().map( - |f| ty::node_id_to_type(ccx.tcx, f.node.id)).collect(); - ty::mk_ctor_fn(tcx, scope, input_tys.as_slice(), enum_ty) + enum_ty } }; diff --git a/src/test/run-pass/issue-8783.rs b/src/test/run-pass/issue-8783.rs new file mode 100644 index 00000000000..d59c0ad52e9 --- /dev/null +++ b/src/test/run-pass/issue-8783.rs @@ -0,0 +1,30 @@ +// 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 std::default::Default; + +struct X { pub x: uint } +impl Default for X { + fn default() -> X { + X { x: 42u } + } +} + +struct Y { pub y: T } +impl Default for Y { + fn default() -> Y { + Y { y: Default::default() } + } +} + +fn main() { + let X { x: _ } = Default::default(); + let Y { y: X { x } } = Default::default(); +}