From 106f9976ab1baec06af41f7009f539cd8371a840 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 2 Nov 2012 09:56:09 -0700 Subject: [PATCH] rustc: Implement typechecking, exhaustiveness checking, and borrow checking for pattern matching of tuple structs. r=nmatsakis Conflicts: src/rustc/middle/typeck/check/alt.rs --- src/rustc/middle/borrowck.rs | 4 +- src/rustc/middle/borrowck/loan.rs | 3 +- src/rustc/middle/borrowck/preserve.rs | 3 +- src/rustc/middle/check_alt.rs | 9 ++ src/rustc/middle/mem_categorization.rs | 48 ++++-- src/rustc/middle/resolve.rs | 5 +- src/rustc/middle/ty.rs | 23 +++ src/rustc/middle/typeck/check/alt.rs | 138 +++++++++++------- .../tuple-struct-nonexhaustive.rs | 11 ++ 9 files changed, 175 insertions(+), 69 deletions(-) create mode 100644 src/test/compile-fail/tuple-struct-nonexhaustive.rs diff --git a/src/rustc/middle/borrowck.rs b/src/rustc/middle/borrowck.rs index 1b1ec022d3f..ccc2ca9888a 100644 --- a/src/rustc/middle/borrowck.rs +++ b/src/rustc/middle/borrowck.rs @@ -594,7 +594,7 @@ impl borrowck_ctxt { // mutable structure. fn inherent_mutability(ck: comp_kind) -> mutability { match ck { - comp_tuple | comp_variant(_) => m_imm, - comp_field(_, m) | comp_index(_, m) => m + comp_tuple | comp_anon_field | comp_variant(_) => m_imm, + comp_field(_, m) | comp_index(_, m) => m } } diff --git a/src/rustc/middle/borrowck/loan.rs b/src/rustc/middle/borrowck/loan.rs index 71414e6e724..b3023497220 100644 --- a/src/rustc/middle/borrowck/loan.rs +++ b/src/rustc/middle/borrowck/loan.rs @@ -116,7 +116,8 @@ impl LoanContext { // overwritten and the component along with it. self.loan_stable_comp(cmt, cmt_base, req_mutbl, m) } - cat_comp(cmt_base, comp_tuple) => { + cat_comp(cmt_base, comp_tuple) | + cat_comp(cmt_base, comp_anon_field) => { // As above. self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm) } diff --git a/src/rustc/middle/borrowck/preserve.rs b/src/rustc/middle/borrowck/preserve.rs index 556ea7867cf..c27ceca055f 100644 --- a/src/rustc/middle/borrowck/preserve.rs +++ b/src/rustc/middle/borrowck/preserve.rs @@ -119,7 +119,8 @@ priv impl &preserve_ctxt { } cat_comp(cmt_base, comp_field(*)) | cat_comp(cmt_base, comp_index(*)) | - cat_comp(cmt_base, comp_tuple) => { + cat_comp(cmt_base, comp_tuple) | + cat_comp(cmt_base, comp_anon_field) => { // Most embedded components: if the base is stable, the // type never changes. self.preserve(cmt_base) diff --git a/src/rustc/middle/check_alt.rs b/src/rustc/middle/check_alt.rs index 7ed7829cf34..5b35ed98a83 100644 --- a/src/rustc/middle/check_alt.rs +++ b/src/rustc/middle/check_alt.rs @@ -355,6 +355,15 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint, Some(vec::append(args, vec::tail(r))) } def_variant(_, _) => None, + def_class(*) => { + // XXX: Is this right? --pcw + let new_args; + match args { + Some(args) => new_args = args, + None => new_args = vec::from_elem(arity, wild()) + } + Some(vec::append(new_args, vec::tail(r))) + } _ => None } } diff --git a/src/rustc/middle/mem_categorization.rs b/src/rustc/middle/mem_categorization.rs index 83eddc1c21b..55bf41b573e 100644 --- a/src/rustc/middle/mem_categorization.rs +++ b/src/rustc/middle/mem_categorization.rs @@ -162,6 +162,8 @@ impl ptr_kind : cmp::Eq { // structure accessible without a dereference": enum comp_kind { comp_tuple, // elt in a tuple + comp_anon_field, // anonymous field (in e.g. + // struct Foo(int, int); comp_variant(ast::def_id), // internals to a variant of given enum comp_field(ast::ident, // name of field ast::mutability), // declared mutability of field @@ -178,6 +180,12 @@ impl comp_kind : cmp::Eq { _ => false } } + comp_anon_field => { + match (*other) { + comp_anon_field => true, + _ => false + } + } comp_variant(e0a) => { match (*other) { comp_variant(e0b) => e0a == e0b, @@ -775,6 +783,14 @@ impl &mem_categorization_ctxt { ty: self.tcx.ty(elt)} } + fn cat_anon_struct_field(elt: N, cmt: cmt) -> cmt { + @{id: elt.id(), span: elt.span(), + cat: cat_comp(cmt, comp_anon_field), + lp: cmt.lp.map(|l| @lp_comp(*l, comp_anon_field)), + mutbl: cmt.mutbl, // imm iff in an immutable context + ty: self.tcx.ty(elt)} + } + fn cat_method_ref(expr: @ast::expr, expr_ty: ty::t) -> cmt { @{id:expr.id, span:expr.span, cat:cat_special(sk_method), lp:None, @@ -834,16 +850,26 @@ impl &mem_categorization_ctxt { // variant(*) } ast::pat_enum(_, Some(subpats)) => { - // variant(x, y, z) - let enum_did = match self.tcx.def_map.find(pat.id) { - Some(ast::def_variant(enum_did, _)) => enum_did, - e => tcx.sess.span_bug(pat.span, - fmt!("resolved to %?, not variant", e)) - }; - - for subpats.each |subpat| { - let subcmt = self.cat_variant(*subpat, enum_did, cmt); - self.cat_pattern(subcmt, *subpat, op); + match self.tcx.def_map.find(pat.id) { + Some(ast::def_variant(enum_did, _)) => { + // variant(x, y, z) + for subpats.each |subpat| { + let subcmt = self.cat_variant(*subpat, enum_did, cmt); + self.cat_pattern(subcmt, *subpat, op); + } + } + Some(ast::def_class(*)) => { + for subpats.each |subpat| { + let cmt_field = self.cat_anon_struct_field(*subpat, + cmt); + self.cat_pattern(cmt_field, *subpat, op); + } + } + _ => { + self.tcx.sess.span_bug( + pat.span, + ~"enum pattern didn't resolve to enum or struct"); + } } } @@ -934,6 +960,7 @@ impl &mem_categorization_ctxt { comp_field(fld, _) => self.tcx.sess.str_of(fld), comp_index(*) => ~"[]", comp_tuple => ~"()", + comp_anon_field => ~"", comp_variant(_) => ~"" } } @@ -986,6 +1013,7 @@ impl &mem_categorization_ctxt { } cat_comp(_, comp_field(*)) => mut_str + ~" field", cat_comp(_, comp_tuple) => ~"tuple content", + cat_comp(_, comp_anon_field) => ~"anonymous field", cat_comp(_, comp_variant(_)) => ~"enum content", cat_comp(_, comp_index(t, _)) => { match ty::get(t).sty { diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs index 97551162de5..a979c0d7b57 100644 --- a/src/rustc/middle/resolve.rs +++ b/src/rustc/middle/resolve.rs @@ -4286,9 +4286,10 @@ impl Resolver { } pat_ident(_, path, _) | pat_enum(path, _) => { - // These two must be enum variants. + // These two must be enum variants or structs. match self.resolve_path(path, ValueNS, false, visitor) { - Some(def @ def_variant(*)) => { + Some(def @ def_variant(*)) | + Some(def @ def_class(*)) => { self.record_def(pattern.id, def); } Some(_) => { diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 1402d1c9324..65f77cefd02 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -3554,6 +3554,29 @@ fn ty_to_def_id(ty: t) -> Option { } } +/// Returns the def ID of the constructor for the given tuple-like struct, or +/// None if the struct is not tuple-like. Fails if the given def ID does not +/// refer to a struct at all. +fn struct_ctor_id(cx: ctxt, struct_did: ast::def_id) -> Option { + if struct_did.crate != ast::local_crate { + // XXX: Cross-crate functionality. + cx.sess.unimpl(~"constructor ID of cross-crate tuple structs"); + } + + match cx.items.find(struct_did.node) { + Some(ast_map::node_item(item, _)) => { + match item.node { + ast::item_class(struct_def, _) => { + struct_def.ctor_id.map(|ctor_id| + ast_util::local_def(*ctor_id)) + } + _ => cx.sess.bug(~"called struct_ctor_id on non-struct") + } + } + _ => cx.sess.bug(~"called struct_ctor_id on non-struct") + } +} + // Enum information type variant_info = @{args: ~[t], ctor_ty: t, name: ast::ident, id: ast::def_id, disr_val: int}; diff --git a/src/rustc/middle/typeck/check/alt.rs b/src/rustc/middle/typeck/check/alt.rs index dc1b14e4beb..5a62ad0461d 100644 --- a/src/rustc/middle/typeck/check/alt.rs +++ b/src/rustc/middle/typeck/check/alt.rs @@ -123,70 +123,102 @@ fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path, let fcx = pcx.fcx; let tcx = pcx.fcx.ccx.tcx; - // Lookup the enum and variant def ids: - let v_def = lookup_def(pcx.fcx, path.span, pat.id); - let v_def_ids = ast_util::variant_def_ids(v_def); - - // Assign the pattern the type of the *enum*, not the variant. - let enum_tpt = ty::lookup_item_type(tcx, v_def_ids.enm); - instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id, - pcx.block_region); + let arg_types, kind_name; // structure_of requires type variables to be resolved. // So when we pass in , it's an error if it // contains type variables. - // Take the enum type params out of `expected`. + // Check to see whether this is an enum or a struct. match structure_of(pcx.fcx, pat.span, expected) { - ty::ty_enum(_, ref expected_substs) => { - // 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::suptype(fcx, pat.span, pat_ty, expected); + ty::ty_enum(_, ref expected_substs) => { + // Lookup the enum and variant def ids: + let v_def = lookup_def(pcx.fcx, path.span, pat.id); + let v_def_ids = ast_util::variant_def_ids(v_def); - // Get the expected types of the arguments. - let arg_types = { - let vinfo = - ty::enum_variant_with_id( - tcx, v_def_ids.enm, v_def_ids.var); - vinfo.args.map(|t| { ty::subst(tcx, expected_substs, *t) }) - }; - let arg_len = arg_types.len(), subpats_len = match subpats { - None => arg_len, - Some(ps) => ps.len() - }; - if arg_len > 0 { - // N-ary variant. - if arg_len != subpats_len { - let s = fmt!("this pattern has %u field%s, but the \ - corresponding variant has %u field%s", - subpats_len, - if subpats_len == 1u { ~"" } else { ~"s" }, - arg_len, - if arg_len == 1u { ~"" } else { ~"s" }); - tcx.sess.span_fatal(pat.span, s); - } + // Assign the pattern the type of the *enum*, not the variant. + let enum_tpt = ty::lookup_item_type(tcx, v_def_ids.enm); + instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id, + pcx.block_region); - do subpats.iter() |pats| { - for vec::each2(*pats, arg_types) |subpat, arg_ty| { - check_pat(pcx, *subpat, *arg_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::suptype(fcx, pat.span, pat_ty, expected); + + // Get the expected types of the arguments. + arg_types = { + let vinfo = + ty::enum_variant_with_id( + tcx, v_def_ids.enm, v_def_ids.var); + vinfo.args.map(|t| { ty::subst(tcx, expected_substs, *t) }) }; - } else if subpats_len > 0 { - tcx.sess.span_fatal - (pat.span, fmt!("this pattern has %u field%s, \ - but the corresponding variant has no fields", - subpats_len, - if subpats_len == 1u { ~"" } - else { ~"s" })); + + kind_name = "variant"; } - } - _ => { + ty::ty_class(struct_def_id, ref expected_substs) => { + // Assign the pattern the type of the struct. + let struct_tpt = ty::lookup_item_type(tcx, struct_def_id); + instantiate_path(pcx.fcx, path, struct_tpt, pat.span, pat.id, + pcx.block_region); + + // 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::suptype(fcx, pat.span, pat_ty, expected); + + // Get the expected types of the arguments. + let class_fields = ty::class_items_as_fields( + tcx, struct_def_id, expected_substs); + arg_types = class_fields.map(|field| field.mt.ty); + + kind_name = "structure"; + } + _ => { + tcx.sess.span_fatal( + pat.span, + fmt!("mismatched types: expected enum or structure but \ + found `%s`", + fcx.infcx().ty_to_str(expected))); + } + } + + let arg_len = arg_types.len(); + + // Count the number of subpatterns. + let subpats_len; + match subpats { + None => subpats_len = arg_len, + Some(subpats) => subpats_len = subpats.len() + } + + if arg_len > 0u { + // N-ary variant. + if arg_len != subpats_len { + let s = fmt!("this pattern has %u field%s, but the corresponding \ + %s has %u field%s", + subpats_len, + if subpats_len == 1u { ~"" } else { ~"s" }, + kind_name, + arg_len, + if arg_len == 1u { ~"" } else { ~"s" }); + // XXX: This should not be fatal. + tcx.sess.span_fatal(pat.span, s); + } + + do subpats.iter() |pats| { + for vec::each2(*pats, arg_types) |subpat, arg_ty| { + check_pat(pcx, *subpat, *arg_ty); + } + }; + } else if subpats_len > 0u { tcx.sess.span_fatal - (pat.span, - fmt!("mismatched types: expected enum but found `%s`", - fcx.infcx().ty_to_str(expected))); - } + (pat.span, fmt!("this pattern has %u field%s, but the \ + corresponding %s has no fields", + subpats_len, + if subpats_len == 1u { ~"" } + else { ~"s" }, + kind_name)); } } diff --git a/src/test/compile-fail/tuple-struct-nonexhaustive.rs b/src/test/compile-fail/tuple-struct-nonexhaustive.rs new file mode 100644 index 00000000000..a32d598686c --- /dev/null +++ b/src/test/compile-fail/tuple-struct-nonexhaustive.rs @@ -0,0 +1,11 @@ +struct Foo(int, int); + +fn main() { + let x = Foo(1, 2); + match x { //~ ERROR non-exhaustive + Foo(1, b) => io::println(fmt!("%d", b)), + Foo(2, b) => io::println(fmt!("%d", b)) + } +} + +