rustc: Implement typechecking, exhaustiveness checking, and borrow checking for pattern matching of tuple structs. r=nmatsakis

Conflicts:

	src/rustc/middle/typeck/check/alt.rs
This commit is contained in:
Patrick Walton 2012-11-02 09:56:09 -07:00
parent b62844e755
commit 106f9976ab
9 changed files with 175 additions and 69 deletions

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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
}
}

View File

@ -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<N: ast_node>(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 => ~"<anonymous field>",
comp_variant(_) => ~"<enum>"
}
}
@ -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 {

View File

@ -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(_) => {

View File

@ -3554,6 +3554,29 @@ fn ty_to_def_id(ty: t) -> Option<ast::def_id> {
}
}
/// 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<ast::def_id> {
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};

View File

@ -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 <expected>, 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));
}
}

View File

@ -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))
}
}