diff --git a/src/rustc/middle/borrowck.rs b/src/rustc/middle/borrowck.rs index 1b1ec022d3ff3..ccc2ca9888abd 100644 --- a/src/rustc/middle/borrowck.rs +++ b/src/rustc/middle/borrowck.rs @@ -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 } } diff --git a/src/rustc/middle/borrowck/loan.rs b/src/rustc/middle/borrowck/loan.rs index 71414e6e72453..b302349722073 100644 --- a/src/rustc/middle/borrowck/loan.rs +++ b/src/rustc/middle/borrowck/loan.rs @@ -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) } diff --git a/src/rustc/middle/borrowck/preserve.rs b/src/rustc/middle/borrowck/preserve.rs index 556ea7867cfca..c27ceca055fdc 100644 --- a/src/rustc/middle/borrowck/preserve.rs +++ b/src/rustc/middle/borrowck/preserve.rs @@ -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) diff --git a/src/rustc/middle/check_alt.rs b/src/rustc/middle/check_alt.rs index 7ed7829cf343b..5b35ed98a837d 100644 --- a/src/rustc/middle/check_alt.rs +++ b/src/rustc/middle/check_alt.rs @@ -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 } } diff --git a/src/rustc/middle/mem_categorization.rs b/src/rustc/middle/mem_categorization.rs index 8ee9adc4e2f3b..3d018ea8b9482 100644 --- a/src/rustc/middle/mem_categorization.rs +++ b/src/rustc/middle/mem_categorization.rs @@ -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, @@ -773,6 +781,14 @@ impl &mem_categorization_ctxt { ty: self.tcx.ty(elt)} } + fn cat_anon_struct_field(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, @@ -832,16 +848,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"); + } } } @@ -932,6 +958,7 @@ impl &mem_categorization_ctxt { comp_field(fld, _) => self.tcx.sess.str_of(fld), comp_index(*) => ~"[]", comp_tuple => ~"()", + comp_anon_field => ~"", comp_variant(_) => ~"" } } @@ -984,6 +1011,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 { diff --git a/src/rustc/middle/pat_util.rs b/src/rustc/middle/pat_util.rs index 48ebda9a67e03..15b9645d5660b 100644 --- a/src/rustc/middle/pat_util.rs +++ b/src/rustc/middle/pat_util.rs @@ -23,12 +23,13 @@ fn pat_id_map(dm: resolve::DefMap, pat: @pat) -> PatIdMap { fn pat_is_variant(dm: resolve::DefMap, pat: @pat) -> bool { match pat.node { - pat_enum(_, _) => true, - pat_ident(_, _, None) | pat_struct(*) => match dm.find(pat.id) { - Some(def_variant(_, _)) => true, + pat_enum(_, _) | pat_ident(_, _, None) | pat_struct(*) => { + match dm.find(pat.id) { + Some(def_variant(*)) | Some(def_class(*)) => true, + _ => false + } + } _ => false - }, - _ => false } } diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs index 97551162de58f..8ebebfa112166 100644 --- a/src/rustc/middle/resolve.rs +++ b/src/rustc/middle/resolve.rs @@ -288,10 +288,10 @@ impl AllowCapturingSelfFlag : cmp::Eq { pure fn ne(other: &AllowCapturingSelfFlag) -> bool { !self.eq(other) } } -enum EnumVariantOrConstResolution { - FoundEnumVariant(def), +enum BareIdentifierPatternResolution { + FoundStructOrEnumVariant(def), FoundConst, - EnumVariantOrConstNotFound + BareIdentifierPatternUnresolved } // Specifies how duplicates should be handled when adding a child item if @@ -4187,28 +4187,31 @@ impl Resolver { if !path.global && path.idents.len() == 1u => { // The meaning of pat_ident with no type parameters - // depends on whether an enum variant with that name is in - // scope. The probing lookup has to be careful not to emit - // spurious errors. Only matching patterns (match) can - // match nullary variants. For binding patterns (let), - // matching such a variant is simply disallowed (since - // it's rarely what you want). + // depends on whether an enum variant or unit-like struct + // with that name is in scope. The probing lookup has to + // be careful not to emit spurious errors. Only matching + // patterns (match) can match nullary variants or + // unit-like structs. For binding patterns (let), matching + // such a value is simply disallowed (since it's rarely + // what you want). let ident = path.idents[0]; - match self.resolve_enum_variant_or_const(ident) { - FoundEnumVariant(def) if mode == RefutableMode => { + match self.resolve_bare_identifier_pattern(ident) { + FoundStructOrEnumVariant(def) + if mode == RefutableMode => { debug!("(resolving pattern) resolving `%s` to \ - enum variant", + struct or enum variant", self.session.str_of(ident)); self.record_def(pattern.id, def); } - FoundEnumVariant(_) => { + FoundStructOrEnumVariant(_) => { self.session.span_err(pattern.span, fmt!("declaration of `%s` \ shadows an enum \ - that's in scope", + variant or unit-like \ + struct in scope", self.session .str_of(ident))); } @@ -4218,7 +4221,7 @@ impl Resolver { conflicts with a constant \ in scope"); } - EnumVariantOrConstNotFound => { + BareIdentifierPatternUnresolved => { debug!("(resolving pattern) binding `%s`", self.session.str_of(ident)); @@ -4286,9 +4289,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(_) => { @@ -4348,13 +4352,11 @@ impl Resolver { } } - fn resolve_enum_variant_or_const(name: ident) - -> EnumVariantOrConstResolution { - + fn resolve_bare_identifier_pattern(name: ident) + -> BareIdentifierPatternResolution { match self.resolve_item_in_lexical_scope(self.current_module, - name, - ValueNS) { - + name, + ValueNS) { Success(target) => { match target.bindings.value_def { None => { @@ -4363,14 +4365,14 @@ impl Resolver { } Some(def) => { match def.def { - def @ def_variant(*) => { - return FoundEnumVariant(def); + def @ def_variant(*) | def @ def_class(*) => { + return FoundStructOrEnumVariant(def); } def_const(*) => { return FoundConst; } _ => { - return EnumVariantOrConstNotFound; + return BareIdentifierPatternUnresolved; } } } @@ -4382,7 +4384,7 @@ impl Resolver { } Failed => { - return EnumVariantOrConstNotFound; + return BareIdentifierPatternUnresolved; } } } diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs index b3776e499946a..08613f15886e8 100644 --- a/src/rustc/middle/trans/alt.rs +++ b/src/rustc/middle/trans/alt.rs @@ -154,16 +154,25 @@ use util::common::indenter; fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export. +// An option identifying a literal: either a unit-like struct or an +// expression. +enum Lit { + UnitLikeStructLit(ast::node_id), // the node ID of the pattern + ExprLit(@ast::expr) +} + // An option identifying a branch (either a literal, a enum variant or a // range) enum Opt { - lit(@ast::expr), + lit(Lit), var(/* disr val */int, /* variant dids */{enm: def_id, var: def_id}), range(@ast::expr, @ast::expr) } fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool { match (*a, *b) { - (lit(a), lit(b)) => const_eval::compare_lit_exprs(tcx, a, b) == 0, + (lit(ExprLit(a)), lit(ExprLit(b))) => + const_eval::compare_lit_exprs(tcx, a, b) == 0, + (lit(UnitLikeStructLit(a)), lit(UnitLikeStructLit(b))) => a == b, (range(a1, a2), range(b1, b2)) => { const_eval::compare_lit_exprs(tcx, a1, b1) == 0 && const_eval::compare_lit_exprs(tcx, a2, b2) == 0 @@ -182,10 +191,15 @@ fn trans_opt(bcx: block, o: &Opt) -> opt_result { let ccx = bcx.ccx(); let mut bcx = bcx; match *o { - lit(lit_expr) => { + lit(ExprLit(lit_expr)) => { let datumblock = expr::trans_to_datum(bcx, lit_expr); return single_result(datumblock.to_result()); } + lit(UnitLikeStructLit(pat_id)) => { + let struct_ty = ty::node_id_to_type(bcx.tcx(), pat_id); + let datumblock = datum::scratch_datum(bcx, struct_ty, true); + return single_result(datumblock.to_result(bcx)); + } var(disr_val, _) => { return single_result(rslt(bcx, C_int(ccx, disr_val))); } @@ -197,12 +211,23 @@ fn trans_opt(bcx: block, o: &Opt) -> opt_result { } fn variant_opt(tcx: ty::ctxt, pat_id: ast::node_id) -> Opt { - let vdef = ast_util::variant_def_ids(tcx.def_map.get(pat_id)); - let variants = ty::enum_variants(tcx, vdef.enm); - for vec::each(*variants) |v| { - if vdef.var == v.id { return var(v.disr_val, vdef); } + match tcx.def_map.get(pat_id) { + ast::def_variant(enum_id, var_id) => { + let variants = ty::enum_variants(tcx, enum_id); + for vec::each(*variants) |v| { + if var_id == v.id { + return var(v.disr_val, {enm: enum_id, var: var_id}); + } + } + core::util::unreachable(); + } + ast::def_class(_) => { + return lit(UnitLikeStructLit(pat_id)); + } + _ => { + tcx.sess.bug(~"non-variant or struct in variant_opt()"); + } } - core::util::unreachable(); } enum TransBindingMode { @@ -363,7 +388,8 @@ fn enter_default(bcx: block, dm: DefMap, m: &[@Match/&r], match p.node { ast::pat_wild | ast::pat_rec(_, _) | ast::pat_tup(_) | ast::pat_struct(*) => Some(~[]), - ast::pat_ident(_, _, None) if !pat_is_variant(dm, p) => Some(~[]), + ast::pat_enum(*) | ast::pat_ident(_, _, None) + if !pat_is_variant(dm, p) => Some(~[]), _ => None } } @@ -425,7 +451,7 @@ fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint, } } ast::pat_lit(l) => { - if opt_eq(tcx, &lit(l), opt) {Some(~[])} else {None} + if opt_eq(tcx, &lit(ExprLit(l)), opt) {Some(~[])} else {None} } ast::pat_range(l1, l2) => { if opt_eq(tcx, &range(l1, l2), opt) {Some(~[])} else {None} @@ -522,6 +548,29 @@ fn enter_tup(bcx: block, dm: DefMap, m: &[@Match/&r], } } +fn enter_tuple_struct(bcx: block, dm: DefMap, m: &[@Match/&r], col: uint, + val: ValueRef, n_elts: uint) + -> ~[@Match/&r] +{ + debug!("enter_tuple_struct(bcx=%s, m=%s, col=%u, val=%?)", + bcx.to_str(), + matches_to_str(bcx, m), + col, + bcx.val_str(val)); + let _indenter = indenter(); + + let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()}; + do enter_match(bcx, dm, m, col, val) |p| { + match p.node { + ast::pat_enum(_, Some(elts)) => Some(elts), + _ => { + assert_is_binding_or_wild(bcx, p); + Some(vec::from_elem(n_elts, dummy)) + } + } + } +} + fn enter_box(bcx: block, dm: DefMap, m: &[@Match/&r], col: uint, val: ValueRef) -> ~[@Match/&r] @@ -611,7 +660,7 @@ fn get_options(ccx: @crate_ctxt, m: &[@Match], col: uint) -> ~[Opt] { } else { match cur.node { ast::pat_lit(l) => { - add_to_set(ccx.tcx, &found, lit(l)); + add_to_set(ccx.tcx, &found, lit(ExprLit(l))); } ast::pat_range(l1, l2) => { add_to_set(ccx.tcx, &found, range(l1, l2)); @@ -733,6 +782,16 @@ fn any_tup_pat(m: &[@Match], col: uint) -> bool { any_pat!(m, ast::pat_tup(_)) } +fn any_tuple_struct_pat(bcx: block, m: &[@Match], col: uint) -> bool { + vec::any(m, |br| { + let pat = br.pats[col]; + match pat.node { + ast::pat_enum(*) => !pat_is_variant(bcx.tcx().def_map, pat), + _ => false + } + }) +} + type mk_fail = fn@() -> BasicBlockRef; fn pick_col(m: &[@Match]) -> uint { @@ -1028,6 +1087,30 @@ fn compile_submatch(bcx: block, return; } + if any_tuple_struct_pat(bcx, m, col) { + let struct_ty = node_id_type(bcx, pat_id); + let struct_element_count; + match ty::get(struct_ty).sty { + ty::ty_class(struct_id, _) => { + struct_element_count = + ty::lookup_class_fields(tcx, struct_id).len(); + } + _ => { + ccx.sess.bug(~"non-struct type in tuple struct pattern"); + } + } + + // XXX: Structs with destructors won't work here. + let llstructvals = vec::from_fn(struct_element_count, + |i| GEPi(bcx, val, [0, 0, i])); + compile_submatch(bcx, + enter_tuple_struct(bcx, dm, m, col, val, + struct_element_count), + vec::append(llstructvals, vals_left), + chk); + return; + } + // Unbox in case of a box field if any_box_pat(m, col) { let llbox = Load(bcx, val); @@ -1335,15 +1418,32 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef, for inner.each |inner_pat| { bcx = bind_irrefutable_pat(bcx, *inner_pat, val, true); } - } + } ast::pat_enum(_, sub_pats) => { - let pat_def = ccx.tcx.def_map.get(pat.id); - let vdefs = ast_util::variant_def_ids(pat_def); - let args = extract_variant_args(bcx, pat.id, vdefs, val); - for sub_pats.each |sub_pat| { - for vec::eachi(args.vals) |i, argval| { - bcx = bind_irrefutable_pat(bcx, sub_pat[i], - *argval, make_copy); + if pat_is_variant(bcx.tcx().def_map, pat) { + let pat_def = ccx.tcx.def_map.get(pat.id); + let vdefs = ast_util::variant_def_ids(pat_def); + let args = extract_variant_args(bcx, pat.id, vdefs, val); + for sub_pats.each |sub_pat| { + for vec::eachi(args.vals) |i, argval| { + bcx = bind_irrefutable_pat(bcx, sub_pat[i], + *argval, make_copy); + } + } + } else { + match sub_pats { + None => { + // This is a unit-like struct. Nothing to do here. + } + Some(elems) => { + // This is the tuple variant case. + // XXX: This won't work for structs with destructors. + for vec::eachi(elems) |i, elem| { + let fldptr = GEPi(bcx, val, [0, 0, i]); + bcx = bind_irrefutable_pat(bcx, *elem, fldptr, + make_copy); + } + } } } } diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 40792ce45ed4f..727958e3e5f8c 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -1893,12 +1893,14 @@ fn trans_struct_def(ccx: @crate_ctxt, struct_def: @ast::struct_def, // If this is a tuple-like struct, translate the constructor. match struct_def.ctor_id { - None => {} - Some(ctor_id) => { + // We only need to translate a constructor if there are fields; + // otherwise this is a unit-like struct. + Some(ctor_id) if struct_def.fields.len() > 0 => { let llfndecl = get_item_val(ccx, ctor_id); trans_tuple_struct(ccx, struct_def.fields, ctor_id, None, llfndecl); } + Some(_) | None => {} } } diff --git a/src/rustc/middle/trans/datum.rs b/src/rustc/middle/trans/datum.rs index 5e11cdd479436..700ac402a8ad4 100644 --- a/src/rustc/middle/trans/datum.rs +++ b/src/rustc/middle/trans/datum.rs @@ -651,6 +651,41 @@ impl Datum { } }; } + ty::ty_class(did, ref substs) => { + // Check whether this struct is a newtype struct. + let fields = ty::class_items_as_fields(ccx.tcx, did, substs); + if fields.len() != 1 || fields[0].ident != + syntax::parse::token::special_idents::unnamed_field { + return None; + } + + let ty = fields[0].mt.ty; + return match self.mode { + ByRef => { + // Recast lv.val as a pointer to the newtype rather + // than a pointer to the struct type. + // XXX: This isn't correct for structs with + // destructors. + Some(Datum { + val: GEPi(bcx, self.val, [0, 0, 0]), + ty: ty, + mode: ByRef, + source: FromLvalue + }) + } + ByValue => { + // Actually, this case cannot happen right now, + // because structs are never immediate. But in + // principle, newtype'd immediate values should be + // immediate, and in that case the * would be a no-op + // except for changing the type, so I am putting this + // code in place here to do the right thing if this + // change ever goes through. + assert ty::type_is_immediate(ty); + Some(Datum {ty: ty, ..self}) + } + } + } _ => { // not derefable. return None; } diff --git a/src/rustc/middle/trans/expr.rs b/src/rustc/middle/trans/expr.rs index dee553536048a..c5f29ef1ab0ba 100644 --- a/src/rustc/middle/trans/expr.rs +++ b/src/rustc/middle/trans/expr.rs @@ -650,6 +650,11 @@ fn trans_def_dps_unadjusted(bcx: block, ref_expr: @ast::expr, return bcx; } } + ast::def_class(*) => { + // Nothing to do here. + // XXX: May not be true in the case of classes with destructors. + return bcx; + } _ => { bcx.tcx().sess.span_bug(ref_expr.span, fmt!( "Non-DPS def %? referened by %s", diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index f44f6a3ce628d..7db69f6b0ead4 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -2597,6 +2597,16 @@ fn deref_sty(cx: ctxt, sty: &sty, expl: bool) -> Option { } } + ty_class(did, ref substs) => { + let fields = class_items_as_fields(cx, did, substs); + if fields.len() == 1 && fields[0].ident == + syntax::parse::token::special_idents::unnamed_field { + Some({ty: fields[0].mt.ty, mutbl: ast::m_imm}) + } else { + None + } + } + _ => None } } @@ -2998,7 +3008,7 @@ fn expr_kind(tcx: ctxt, ast::expr_path(*) => { match resolve_expr(tcx, expr) { ast::def_fn(*) | ast::def_static_method(*) | - ast::def_variant(*) => RvalueDpsExpr, + ast::def_variant(*) | ast::def_class(*) => RvalueDpsExpr, // Note: there is actually a good case to be made that // def_args, particularly those of immediate type, ought to @@ -3570,6 +3580,29 @@ fn ty_to_def_id(ty: t) -> Option { } } +/// 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 { + 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}; diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs index 339a035b35cfa..546efc0963de9 100644 --- a/src/rustc/middle/typeck/check.rs +++ b/src/rustc/middle/typeck/check.rs @@ -467,10 +467,6 @@ fn check_struct(ccx: @crate_ctxt, struct_def: @ast::struct_def, for struct_def.methods.each |m| { check_method(ccx, *m, self_ty, local_def(id)); } - // Check that there's at least one field - if struct_def.fields.len() < 1u { - ccx.tcx.sess.span_err(span, ~"a struct must have at least one field"); - } // Check that the class is instantiable check_instantiable(ccx.tcx, span, id); } @@ -1763,6 +1759,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, with a single variant which has a \ single argument"); } + ty::ty_class(*) => { + tcx.sess.span_err( + expr.span, + ~"can only dereference structs with one anonymous \ + field"); + } _ => { tcx.sess.span_err( expr.span, diff --git a/src/rustc/middle/typeck/check/alt.rs b/src/rustc/middle/typeck/check/alt.rs index a8308bb1b3c24..bbcf147c8686e 100644 --- a/src/rustc/middle/typeck/check/alt.rs +++ b/src/rustc/middle/typeck/check/alt.rs @@ -123,66 +123,98 @@ 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); + let arg_types, kind_name; - // 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); - - // 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); - - // 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 > 0u { - // 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); - } - - do subpats.iter() |pats| { - for vec::each2(*pats, arg_types) |subpat, arg_ty| { - check_pat(pcx, *subpat, *arg_ty); - } + 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); + + // 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); + + // 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 > 0u { - 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)); } } diff --git a/src/test/compile-fail/tuple-struct-nonexhaustive.rs b/src/test/compile-fail/tuple-struct-nonexhaustive.rs new file mode 100644 index 0000000000000..a32d598686c8c --- /dev/null +++ b/src/test/compile-fail/tuple-struct-nonexhaustive.rs @@ -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)) + } +} + + diff --git a/src/test/run-pass/struct-deref.rs b/src/test/run-pass/struct-deref.rs new file mode 100644 index 0000000000000..74726f3c867fd --- /dev/null +++ b/src/test/run-pass/struct-deref.rs @@ -0,0 +1,7 @@ +struct Foo(int); + +fn main() { + let x: Foo = Foo(2); + assert *x == 2; +} + diff --git a/src/test/run-pass/tuple-struct-destructuring.rs b/src/test/run-pass/tuple-struct-destructuring.rs new file mode 100644 index 0000000000000..cffd9d828cbb1 --- /dev/null +++ b/src/test/run-pass/tuple-struct-destructuring.rs @@ -0,0 +1,10 @@ +struct Foo(int, int); + +fn main() { + let x = Foo(1, 2); + let Foo(y, z) = x; + io::println(fmt!("%d %d", y, z)); + assert y == 1; + assert z == 2; +} + diff --git a/src/test/run-pass/tuple-struct-matching.rs b/src/test/run-pass/tuple-struct-matching.rs new file mode 100644 index 0000000000000..923e8cbc09d5d --- /dev/null +++ b/src/test/run-pass/tuple-struct-matching.rs @@ -0,0 +1,13 @@ +struct Foo(int, int); + +fn main() { + let x = Foo(1, 2); + match x { + Foo(a, b) => { + assert a == 1; + assert b == 2; + io::println(fmt!("%d %d", a, b)); + } + } +} + diff --git a/src/test/run-pass/unit-like-struct.rs b/src/test/run-pass/unit-like-struct.rs new file mode 100644 index 0000000000000..e5eb50a8afd16 --- /dev/null +++ b/src/test/run-pass/unit-like-struct.rs @@ -0,0 +1,9 @@ +struct Foo; + +fn main() { + let x: Foo = Foo; + match x { + Foo => { io::println("hi"); } + } +} +