Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustc: Typecheck, privacy check, and borrow check struct-like enum varia... #3858

Merged
merged 1 commit into from
Oct 25, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 32 additions & 7 deletions src/rustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ impl &mem_categorization_ctxt {
}

let base_cmt = self.cat_expr(base);
self.cat_field(expr, base_cmt, f_name)
self.cat_field(expr, base_cmt, f_name, expr.id)
}

ast::expr_index(base, _) => {
Expand Down Expand Up @@ -632,9 +632,14 @@ impl &mem_categorization_ctxt {
}
}

fn cat_field<N:ast_node>(node: N, base_cmt: cmt,
f_name: ast::ident) -> cmt {
let f_mutbl = match field_mutbl(self.tcx, base_cmt.ty, f_name) {
/// The `field_id` parameter is the ID of the enclosing expression or
/// pattern. It is used to determine which variant of an enum is in use.
fn cat_field<N:ast_node>(node: N,
base_cmt: cmt,
f_name: ast::ident,
field_id: ast::node_id) -> cmt {
let f_mutbl = match field_mutbl(self.tcx, base_cmt.ty, f_name,
field_id) {
Some(f_mutbl) => f_mutbl,
None => {
self.tcx.sess.span_bug(
Expand Down Expand Up @@ -851,15 +856,15 @@ impl &mem_categorization_ctxt {
ast::pat_rec(field_pats, _) => {
// {f1: p1, ..., fN: pN}
for field_pats.each |fp| {
let cmt_field = self.cat_field(fp.pat, cmt, fp.ident);
let cmt_field = self.cat_field(fp.pat, cmt, fp.ident, pat.id);
self.cat_pattern(cmt_field, fp.pat, op);
}
}

ast::pat_struct(_, field_pats, _) => {
// {f1: p1, ..., fN: pN}
for field_pats.each |fp| {
let cmt_field = self.cat_field(fp.pat, cmt, fp.ident);
let cmt_field = self.cat_field(fp.pat, cmt, fp.ident, pat.id);
self.cat_pattern(cmt_field, fp.pat, op);
}
}
Expand Down Expand Up @@ -998,9 +1003,13 @@ impl &mem_categorization_ctxt {
}
}

/// The node_id here is the node of the expression that references the field.
/// This function looks it up in the def map in case the type happens to be
/// an enum to determine which variant is in use.
fn field_mutbl(tcx: ty::ctxt,
base_ty: ty::t,
f_name: ast::ident) -> Option<ast::mutability> {
f_name: ast::ident,
node_id: ast::node_id) -> Option<ast::mutability> {
// Need to refactor so that records/class fields can be treated uniformly.
match ty::get(base_ty).sty {
ty::ty_rec(fields) => {
Expand All @@ -1021,6 +1030,22 @@ fn field_mutbl(tcx: ty::ctxt,
}
}
}
ty::ty_enum(*) => {
match tcx.def_map.get(node_id) {
ast::def_variant(_, variant_id) => {
for ty::lookup_class_fields(tcx, variant_id).each |fld| {
if fld.ident == f_name {
let m = match fld.mutability {
ast::class_mutable => ast::m_mutbl,
ast::class_immutable => ast::m_imm
};
return Some(m);
}
}
}
_ => {}
}
}
_ => { }
}

Expand Down
25 changes: 25 additions & 0 deletions src/rustc/middle/privacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,31 @@ fn check_crate(tcx: ty::ctxt, method_map: &method_map, crate: @ast::crate) {
}
}
}
ty_enum(enum_id, _) => {
if enum_id.crate != local_crate ||
!privileged_items.contains(
&enum_id.node) {
match tcx.def_map.find(pattern.id) {
Some(def_variant(_, variant_id)) => {
for fields.each |field| {
debug!("(privacy checking) \
checking field in \
struct variant pattern");
check_field(pattern.span,
variant_id,
field.ident);
}
}
_ => {
tcx.sess.span_bug(pattern.span,
~"resolve didn't \
map enum struct \
pattern to a \
variant def");
}
}
}
}
_ => {
tcx.sess.span_bug(pattern.span,
~"struct pattern didn't have \
Expand Down
210 changes: 140 additions & 70 deletions src/rustc/middle/typeck/check/alt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,139 @@ 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<K,V>).
/// `etc` is true if the pattern said '...' and false otherwise.
fn check_struct_pat_fields(pcx: pat_ctxt,
span: span,
path: @ast::path,
fields: ~[ast::field_pat],
class_fields: ~[ty::field_ty],
class_id: ast::def_id,
substitutions: &ty::substs,
etc: bool) {
let tcx = pcx.fcx.ccx.tcx;

// Index the class fields.
let field_map = std::map::HashMap();
for class_fields.eachi |i, class_field| {
field_map.insert(class_field.ident, i);
}

// Typecheck each field.
let found_fields = std::map::HashMap();
for fields.each |field| {
match field_map.find(field.ident) {
Some(index) => {
let class_field = class_fields[index];
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 => {
let name = pprust::path_to_str(path, tcx.sess.intr());
tcx.sess.span_err(span,
fmt!("struct `%s` does not have a field
named `%s`", name,
tcx.sess.str_of(field.ident)));
}
}
}

// Report an error if not all the fields were specified.
if !etc {
for class_fields.eachi |i, field| {
if found_fields.contains_key(i) {
loop;
}
tcx.sess.span_err(span,
fmt!("pattern does not mention field `%s`",
tcx.sess.str_of(field.ident)));
}
}
}

fn check_struct_pat(pcx: pat_ctxt, pat_id: ast::node_id, span: span,
expected: ty::t, path: @ast::path,
fields: ~[ast::field_pat], etc: bool,
class_id: ast::def_id, substitutions: &ty::substs) {
let fcx = pcx.fcx;
let tcx = pcx.fcx.ccx.tcx;

let class_fields = ty::lookup_class_fields(tcx, class_id);

// Check to ensure that the struct is the one specified.
match tcx.def_map.find(pat_id) {
Some(ast::def_class(supplied_def_id))
if supplied_def_id == class_id => {
// OK.
}
Some(ast::def_class(*)) | Some(ast::def_variant(*)) => {
let name = pprust::path_to_str(path, tcx.sess.intr());
tcx.sess.span_err(span,
fmt!("mismatched types: expected `%s` but \
found `%s`",
fcx.infcx().ty_to_str(expected),
name));
}
_ => {
tcx.sess.span_bug(span, ~"resolve didn't write in class");
}
}
// Forbid pattern-matching structs with destructors.
if ty::has_dtor(tcx, class_id) {
tcx.sess.span_err(span, ~"deconstructing struct not allowed in \
pattern (it has a destructor)");
}
check_struct_pat_fields(pcx, span, path, fields, class_fields, class_id,
substitutions, etc);
}
fn check_struct_like_enum_variant_pat(pcx: pat_ctxt,
pat_id: ast::node_id,
span: span,
expected: ty::t,
path: @ast::path,
fields: ~[ast::field_pat],
etc: bool,
enum_id: ast::def_id,
substitutions: &ty::substs) {
let fcx = pcx.fcx;
let tcx = pcx.fcx.ccx.tcx;
// Find the variant that was specified.
match tcx.def_map.find(pat_id) {
Some(ast::def_variant(found_enum_id, variant_id))
if found_enum_id == enum_id => {
// Get the struct fields from this struct-like enum variant.
let class_fields = ty::lookup_class_fields(tcx, variant_id);
check_struct_pat_fields(pcx, span, path, fields, class_fields,
variant_id, substitutions, etc);
}
Some(ast::def_class(*)) | Some(ast::def_variant(*)) => {
let name = pprust::path_to_str(path, tcx.sess.intr());
tcx.sess.span_err(span,
fmt!("mismatched types: expected `%s` but \
found `%s`",
fcx.infcx().ty_to_str(expected),
name));
}
_ => {
tcx.sess.span_bug(span, ~"resolve didn't write in variant");
}
}
}
// Pattern checking is top-down rather than bottom-up so that bindings get
// their types immediately.
fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
Expand Down Expand Up @@ -306,13 +439,16 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
}
ast::pat_struct(path, fields, etc) => {
// Grab the class data that we care about.
let class_fields, class_id, substitutions;
let structure = structure_of(fcx, pat.span, expected);
match structure {
ty::ty_class(cid, ref substs) => {
class_id = cid;
substitutions = substs;
class_fields = ty::lookup_class_fields(tcx, class_id);
check_struct_pat(pcx, pat.id, pat.span, expected, path,
fields, etc, cid, substs);
}
ty::ty_enum(eid, ref substs) => {
check_struct_like_enum_variant_pat(
pcx, pat.id, pat.span, expected, path, fields, etc, eid,
substs);
}
_ => {
// XXX: This should not be fatal.
Expand All @@ -323,72 +459,6 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
}
}

// Check to ensure that the struct is the one specified.
match tcx.def_map.get(pat.id) {
ast::def_class(supplied_def_id)
if supplied_def_id == class_id => {
// OK.
}
ast::def_class(*) => {
let name = pprust::path_to_str(path, tcx.sess.intr());
tcx.sess.span_err(pat.span,
fmt!("mismatched types: expected `%s` but \
found `%s`",
fcx.infcx().ty_to_str(expected),
name));
}
_ => {
tcx.sess.span_bug(pat.span, ~"resolve didn't write in class");
}
}
// Forbid pattern-matching structs with destructors.
if ty::has_dtor(tcx, class_id) {
tcx.sess.span_err(pat.span, ~"deconstructing struct not allowed \
in pattern (it has a destructor)");
}
// Index the class fields.
let field_map = std::map::HashMap();
for class_fields.eachi |i, class_field| {
field_map.insert(class_field.ident, i);
}
// Typecheck each field.
let found_fields = std::map::HashMap();
for fields.each |field| {
match field_map.find(field.ident) {
Some(index) => {
let class_field = class_fields[index];
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 => {
let name = pprust::path_to_str(path, tcx.sess.intr());
tcx.sess.span_err(pat.span,
fmt!("struct `%s` does not have a field
named `%s`", name,
tcx.sess.str_of(field.ident)));
}
}
}

// Report an error if not all the fields were specified.
if !etc {
for class_fields.eachi |i, field| {
if found_fields.contains_key(i) {
loop;
}
tcx.sess.span_err(pat.span,
fmt!("pattern does not mention field `%s`",
tcx.sess.str_of(field.ident)));
}
}

// Finally, write in the type.
fcx.write_ty(pat.id, expected);
}
Expand Down