Skip to content

Commit 106f997

Browse files
committed
rustc: Implement typechecking, exhaustiveness checking, and borrow checking for pattern matching of tuple structs. r=nmatsakis
Conflicts: src/rustc/middle/typeck/check/alt.rs
1 parent b62844e commit 106f997

File tree

9 files changed

+177
-71
lines changed

9 files changed

+177
-71
lines changed

src/rustc/middle/borrowck.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ impl borrowck_ctxt {
594594
// mutable structure.
595595
fn inherent_mutability(ck: comp_kind) -> mutability {
596596
match ck {
597-
comp_tuple | comp_variant(_) => m_imm,
598-
comp_field(_, m) | comp_index(_, m) => m
597+
comp_tuple | comp_anon_field | comp_variant(_) => m_imm,
598+
comp_field(_, m) | comp_index(_, m) => m
599599
}
600600
}

src/rustc/middle/borrowck/loan.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ impl LoanContext {
116116
// overwritten and the component along with it.
117117
self.loan_stable_comp(cmt, cmt_base, req_mutbl, m)
118118
}
119-
cat_comp(cmt_base, comp_tuple) => {
119+
cat_comp(cmt_base, comp_tuple) |
120+
cat_comp(cmt_base, comp_anon_field) => {
120121
// As above.
121122
self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm)
122123
}

src/rustc/middle/borrowck/preserve.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ priv impl &preserve_ctxt {
119119
}
120120
cat_comp(cmt_base, comp_field(*)) |
121121
cat_comp(cmt_base, comp_index(*)) |
122-
cat_comp(cmt_base, comp_tuple) => {
122+
cat_comp(cmt_base, comp_tuple) |
123+
cat_comp(cmt_base, comp_anon_field) => {
123124
// Most embedded components: if the base is stable, the
124125
// type never changes.
125126
self.preserve(cmt_base)

src/rustc/middle/check_alt.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,15 @@ fn specialize(tcx: ty::ctxt, r: ~[@pat], ctor_id: ctor, arity: uint,
355355
Some(vec::append(args, vec::tail(r)))
356356
}
357357
def_variant(_, _) => None,
358+
def_class(*) => {
359+
// XXX: Is this right? --pcw
360+
let new_args;
361+
match args {
362+
Some(args) => new_args = args,
363+
None => new_args = vec::from_elem(arity, wild())
364+
}
365+
Some(vec::append(new_args, vec::tail(r)))
366+
}
358367
_ => None
359368
}
360369
}

src/rustc/middle/mem_categorization.rs

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ impl ptr_kind : cmp::Eq {
162162
// structure accessible without a dereference":
163163
enum comp_kind {
164164
comp_tuple, // elt in a tuple
165+
comp_anon_field, // anonymous field (in e.g.
166+
// struct Foo(int, int);
165167
comp_variant(ast::def_id), // internals to a variant of given enum
166168
comp_field(ast::ident, // name of field
167169
ast::mutability), // declared mutability of field
@@ -178,6 +180,12 @@ impl comp_kind : cmp::Eq {
178180
_ => false
179181
}
180182
}
183+
comp_anon_field => {
184+
match (*other) {
185+
comp_anon_field => true,
186+
_ => false
187+
}
188+
}
181189
comp_variant(e0a) => {
182190
match (*other) {
183191
comp_variant(e0b) => e0a == e0b,
@@ -775,6 +783,14 @@ impl &mem_categorization_ctxt {
775783
ty: self.tcx.ty(elt)}
776784
}
777785

786+
fn cat_anon_struct_field<N: ast_node>(elt: N, cmt: cmt) -> cmt {
787+
@{id: elt.id(), span: elt.span(),
788+
cat: cat_comp(cmt, comp_anon_field),
789+
lp: cmt.lp.map(|l| @lp_comp(*l, comp_anon_field)),
790+
mutbl: cmt.mutbl, // imm iff in an immutable context
791+
ty: self.tcx.ty(elt)}
792+
}
793+
778794
fn cat_method_ref(expr: @ast::expr, expr_ty: ty::t) -> cmt {
779795
@{id:expr.id, span:expr.span,
780796
cat:cat_special(sk_method), lp:None,
@@ -834,16 +850,26 @@ impl &mem_categorization_ctxt {
834850
// variant(*)
835851
}
836852
ast::pat_enum(_, Some(subpats)) => {
837-
// variant(x, y, z)
838-
let enum_did = match self.tcx.def_map.find(pat.id) {
839-
Some(ast::def_variant(enum_did, _)) => enum_did,
840-
e => tcx.sess.span_bug(pat.span,
841-
fmt!("resolved to %?, not variant", e))
842-
};
843-
844-
for subpats.each |subpat| {
845-
let subcmt = self.cat_variant(*subpat, enum_did, cmt);
846-
self.cat_pattern(subcmt, *subpat, op);
853+
match self.tcx.def_map.find(pat.id) {
854+
Some(ast::def_variant(enum_did, _)) => {
855+
// variant(x, y, z)
856+
for subpats.each |subpat| {
857+
let subcmt = self.cat_variant(*subpat, enum_did, cmt);
858+
self.cat_pattern(subcmt, *subpat, op);
859+
}
860+
}
861+
Some(ast::def_class(*)) => {
862+
for subpats.each |subpat| {
863+
let cmt_field = self.cat_anon_struct_field(*subpat,
864+
cmt);
865+
self.cat_pattern(cmt_field, *subpat, op);
866+
}
867+
}
868+
_ => {
869+
self.tcx.sess.span_bug(
870+
pat.span,
871+
~"enum pattern didn't resolve to enum or struct");
872+
}
847873
}
848874
}
849875

@@ -934,6 +960,7 @@ impl &mem_categorization_ctxt {
934960
comp_field(fld, _) => self.tcx.sess.str_of(fld),
935961
comp_index(*) => ~"[]",
936962
comp_tuple => ~"()",
963+
comp_anon_field => ~"<anonymous field>",
937964
comp_variant(_) => ~"<enum>"
938965
}
939966
}
@@ -986,6 +1013,7 @@ impl &mem_categorization_ctxt {
9861013
}
9871014
cat_comp(_, comp_field(*)) => mut_str + ~" field",
9881015
cat_comp(_, comp_tuple) => ~"tuple content",
1016+
cat_comp(_, comp_anon_field) => ~"anonymous field",
9891017
cat_comp(_, comp_variant(_)) => ~"enum content",
9901018
cat_comp(_, comp_index(t, _)) => {
9911019
match ty::get(t).sty {

src/rustc/middle/resolve.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4286,9 +4286,10 @@ impl Resolver {
42864286
}
42874287

42884288
pat_ident(_, path, _) | pat_enum(path, _) => {
4289-
// These two must be enum variants.
4289+
// These two must be enum variants or structs.
42904290
match self.resolve_path(path, ValueNS, false, visitor) {
4291-
Some(def @ def_variant(*)) => {
4291+
Some(def @ def_variant(*)) |
4292+
Some(def @ def_class(*)) => {
42924293
self.record_def(pattern.id, def);
42934294
}
42944295
Some(_) => {

src/rustc/middle/ty.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3554,6 +3554,29 @@ fn ty_to_def_id(ty: t) -> Option<ast::def_id> {
35543554
}
35553555
}
35563556

3557+
/// Returns the def ID of the constructor for the given tuple-like struct, or
3558+
/// None if the struct is not tuple-like. Fails if the given def ID does not
3559+
/// refer to a struct at all.
3560+
fn struct_ctor_id(cx: ctxt, struct_did: ast::def_id) -> Option<ast::def_id> {
3561+
if struct_did.crate != ast::local_crate {
3562+
// XXX: Cross-crate functionality.
3563+
cx.sess.unimpl(~"constructor ID of cross-crate tuple structs");
3564+
}
3565+
3566+
match cx.items.find(struct_did.node) {
3567+
Some(ast_map::node_item(item, _)) => {
3568+
match item.node {
3569+
ast::item_class(struct_def, _) => {
3570+
struct_def.ctor_id.map(|ctor_id|
3571+
ast_util::local_def(*ctor_id))
3572+
}
3573+
_ => cx.sess.bug(~"called struct_ctor_id on non-struct")
3574+
}
3575+
}
3576+
_ => cx.sess.bug(~"called struct_ctor_id on non-struct")
3577+
}
3578+
}
3579+
35573580
// Enum information
35583581
type variant_info = @{args: ~[t], ctor_ty: t, name: ast::ident,
35593582
id: ast::def_id, disr_val: int};

src/rustc/middle/typeck/check/alt.rs

Lines changed: 87 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -123,70 +123,102 @@ fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
123123
let fcx = pcx.fcx;
124124
let tcx = pcx.fcx.ccx.tcx;
125125

126-
// Lookup the enum and variant def ids:
127-
let v_def = lookup_def(pcx.fcx, path.span, pat.id);
128-
let v_def_ids = ast_util::variant_def_ids(v_def);
129-
130-
// Assign the pattern the type of the *enum*, not the variant.
131-
let enum_tpt = ty::lookup_item_type(tcx, v_def_ids.enm);
132-
instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id,
133-
pcx.block_region);
126+
let arg_types, kind_name;
134127

135128
// structure_of requires type variables to be resolved.
136129
// So when we pass in <expected>, it's an error if it
137130
// contains type variables.
138131

139-
// Take the enum type params out of `expected`.
132+
// Check to see whether this is an enum or a struct.
140133
match structure_of(pcx.fcx, pat.span, expected) {
141-
ty::ty_enum(_, ref expected_substs) => {
142-
// check that the type of the value being matched is a subtype
143-
// of the type of the pattern:
144-
let pat_ty = fcx.node_ty(pat.id);
145-
demand::suptype(fcx, pat.span, pat_ty, expected);
146-
147-
// Get the expected types of the arguments.
148-
let arg_types = {
149-
let vinfo =
150-
ty::enum_variant_with_id(
151-
tcx, v_def_ids.enm, v_def_ids.var);
152-
vinfo.args.map(|t| { ty::subst(tcx, expected_substs, *t) })
153-
};
154-
let arg_len = arg_types.len(), subpats_len = match subpats {
155-
None => arg_len,
156-
Some(ps) => ps.len()
157-
};
158-
if arg_len > 0 {
159-
// N-ary variant.
160-
if arg_len != subpats_len {
161-
let s = fmt!("this pattern has %u field%s, but the \
162-
corresponding variant has %u field%s",
163-
subpats_len,
164-
if subpats_len == 1u { ~"" } else { ~"s" },
165-
arg_len,
166-
if arg_len == 1u { ~"" } else { ~"s" });
167-
tcx.sess.span_fatal(pat.span, s);
168-
}
169-
170-
do subpats.iter() |pats| {
171-
for vec::each2(*pats, arg_types) |subpat, arg_ty| {
172-
check_pat(pcx, *subpat, *arg_ty);
173-
}
134+
ty::ty_enum(_, ref expected_substs) => {
135+
// Lookup the enum and variant def ids:
136+
let v_def = lookup_def(pcx.fcx, path.span, pat.id);
137+
let v_def_ids = ast_util::variant_def_ids(v_def);
138+
139+
// Assign the pattern the type of the *enum*, not the variant.
140+
let enum_tpt = ty::lookup_item_type(tcx, v_def_ids.enm);
141+
instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id,
142+
pcx.block_region);
143+
144+
// check that the type of the value being matched is a subtype
145+
// of the type of the pattern:
146+
let pat_ty = fcx.node_ty(pat.id);
147+
demand::suptype(fcx, pat.span, pat_ty, expected);
148+
149+
// Get the expected types of the arguments.
150+
arg_types = {
151+
let vinfo =
152+
ty::enum_variant_with_id(
153+
tcx, v_def_ids.enm, v_def_ids.var);
154+
vinfo.args.map(|t| { ty::subst(tcx, expected_substs, *t) })
174155
};
175-
} else if subpats_len > 0 {
176-
tcx.sess.span_fatal
177-
(pat.span, fmt!("this pattern has %u field%s, \
178-
but the corresponding variant has no fields",
179-
subpats_len,
180-
if subpats_len == 1u { ~"" }
181-
else { ~"s" }));
156+
157+
kind_name = "variant";
182158
}
183-
}
184-
_ => {
159+
ty::ty_class(struct_def_id, ref expected_substs) => {
160+
// Assign the pattern the type of the struct.
161+
let struct_tpt = ty::lookup_item_type(tcx, struct_def_id);
162+
instantiate_path(pcx.fcx, path, struct_tpt, pat.span, pat.id,
163+
pcx.block_region);
164+
165+
// Check that the type of the value being matched is a subtype of
166+
// the type of the pattern.
167+
let pat_ty = fcx.node_ty(pat.id);
168+
demand::suptype(fcx, pat.span, pat_ty, expected);
169+
170+
// Get the expected types of the arguments.
171+
let class_fields = ty::class_items_as_fields(
172+
tcx, struct_def_id, expected_substs);
173+
arg_types = class_fields.map(|field| field.mt.ty);
174+
175+
kind_name = "structure";
176+
}
177+
_ => {
178+
tcx.sess.span_fatal(
179+
pat.span,
180+
fmt!("mismatched types: expected enum or structure but \
181+
found `%s`",
182+
fcx.infcx().ty_to_str(expected)));
183+
}
184+
}
185+
186+
let arg_len = arg_types.len();
187+
188+
// Count the number of subpatterns.
189+
let subpats_len;
190+
match subpats {
191+
None => subpats_len = arg_len,
192+
Some(subpats) => subpats_len = subpats.len()
193+
}
194+
195+
if arg_len > 0u {
196+
// N-ary variant.
197+
if arg_len != subpats_len {
198+
let s = fmt!("this pattern has %u field%s, but the corresponding \
199+
%s has %u field%s",
200+
subpats_len,
201+
if subpats_len == 1u { ~"" } else { ~"s" },
202+
kind_name,
203+
arg_len,
204+
if arg_len == 1u { ~"" } else { ~"s" });
205+
// XXX: This should not be fatal.
206+
tcx.sess.span_fatal(pat.span, s);
207+
}
208+
209+
do subpats.iter() |pats| {
210+
for vec::each2(*pats, arg_types) |subpat, arg_ty| {
211+
check_pat(pcx, *subpat, *arg_ty);
212+
}
213+
};
214+
} else if subpats_len > 0u {
185215
tcx.sess.span_fatal
186-
(pat.span,
187-
fmt!("mismatched types: expected enum but found `%s`",
188-
fcx.infcx().ty_to_str(expected)));
189-
}
216+
(pat.span, fmt!("this pattern has %u field%s, but the \
217+
corresponding %s has no fields",
218+
subpats_len,
219+
if subpats_len == 1u { ~"" }
220+
else { ~"s" },
221+
kind_name));
190222
}
191223
}
192224
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
struct Foo(int, int);
2+
3+
fn main() {
4+
let x = Foo(1, 2);
5+
match x { //~ ERROR non-exhaustive
6+
Foo(1, b) => io::println(fmt!("%d", b)),
7+
Foo(2, b) => io::println(fmt!("%d", b))
8+
}
9+
}
10+
11+

0 commit comments

Comments
 (0)