Skip to content

Commit 65ee0e1

Browse files
committed
Merge pull request #3858 from pcwalton/struct-like-typeck
rustc: Typecheck, privacy check, and borrow check struct-like enum variants. r=tjc
2 parents 75947b3 + 588ea59 commit 65ee0e1

File tree

3 files changed

+197
-77
lines changed

3 files changed

+197
-77
lines changed

src/rustc/middle/mem_categorization.rs

+32-7
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ impl &mem_categorization_ctxt {
464464
}
465465

466466
let base_cmt = self.cat_expr(base);
467-
self.cat_field(expr, base_cmt, f_name)
467+
self.cat_field(expr, base_cmt, f_name, expr.id)
468468
}
469469

470470
ast::expr_index(base, _) => {
@@ -632,9 +632,14 @@ impl &mem_categorization_ctxt {
632632
}
633633
}
634634

635-
fn cat_field<N:ast_node>(node: N, base_cmt: cmt,
636-
f_name: ast::ident) -> cmt {
637-
let f_mutbl = match field_mutbl(self.tcx, base_cmt.ty, f_name) {
635+
/// The `field_id` parameter is the ID of the enclosing expression or
636+
/// pattern. It is used to determine which variant of an enum is in use.
637+
fn cat_field<N:ast_node>(node: N,
638+
base_cmt: cmt,
639+
f_name: ast::ident,
640+
field_id: ast::node_id) -> cmt {
641+
let f_mutbl = match field_mutbl(self.tcx, base_cmt.ty, f_name,
642+
field_id) {
638643
Some(f_mutbl) => f_mutbl,
639644
None => {
640645
self.tcx.sess.span_bug(
@@ -851,15 +856,15 @@ impl &mem_categorization_ctxt {
851856
ast::pat_rec(field_pats, _) => {
852857
// {f1: p1, ..., fN: pN}
853858
for field_pats.each |fp| {
854-
let cmt_field = self.cat_field(fp.pat, cmt, fp.ident);
859+
let cmt_field = self.cat_field(fp.pat, cmt, fp.ident, pat.id);
855860
self.cat_pattern(cmt_field, fp.pat, op);
856861
}
857862
}
858863

859864
ast::pat_struct(_, field_pats, _) => {
860865
// {f1: p1, ..., fN: pN}
861866
for field_pats.each |fp| {
862-
let cmt_field = self.cat_field(fp.pat, cmt, fp.ident);
867+
let cmt_field = self.cat_field(fp.pat, cmt, fp.ident, pat.id);
863868
self.cat_pattern(cmt_field, fp.pat, op);
864869
}
865870
}
@@ -998,9 +1003,13 @@ impl &mem_categorization_ctxt {
9981003
}
9991004
}
10001005

1006+
/// The node_id here is the node of the expression that references the field.
1007+
/// This function looks it up in the def map in case the type happens to be
1008+
/// an enum to determine which variant is in use.
10011009
fn field_mutbl(tcx: ty::ctxt,
10021010
base_ty: ty::t,
1003-
f_name: ast::ident) -> Option<ast::mutability> {
1011+
f_name: ast::ident,
1012+
node_id: ast::node_id) -> Option<ast::mutability> {
10041013
// Need to refactor so that records/class fields can be treated uniformly.
10051014
match ty::get(base_ty).sty {
10061015
ty::ty_rec(fields) => {
@@ -1021,6 +1030,22 @@ fn field_mutbl(tcx: ty::ctxt,
10211030
}
10221031
}
10231032
}
1033+
ty::ty_enum(*) => {
1034+
match tcx.def_map.get(node_id) {
1035+
ast::def_variant(_, variant_id) => {
1036+
for ty::lookup_class_fields(tcx, variant_id).each |fld| {
1037+
if fld.ident == f_name {
1038+
let m = match fld.mutability {
1039+
ast::class_mutable => ast::m_mutbl,
1040+
ast::class_immutable => ast::m_imm
1041+
};
1042+
return Some(m);
1043+
}
1044+
}
1045+
}
1046+
_ => {}
1047+
}
1048+
}
10241049
_ => { }
10251050
}
10261051

src/rustc/middle/privacy.rs

+25
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,31 @@ fn check_crate(tcx: ty::ctxt, method_map: &method_map, crate: @ast::crate) {
239239
}
240240
}
241241
}
242+
ty_enum(enum_id, _) => {
243+
if enum_id.crate != local_crate ||
244+
!privileged_items.contains(
245+
&enum_id.node) {
246+
match tcx.def_map.find(pattern.id) {
247+
Some(def_variant(_, variant_id)) => {
248+
for fields.each |field| {
249+
debug!("(privacy checking) \
250+
checking field in \
251+
struct variant pattern");
252+
check_field(pattern.span,
253+
variant_id,
254+
field.ident);
255+
}
256+
}
257+
_ => {
258+
tcx.sess.span_bug(pattern.span,
259+
~"resolve didn't \
260+
map enum struct \
261+
pattern to a \
262+
variant def");
263+
}
264+
}
265+
}
266+
}
242267
_ => {
243268
tcx.sess.span_bug(pattern.span,
244269
~"struct pattern didn't have \

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

+140-70
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,139 @@ fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
186186
}
187187
}
188188

189+
/// `path` is the AST path item naming the type of this struct.
190+
/// `fields` is the field patterns of the struct pattern.
191+
/// `class_fields` describes the type of each field of the struct.
192+
/// `class_id` is the ID of the struct.
193+
/// `substitutions` are the type substitutions applied to this struct type
194+
/// (e.g. K,V in HashMap<K,V>).
195+
/// `etc` is true if the pattern said '...' and false otherwise.
196+
fn check_struct_pat_fields(pcx: pat_ctxt,
197+
span: span,
198+
path: @ast::path,
199+
fields: ~[ast::field_pat],
200+
class_fields: ~[ty::field_ty],
201+
class_id: ast::def_id,
202+
substitutions: &ty::substs,
203+
etc: bool) {
204+
let tcx = pcx.fcx.ccx.tcx;
205+
206+
// Index the class fields.
207+
let field_map = std::map::HashMap();
208+
for class_fields.eachi |i, class_field| {
209+
field_map.insert(class_field.ident, i);
210+
}
211+
212+
// Typecheck each field.
213+
let found_fields = std::map::HashMap();
214+
for fields.each |field| {
215+
match field_map.find(field.ident) {
216+
Some(index) => {
217+
let class_field = class_fields[index];
218+
let field_type = ty::lookup_field_type(tcx,
219+
class_id,
220+
class_field.id,
221+
substitutions);
222+
check_pat(pcx, field.pat, field_type);
223+
found_fields.insert(index, ());
224+
}
225+
None => {
226+
let name = pprust::path_to_str(path, tcx.sess.intr());
227+
tcx.sess.span_err(span,
228+
fmt!("struct `%s` does not have a field
229+
named `%s`", name,
230+
tcx.sess.str_of(field.ident)));
231+
}
232+
}
233+
}
234+
235+
// Report an error if not all the fields were specified.
236+
if !etc {
237+
for class_fields.eachi |i, field| {
238+
if found_fields.contains_key(i) {
239+
loop;
240+
}
241+
tcx.sess.span_err(span,
242+
fmt!("pattern does not mention field `%s`",
243+
tcx.sess.str_of(field.ident)));
244+
}
245+
}
246+
}
247+
248+
fn check_struct_pat(pcx: pat_ctxt, pat_id: ast::node_id, span: span,
249+
expected: ty::t, path: @ast::path,
250+
fields: ~[ast::field_pat], etc: bool,
251+
class_id: ast::def_id, substitutions: &ty::substs) {
252+
let fcx = pcx.fcx;
253+
let tcx = pcx.fcx.ccx.tcx;
254+
255+
let class_fields = ty::lookup_class_fields(tcx, class_id);
256+
257+
// Check to ensure that the struct is the one specified.
258+
match tcx.def_map.find(pat_id) {
259+
Some(ast::def_class(supplied_def_id))
260+
if supplied_def_id == class_id => {
261+
// OK.
262+
}
263+
Some(ast::def_class(*)) | Some(ast::def_variant(*)) => {
264+
let name = pprust::path_to_str(path, tcx.sess.intr());
265+
tcx.sess.span_err(span,
266+
fmt!("mismatched types: expected `%s` but \
267+
found `%s`",
268+
fcx.infcx().ty_to_str(expected),
269+
name));
270+
}
271+
_ => {
272+
tcx.sess.span_bug(span, ~"resolve didn't write in class");
273+
}
274+
}
275+
276+
// Forbid pattern-matching structs with destructors.
277+
if ty::has_dtor(tcx, class_id) {
278+
tcx.sess.span_err(span, ~"deconstructing struct not allowed in \
279+
pattern (it has a destructor)");
280+
}
281+
282+
check_struct_pat_fields(pcx, span, path, fields, class_fields, class_id,
283+
substitutions, etc);
284+
}
285+
286+
fn check_struct_like_enum_variant_pat(pcx: pat_ctxt,
287+
pat_id: ast::node_id,
288+
span: span,
289+
expected: ty::t,
290+
path: @ast::path,
291+
fields: ~[ast::field_pat],
292+
etc: bool,
293+
enum_id: ast::def_id,
294+
substitutions: &ty::substs) {
295+
let fcx = pcx.fcx;
296+
let tcx = pcx.fcx.ccx.tcx;
297+
298+
// Find the variant that was specified.
299+
match tcx.def_map.find(pat_id) {
300+
Some(ast::def_variant(found_enum_id, variant_id))
301+
if found_enum_id == enum_id => {
302+
// Get the struct fields from this struct-like enum variant.
303+
let class_fields = ty::lookup_class_fields(tcx, variant_id);
304+
305+
check_struct_pat_fields(pcx, span, path, fields, class_fields,
306+
variant_id, substitutions, etc);
307+
}
308+
Some(ast::def_class(*)) | Some(ast::def_variant(*)) => {
309+
let name = pprust::path_to_str(path, tcx.sess.intr());
310+
tcx.sess.span_err(span,
311+
fmt!("mismatched types: expected `%s` but \
312+
found `%s`",
313+
fcx.infcx().ty_to_str(expected),
314+
name));
315+
}
316+
_ => {
317+
tcx.sess.span_bug(span, ~"resolve didn't write in variant");
318+
}
319+
}
320+
}
321+
189322
// Pattern checking is top-down rather than bottom-up so that bindings get
190323
// their types immediately.
191324
fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
@@ -306,13 +439,16 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
306439
}
307440
ast::pat_struct(path, fields, etc) => {
308441
// Grab the class data that we care about.
309-
let class_fields, class_id, substitutions;
310442
let structure = structure_of(fcx, pat.span, expected);
311443
match structure {
312444
ty::ty_class(cid, ref substs) => {
313-
class_id = cid;
314-
substitutions = substs;
315-
class_fields = ty::lookup_class_fields(tcx, class_id);
445+
check_struct_pat(pcx, pat.id, pat.span, expected, path,
446+
fields, etc, cid, substs);
447+
}
448+
ty::ty_enum(eid, ref substs) => {
449+
check_struct_like_enum_variant_pat(
450+
pcx, pat.id, pat.span, expected, path, fields, etc, eid,
451+
substs);
316452
}
317453
_ => {
318454
// XXX: This should not be fatal.
@@ -323,72 +459,6 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
323459
}
324460
}
325461

326-
// Check to ensure that the struct is the one specified.
327-
match tcx.def_map.get(pat.id) {
328-
ast::def_class(supplied_def_id)
329-
if supplied_def_id == class_id => {
330-
// OK.
331-
}
332-
ast::def_class(*) => {
333-
let name = pprust::path_to_str(path, tcx.sess.intr());
334-
tcx.sess.span_err(pat.span,
335-
fmt!("mismatched types: expected `%s` but \
336-
found `%s`",
337-
fcx.infcx().ty_to_str(expected),
338-
name));
339-
}
340-
_ => {
341-
tcx.sess.span_bug(pat.span, ~"resolve didn't write in class");
342-
}
343-
}
344-
345-
// Forbid pattern-matching structs with destructors.
346-
if ty::has_dtor(tcx, class_id) {
347-
tcx.sess.span_err(pat.span, ~"deconstructing struct not allowed \
348-
in pattern (it has a destructor)");
349-
}
350-
351-
// Index the class fields.
352-
let field_map = std::map::HashMap();
353-
for class_fields.eachi |i, class_field| {
354-
field_map.insert(class_field.ident, i);
355-
}
356-
357-
// Typecheck each field.
358-
let found_fields = std::map::HashMap();
359-
for fields.each |field| {
360-
match field_map.find(field.ident) {
361-
Some(index) => {
362-
let class_field = class_fields[index];
363-
let field_type = ty::lookup_field_type(tcx,
364-
class_id,
365-
class_field.id,
366-
substitutions);
367-
check_pat(pcx, field.pat, field_type);
368-
found_fields.insert(index, ());
369-
}
370-
None => {
371-
let name = pprust::path_to_str(path, tcx.sess.intr());
372-
tcx.sess.span_err(pat.span,
373-
fmt!("struct `%s` does not have a field
374-
named `%s`", name,
375-
tcx.sess.str_of(field.ident)));
376-
}
377-
}
378-
}
379-
380-
// Report an error if not all the fields were specified.
381-
if !etc {
382-
for class_fields.eachi |i, field| {
383-
if found_fields.contains_key(i) {
384-
loop;
385-
}
386-
tcx.sess.span_err(pat.span,
387-
fmt!("pattern does not mention field `%s`",
388-
tcx.sess.str_of(field.ident)));
389-
}
390-
}
391-
392462
// Finally, write in the type.
393463
fcx.write_ty(pat.id, expected);
394464
}

0 commit comments

Comments
 (0)