From 7c64f0360774c05dfc819270f8f53266b23b1ced Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 2 May 2014 14:53:33 -0700 Subject: [PATCH] librustc: Implement the `Box` type syntax. RFC #14. Issue #13885. --- src/librustc/middle/lang_items.rs | 1 + src/librustc/middle/typeck/astconv.rs | 256 +++++++++++++++++--------- src/libstd/owned.rs | 8 + src/test/run-pass/new-box.rs | 41 +++++ 4 files changed, 218 insertions(+), 88 deletions(-) create mode 100644 src/test/run-pass/new-box.rs diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 42ef8b9d51b..48465309f1e 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -262,6 +262,7 @@ lets_do_this! { ManagedHeapLangItem, "managed_heap", managed_heap; ExchangeHeapLangItem, "exchange_heap", exchange_heap; GcLangItem, "gc", gc; + OwnedBoxLangItem, "owned_box", owned_box; CovariantTypeItem, "covariant_type", covariant_type; ContravariantTypeItem, "contravariant_type", contravariant_type; diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index 77f339ec10e..bd26e2e0c4d 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -317,7 +317,7 @@ pub fn ast_ty_to_prim_ty(tcx: &ty::ctxt, ast_ty: &ast::Ty) -> Option { match ast_ty.node { ast::TyPath(ref path, _, id) => { let a_def = match tcx.def_map.borrow().find(&id) { - None => tcx.sess.span_fatal( + None => tcx.sess.span_bug( ast_ty.span, format!("unbound path {}", path_to_str(path))), Some(&d) => d }; @@ -366,95 +366,173 @@ pub fn ast_ty_to_prim_ty(tcx: &ty::ctxt, ast_ty: &ast::Ty) -> Option { } } +/// Converts the given AST type to a built-in type. A "built-in type" is, at +/// present, either a core numeric type, a string, or `Box`. +pub fn ast_ty_to_builtin_ty( + this: &AC, + rscope: &RS, + ast_ty: &ast::Ty) + -> Option { + match ast_ty_to_prim_ty(this.tcx(), ast_ty) { + Some(typ) => return Some(typ), + None => {} + } + + match ast_ty.node { + ast::TyPath(ref path, _, id) => { + let a_def = match this.tcx().def_map.borrow().find(&id) { + None => this.tcx().sess.span_bug( + ast_ty.span, format!("unbound path {}", path_to_str(path))), + Some(&d) => d + }; + + // FIXME(#12938): This is a hack until we have full support for + // DST. + match a_def { + ast::DefTy(did) | ast::DefStruct(did) + if Some(did) == this.tcx().lang_items.owned_box() => { + if path.segments + .iter() + .flat_map(|s| s.types.iter()) + .len() > 1 { + this.tcx() + .sess + .span_err(path.span, + "`Box` has only one type parameter") + } + + for inner_ast_type in path.segments + .iter() + .flat_map(|s| s.types.iter()) { + let mt = ast::MutTy { + ty: *inner_ast_type, + mutbl: ast::MutImmutable, + }; + return Some(mk_pointer(this, + rscope, + &mt, + Uniq, + |typ| { + match ty::get(typ).sty { + ty::ty_str => { + this.tcx() + .sess + .span_err(path.span, + "`Box` is not a type"); + ty::mk_err() + } + ty::ty_vec(_, None) => { + this.tcx() + .sess + .span_err(path.span, + "`Box<[T]>` is not a type"); + ty::mk_err() + } + _ => ty::mk_uniq(this.tcx(), typ), + } + })) + } + this.tcx().sess.span_bug(path.span, + "not enough type parameters \ + supplied to `Box`") + } + _ => None + } + } + _ => None + } +} + +enum PointerTy { + Box, + RPtr(ty::Region), + Uniq +} + +fn ast_ty_to_mt(this: &AC, + rscope: &RS, + ty: &ast::Ty) -> ty::mt { + ty::mt {ty: ast_ty_to_ty(this, rscope, ty), mutbl: ast::MutImmutable} +} + +// Handle `~`, `Box`, and `&` being able to mean strs and vecs. +// If a_seq_ty is a str or a vec, make it a str/vec. +// Also handle first-class trait types. +fn mk_pointer( + this: &AC, + rscope: &RS, + a_seq_ty: &ast::MutTy, + ptr_ty: PointerTy, + constr: |ty::t| -> ty::t) + -> ty::t { + let tcx = this.tcx(); + debug!("mk_pointer(ptr_ty={:?})", ptr_ty); + + match a_seq_ty.ty.node { + ast::TyVec(ty) => { + let mut mt = ast_ty_to_mt(this, rscope, ty); + if a_seq_ty.mutbl == ast::MutMutable { + mt.mutbl = ast::MutMutable; + } + return constr(ty::mk_vec(tcx, mt, None)); + } + ast::TyPath(ref path, ref bounds, id) => { + // Note that the "bounds must be empty if path is not a trait" + // restriction is enforced in the below case for ty_path, which + // will run after this as long as the path isn't a trait. + match tcx.def_map.borrow().find(&id) { + Some(&ast::DefPrimTy(ast::TyStr)) => { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + match ptr_ty { + Uniq => { + return constr(ty::mk_str(tcx)); + } + RPtr(r) => { + return ty::mk_str_slice(tcx, r, ast::MutImmutable); + } + _ => tcx.sess.span_err(path.span, + format!("managed strings are not supported")), + } + } + Some(&ast::DefTrait(trait_def_id)) => { + let result = ast_path_to_trait_ref( + this, rscope, trait_def_id, None, path); + let trait_store = match ptr_ty { + Uniq => ty::UniqTraitStore, + RPtr(r) => { + ty::RegionTraitStore(r, a_seq_ty.mutbl) + } + _ => { + tcx.sess.span_err( + path.span, + "~trait or &trait are the only supported \ + forms of casting-to-trait"); + return ty::mk_err(); + } + }; + let bounds = conv_builtin_bounds(this.tcx(), bounds, trait_store); + return ty::mk_trait(tcx, + result.def_id, + result.substs.clone(), + trait_store, + bounds); + } + _ => {} + } + } + _ => {} + } + + constr(ast_ty_to_ty(this, rscope, a_seq_ty.ty)) +} + // Parses the programmer's textual representation of a type into our // internal notion of a type. pub fn ast_ty_to_ty( this: &AC, rscope: &RS, ast_ty: &ast::Ty) -> ty::t { - enum PointerTy { - Box, - RPtr(ty::Region), - Uniq - } - - fn ast_ty_to_mt(this: &AC, - rscope: &RS, - ty: &ast::Ty) -> ty::mt { - ty::mt {ty: ast_ty_to_ty(this, rscope, ty), mutbl: ast::MutImmutable} - } - - // Handle ~, and & being able to mean strs and vecs. - // If a_seq_ty is a str or a vec, make it a str/vec. - // Also handle first-class trait types. - fn mk_pointer( - this: &AC, - rscope: &RS, - a_seq_ty: &ast::MutTy, - ptr_ty: PointerTy, - constr: |ty::t| -> ty::t) - -> ty::t { - let tcx = this.tcx(); - debug!("mk_pointer(ptr_ty={:?})", ptr_ty); - - match a_seq_ty.ty.node { - ast::TyVec(ty) => { - let mut mt = ast_ty_to_mt(this, rscope, ty); - if a_seq_ty.mutbl == ast::MutMutable { - mt.mutbl = ast::MutMutable; - } - return constr(ty::mk_vec(tcx, mt, None)); - } - ast::TyPath(ref path, ref bounds, id) => { - // Note that the "bounds must be empty if path is not a trait" - // restriction is enforced in the below case for ty_path, which - // will run after this as long as the path isn't a trait. - match tcx.def_map.borrow().find(&id) { - Some(&ast::DefPrimTy(ast::TyStr)) => { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - match ptr_ty { - Uniq => { - return ty::mk_uniq(tcx, ty::mk_str(tcx)); - } - RPtr(r) => { - return ty::mk_str_slice(tcx, r, ast::MutImmutable); - } - _ => tcx.sess.span_err(path.span, - format!("managed strings are not supported")), - } - } - Some(&ast::DefTrait(trait_def_id)) => { - let result = ast_path_to_trait_ref( - this, rscope, trait_def_id, None, path); - let trait_store = match ptr_ty { - Uniq => ty::UniqTraitStore, - RPtr(r) => { - ty::RegionTraitStore(r, a_seq_ty.mutbl) - } - _ => { - tcx.sess.span_err( - path.span, - "~trait or &trait are the only supported \ - forms of casting-to-trait"); - return ty::mk_err(); - } - }; - let bounds = conv_builtin_bounds(this.tcx(), bounds, trait_store); - return ty::mk_trait(tcx, - result.def_id, - result.substs.clone(), - trait_store, - bounds); - } - _ => {} - } - } - _ => {} - } - - constr(ast_ty_to_ty(this, rscope, a_seq_ty.ty)) - } - let tcx = this.tcx(); let mut ast_ty_to_ty_cache = tcx.ast_ty_to_ty_cache.borrow_mut(); @@ -471,7 +549,8 @@ pub fn ast_ty_to_ty( ast_ty_to_ty_cache.insert(ast_ty.id, ty::atttce_unresolved); drop(ast_ty_to_ty_cache); - let typ = ast_ty_to_prim_ty(tcx, ast_ty).unwrap_or_else(|| match ast_ty.node { + let typ = ast_ty_to_builtin_ty(this, rscope, ast_ty).unwrap_or_else(|| { + match ast_ty.node { ast::TyNil => ty::mk_nil(), ast::TyBot => ty::mk_bot(), ast::TyBox(ty) => { @@ -555,7 +634,7 @@ pub fn ast_ty_to_ty( } ast::TyPath(ref path, ref bounds, id) => { let a_def = match tcx.def_map.borrow().find(&id) { - None => tcx.sess.span_fatal( + None => tcx.sess.span_bug( ast_ty.span, format!("unbound path {}", path_to_str(path))), Some(&d) => d }; @@ -639,7 +718,8 @@ pub fn ast_ty_to_ty( // and will not descend into this routine. this.ty_infer(ast_ty.span) } - }); + } + }); tcx.ast_ty_to_ty_cache.borrow_mut().insert(ast_ty.id, ty::atttce_resolved(typ)); return typ; diff --git a/src/libstd/owned.rs b/src/libstd/owned.rs index 1fa86c53117..826ada8f252 100644 --- a/src/libstd/owned.rs +++ b/src/libstd/owned.rs @@ -26,6 +26,14 @@ pub static HEAP: () = (); #[cfg(test)] pub static HEAP: () = (); +/// A type that represents a uniquely-owned value. +#[lang="owned_box"] +#[cfg(not(test))] +pub struct Box(*T); + +#[cfg(test)] +pub struct Box(*T); + #[cfg(not(test))] impl Eq for ~T { #[inline] diff --git a/src/test/run-pass/new-box.rs b/src/test/run-pass/new-box.rs new file mode 100644 index 00000000000..0202695841e --- /dev/null +++ b/src/test/run-pass/new-box.rs @@ -0,0 +1,41 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::owned::Box; + +fn f(x: Box) { + let y: &int = x; + println!("{}", *x); + println!("{}", *y); +} + +trait Trait { + fn printme(&self); +} + +struct Struct; + +impl Trait for Struct { + fn printme(&self) { + println!("hello world!"); + } +} + +fn g(x: Box) { + x.printme(); + let y: &Trait = x; + y.printme(); +} + +fn main() { + f(box 1234); + g(box Struct as Box); +} +