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:
parent
b62844e755
commit
106f9976ab
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(_) => {
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue