fixes the linking error on bitrig.
use core::prelude::*;
+use heap;
+
use core::any::Any;
use core::cmp::Ordering;
use core::fmt;
use core::hash::{self, Hash};
-use core::marker::Unsize;
+use core::marker::{self, Unsize};
use core::mem;
use core::ops::{CoerceUnsized, Deref, DerefMut};
+use core::ops::{Placer, Boxed, Place, InPlace, BoxPlace};
use core::ptr::Unique;
use core::raw::{TraitObject};
///
/// ```
/// # #![feature(box_heap)]
-/// #![feature(box_syntax)]
+/// #![feature(box_syntax, placement_in_syntax)]
/// use std::boxed::HEAP;
///
/// fn main() {
#[lang = "exchange_heap"]
#[unstable(feature = "box_heap",
reason = "may be renamed; uncertain about custom allocator design")]
-pub const HEAP: () = ();
+pub const HEAP: ExchangeHeapSingleton =
+ ExchangeHeapSingleton { _force_singleton: () };
+
+/// This the singleton type used solely for `boxed::HEAP`.
+#[derive(Copy, Clone)]
+pub struct ExchangeHeapSingleton { _force_singleton: () }
/// A pointer type for heap allocation.
///
#[lang = "owned_box"]
#[stable(feature = "rust1", since = "1.0.0")]
#[fundamental]
-pub struct Box<T>(Unique<T>);
+pub struct Box<T: ?Sized>(Unique<T>);
+
+/// `IntermediateBox` represents uninitialized backing storage for `Box`.
+///
+/// FIXME (pnkfelix): Ideally we would just reuse `Box<T>` instead of
+/// introducing a separate `IntermediateBox<T>`; but then you hit
+/// issues when you e.g. attempt to destructure an instance of `Box`,
+/// since it is a lang item and so it gets special handling by the
+/// compiler. Easier just to make this parallel type for now.
+///
+/// FIXME (pnkfelix): Currently the `box` protocol only supports
+/// creating instances of sized types. This IntermediateBox is
+/// designed to be forward-compatible with a future protocol that
+/// supports creating instances of unsized types; that is why the type
+/// parameter has the `?Sized` generalization marker, and is also why
+/// this carries an explicit size. However, it probably does not need
+/// to carry the explicit alignment; that is just a work-around for
+/// the fact that the `align_of` intrinsic currently requires the
+/// input type to be Sized (which I do not think is strictly
+/// necessary).
+#[unstable(feature = "placement_in", reason = "placement box design is still being worked out.")]
+pub struct IntermediateBox<T: ?Sized>{
+ ptr: *mut u8,
+ size: usize,
+ align: usize,
+ marker: marker::PhantomData<*mut T>,
+}
+
+impl<T> Place<T> for IntermediateBox<T> {
+ fn pointer(&mut self) -> *mut T {
+ unsafe { ::core::mem::transmute(self.ptr) }
+ }
+}
+
+unsafe fn finalize<T>(b: IntermediateBox<T>) -> Box<T> {
+ let p = b.ptr as *mut T;
+ mem::forget(b);
+ mem::transmute(p)
+}
+
+fn make_place<T>() -> IntermediateBox<T> {
+ let size = mem::size_of::<T>();
+ let align = mem::align_of::<T>();
+
+ let p = if size == 0 {
+ heap::EMPTY as *mut u8
+ } else {
+ let p = unsafe {
+ heap::allocate(size, align)
+ };
+ if p.is_null() {
+ panic!("Box make_place allocation failure.");
+ }
+ p
+ };
+
+ IntermediateBox { ptr: p, size: size, align: align, marker: marker::PhantomData }
+}
+
+impl<T> BoxPlace<T> for IntermediateBox<T> {
+ fn make_place() -> IntermediateBox<T> { make_place() }
+}
+
+impl<T> InPlace<T> for IntermediateBox<T> {
+ type Owner = Box<T>;
+ unsafe fn finalize(self) -> Box<T> { finalize(self) }
+}
+
+impl<T> Boxed for Box<T> {
+ type Data = T;
+ type Place = IntermediateBox<T>;
+ unsafe fn finalize(b: IntermediateBox<T>) -> Box<T> { finalize(b) }
+}
+
+impl<T> Placer<T> for ExchangeHeapSingleton {
+ type Place = IntermediateBox<T>;
+
+ fn make_place(self) -> IntermediateBox<T> {
+ make_place()
+ }
+}
+
+impl<T: ?Sized> Drop for IntermediateBox<T> {
+ fn drop(&mut self) {
+ if self.size > 0 {
+ unsafe {
+ heap::deallocate(self.ptr, self.size, self.align)
+ }
+ }
+ }
+}
impl<T> Box<T> {
/// Allocates memory on the heap and then moves `x` into it.
/// let y = x.clone();
/// ```
#[inline]
- fn clone(&self) -> Box<T> { box {(**self).clone()} }
-
+ fn clone(&self) -> Box<T> { box (HEAP) {(**self).clone()} }
/// Copies `source`'s contents into `self` without creating a new allocation.
///
/// # Examples
test(no_crate_inject))]
#![no_std]
+// SNAP d4432b3
+#![allow(unused_features)] // until feature(placement_in_syntax) is in snap
#![feature(allocator)]
#![feature(box_syntax)]
#![feature(coerce_unsized)]
#![feature(no_std)]
#![feature(nonzero)]
#![feature(optin_builtin_traits)]
+#![feature(placement_in_syntax)]
+#![feature(placement_new_protocol)]
#![feature(raw)]
#![feature(staged_api)]
#![feature(unboxed_closures)]
/// elements.
pub fn size_of<T>() -> usize;
+ #[cfg(not(stage0))]
+ /// Moves a value to an uninitialized memory location.
+ ///
+ /// Drop glue is not run on the destination.
+ pub fn move_val_init<T>(dst: *mut T, src: T);
+
+ // SNAP d4432b3
+ #[cfg(stage0)]
/// Moves a value to an uninitialized memory location.
///
/// Drop glue is not run on the destination.
// *const T -> *const U
impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
+
+/// Both `in (PLACE) EXPR` and `box EXPR` desugar into expressions
+/// that allocate an intermediate "place" that holds uninitialized
+/// state. The desugaring evaluates EXPR, and writes the result at
+/// the address returned by the `pointer` method of this trait.
+///
+/// A `Place` can be thought of as a special representation for a
+/// hypothetical `&uninit` reference (which Rust cannot currently
+/// express directly). That is, it represents a pointer to
+/// uninitialized storage.
+///
+/// The client is responsible for two steps: First, initializing the
+/// payload (it can access its address via `pointer`). Second,
+/// converting the agent to an instance of the owning pointer, via the
+/// appropriate `finalize` method (see the `InPlace`.
+///
+/// If evaluating EXPR fails, then the destructor for the
+/// implementation of Place to clean up any intermediate state
+/// (e.g. deallocate box storage, pop a stack, etc).
+#[unstable(feature = "placement_new_protocol")]
+pub trait Place<Data: ?Sized> {
+ /// Returns the address where the input value will be written.
+ /// Note that the data at this address is generally uninitialized,
+ /// and thus one should use `ptr::write` for initializing it.
+ fn pointer(&mut self) -> *mut Data;
+}
+
+/// Interface to implementations of `in (PLACE) EXPR`.
+///
+/// `in (PLACE) EXPR` effectively desugars into:
+///
+/// ```rust,ignore
+/// let p = PLACE;
+/// let mut place = Placer::make_place(p);
+/// let raw_place = Place::pointer(&mut place);
+/// let value = EXPR;
+/// unsafe {
+/// std::ptr::write(raw_place, value);
+/// InPlace::finalize(place)
+/// }
+/// ```
+///
+/// The type of `in (PLACE) EXPR` is derived from the type of `PLACE`;
+/// if the type of `PLACE` is `P`, then the final type of the whole
+/// expression is `P::Place::Owner` (see the `InPlace` and `Boxed`
+/// traits).
+///
+/// Values for types implementing this trait usually are transient
+/// intermediate values (e.g. the return value of `Vec::emplace_back`)
+/// or `Copy`, since the `make_place` method takes `self` by value.
+#[unstable(feature = "placement_new_protocol")]
+pub trait Placer<Data: ?Sized> {
+ /// `Place` is the intermedate agent guarding the
+ /// uninitialized state for `Data`.
+ type Place: InPlace<Data>;
+
+ /// Creates a fresh place from `self`.
+ fn make_place(self) -> Self::Place;
+}
+
+/// Specialization of `Place` trait supporting `in (PLACE) EXPR`.
+#[unstable(feature = "placement_new_protocol")]
+pub trait InPlace<Data: ?Sized>: Place<Data> {
+ /// `Owner` is the type of the end value of `in (PLACE) EXPR`
+ ///
+ /// Note that when `in (PLACE) EXPR` is solely used for
+ /// side-effecting an existing data-structure,
+ /// e.g. `Vec::emplace_back`, then `Owner` need not carry any
+ /// information at all (e.g. it can be the unit type `()` in that
+ /// case).
+ type Owner;
+
+ /// Converts self into the final value, shifting
+ /// deallocation/cleanup responsibilities (if any remain), over to
+ /// the returned instance of `Owner` and forgetting self.
+ unsafe fn finalize(self) -> Self::Owner;
+}
+
+/// Core trait for the `box EXPR` form.
+///
+/// `box EXPR` effectively desugars into:
+///
+/// ```rust,ignore
+/// let mut place = BoxPlace::make_place();
+/// let raw_place = Place::pointer(&mut place);
+/// let value = EXPR;
+/// unsafe {
+/// ::std::ptr::write(raw_place, value);
+/// Boxed::finalize(place)
+/// }
+/// ```
+///
+/// The type of `box EXPR` is supplied from its surrounding
+/// context; in the above expansion, the result type `T` is used
+/// to determine which implementation of `Boxed` to use, and that
+/// `<T as Boxed>` in turn dictates determines which
+/// implementation of `BoxPlace` to use, namely:
+/// `<<T as Boxed>::Place as BoxPlace>`.
+#[unstable(feature = "placement_new_protocol")]
+pub trait Boxed {
+ /// The kind of data that is stored in this kind of box.
+ type Data; /* (`Data` unused b/c cannot yet express below bound.) */
+ /// The place that will negotiate the storage of the data.
+ type Place: BoxPlace<Self::Data>;
+
+ /// Converts filled place into final owning value, shifting
+ /// deallocation/cleanup responsibilities (if any remain), over to
+ /// returned instance of `Self` and forgetting `filled`.
+ unsafe fn finalize(filled: Self::Place) -> Self;
+}
+
+/// Specialization of `Place` trait supporting `box EXPR`.
+#[unstable(feature = "placement_new_protocol")]
+pub trait BoxPlace<Data: ?Sized> : Place<Data> {
+ /// Creates a globally fresh place.
+ fn make_place() -> Self;
+}
//! Enforces the Rust effect system. Currently there is just one effect,
//! `unsafe`.
-use self::UnsafeContext::*;
+use self::RootUnsafeContext::*;
use middle::def;
use middle::ty::{self, Ty};
use syntax::visit;
use syntax::visit::Visitor;
+#[derive(Copy, Clone)]
+struct UnsafeContext {
+ push_unsafe_count: usize,
+ root: RootUnsafeContext,
+}
+
+impl UnsafeContext {
+ fn new(root: RootUnsafeContext) -> UnsafeContext {
+ UnsafeContext { root: root, push_unsafe_count: 0 }
+ }
+}
+
#[derive(Copy, Clone, PartialEq)]
-enum UnsafeContext {
+enum RootUnsafeContext {
SafeContext,
UnsafeFn,
UnsafeBlock(ast::NodeId),
impl<'a, 'tcx> EffectCheckVisitor<'a, 'tcx> {
fn require_unsafe(&mut self, span: Span, description: &str) {
- match self.unsafe_context {
+ if self.unsafe_context.push_unsafe_count > 0 { return; }
+ match self.unsafe_context.root {
SafeContext => {
// Report an error.
span_err!(self.tcx.sess, span, E0133,
let old_unsafe_context = self.unsafe_context;
if is_unsafe_fn {
- self.unsafe_context = UnsafeFn
+ self.unsafe_context = UnsafeContext::new(UnsafeFn)
} else if is_item_fn {
- self.unsafe_context = SafeContext
+ self.unsafe_context = UnsafeContext::new(SafeContext)
}
visit::walk_fn(self, fn_kind, fn_decl, block, span);
// external blocks (e.g. `unsafe { println("") }`,
// expands to `unsafe { ... unsafe { ... } }` where
// the inner one is compiler generated).
- if self.unsafe_context == SafeContext || source == ast::CompilerGenerated {
- self.unsafe_context = UnsafeBlock(block.id)
+ if self.unsafe_context.root == SafeContext || source == ast::CompilerGenerated {
+ self.unsafe_context.root = UnsafeBlock(block.id)
}
}
+ ast::PushUnsafeBlock(..) => {
+ self.unsafe_context.push_unsafe_count =
+ self.unsafe_context.push_unsafe_count.checked_add(1).unwrap();
+ }
+ ast::PopUnsafeBlock(..) => {
+ self.unsafe_context.push_unsafe_count =
+ self.unsafe_context.push_unsafe_count.checked_sub(1).unwrap();
+ }
}
visit::walk_block(self, block);
pub fn check_crate(tcx: &ty::ctxt) {
let mut visitor = EffectCheckVisitor {
tcx: tcx,
- unsafe_context: SafeContext,
+ unsafe_context: UnsafeContext::new(SafeContext),
};
visit::walk_crate(&mut visitor, tcx.map.krate());
None => {}
}
self.consume_expr(&**base);
+ if place.is_some() {
+ self.tcx().sess.span_bug(
+ expr.span,
+ "box with explicit place remains after expansion");
+ }
}
ast::ExprMac(..) => {
fn maybe_do_stability_check(tcx: &ty::ctxt, id: ast::DefId, span: Span,
cb: &mut FnMut(ast::DefId, Span, &Option<&Stability>)) {
- if !is_staged_api(tcx, id) { return }
- if is_internal(tcx, span) { return }
+ if !is_staged_api(tcx, id) {
+ debug!("maybe_do_stability_check: \
+ skipping id={:?} since it is not staged_api", id);
+ return;
+ }
+ if is_internal(tcx, span) {
+ debug!("maybe_do_stability_check: \
+ skipping span={:?} since it is internal", span);
+ return;
+ }
let ref stability = lookup(tcx, id);
+ debug!("maybe_do_stability_check: \
+ inspecting id={:?} span={:?} of stability={:?}", id, span, stability);
cb(id, span, stability);
}
None = 0b0000_0000__0000_0000__0000,
// Things that are interior to the value (first nibble):
- InteriorUnsized = 0b0000_0000__0000_0000__0001,
InteriorUnsafe = 0b0000_0000__0000_0000__0010,
InteriorParam = 0b0000_0000__0000_0000__0100,
// InteriorAll = 0b00000000__00000000__1111,
OwnsDtor = 0b0000_0000__0000_0010__0000,
OwnsAll = 0b0000_0000__1111_1111__0000,
- // Things that are reachable by the value in any way (fourth nibble):
- ReachesBorrowed = 0b0000_0010__0000_0000__0000,
- ReachesMutable = 0b0000_1000__0000_0000__0000,
- ReachesFfiUnsafe = 0b0010_0000__0000_0000__0000,
- ReachesAll = 0b0011_1111__0000_0000__0000,
-
// Things that mean drop glue is necessary
NeedsDrop = 0b0000_0000__0000_0111__0000,
- // Things that prevent values from being considered sized
- Nonsized = 0b0000_0000__0000_0000__0001,
-
// All bits
All = 0b1111_1111__1111_1111__1111
}
self.intersects(TC::OwnsOwned)
}
- pub fn is_sized(&self, _: &ctxt) -> bool {
- !self.intersects(TC::Nonsized)
- }
-
pub fn interior_param(&self) -> bool {
self.intersects(TC::InteriorParam)
}
self.intersects(TC::InteriorUnsafe)
}
- pub fn interior_unsized(&self) -> bool {
- self.intersects(TC::InteriorUnsized)
- }
-
pub fn needs_drop(&self, _: &ctxt) -> bool {
self.intersects(TC::NeedsDrop)
}
/// Includes only those bits that still apply when indirected through a `Box` pointer
pub fn owned_pointer(&self) -> TypeContents {
- TC::OwnsOwned | (
- *self & (TC::OwnsAll | TC::ReachesAll))
- }
-
- /// Includes only those bits that still apply when indirected through a reference (`&`)
- pub fn reference(&self, bits: TypeContents) -> TypeContents {
- bits | (
- *self & TC::ReachesAll)
- }
-
- /// Includes only those bits that still apply when indirected through a raw pointer (`*`)
- pub fn unsafe_pointer(&self) -> TypeContents {
- *self & TC::ReachesAll
+ TC::OwnsOwned | (*self & TC::OwnsAll)
}
pub fn union<T, F>(v: &[T], mut f: F) -> TypeContents where
let result = match ty.sty {
// usize and isize are ffi-unsafe
TyUint(ast::TyUs) | TyInt(ast::TyIs) => {
- TC::ReachesFfiUnsafe
+ TC::None
}
// Scalar and unique types are sendable, and durable
}
TyBox(typ) => {
- TC::ReachesFfiUnsafe | tc_ty(cx, typ, cache).owned_pointer()
+ tc_ty(cx, typ, cache).owned_pointer()
}
- TyTrait(box TraitTy { ref bounds, .. }) => {
- object_contents(bounds) | TC::ReachesFfiUnsafe | TC::Nonsized
+ TyTrait(_) => {
+ TC::All - TC::InteriorParam
}
- TyRawPtr(ref mt) => {
- tc_ty(cx, mt.ty, cache).unsafe_pointer()
+ TyRawPtr(_) => {
+ TC::None
}
- TyRef(r, ref mt) => {
- tc_ty(cx, mt.ty, cache).reference(borrowed_contents(*r, mt.mutbl)) |
- TC::ReachesFfiUnsafe
+ TyRef(_, _) => {
+ TC::None
}
TyArray(ty, _) => {
}
TySlice(ty) => {
- tc_ty(cx, ty, cache) | TC::Nonsized
+ tc_ty(cx, ty, cache)
}
- TyStr => TC::Nonsized,
+ TyStr => TC::None,
TyStruct(did, substs) => {
let flds = cx.struct_fields(did, substs);
let mut res =
TypeContents::union(&flds[..],
- |f| tc_mt(cx, f.mt, cache));
-
- if !cx.lookup_repr_hints(did).contains(&attr::ReprExtern) {
- res = res | TC::ReachesFfiUnsafe;
- }
+ |f| tc_ty(cx, f.mt.ty, cache));
if cx.has_dtor(did) {
res = res | TC::OwnsDtor;
}
TyClosure(did, substs) => {
- // FIXME(#14449): `borrowed_contents` below assumes `&mut` closure.
let param_env = cx.empty_parameter_environment();
let infcx = infer::new_infer_ctxt(cx, &cx.tables, Some(param_env), false);
let upvars = infcx.closure_upvars(did, substs).unwrap();
res = res | TC::OwnsDtor;
}
- if !variants.is_empty() {
- let repr_hints = cx.lookup_repr_hints(did);
- if repr_hints.len() > 1 {
- // this is an error later on, but this type isn't safe
- res = res | TC::ReachesFfiUnsafe;
- }
-
- match repr_hints.get(0) {
- Some(h) => if !h.is_ffi_safe() {
- res = res | TC::ReachesFfiUnsafe;
- },
- // ReprAny
- None => {
- res = res | TC::ReachesFfiUnsafe;
-
- // We allow ReprAny enums if they are eligible for
- // the nullable pointer optimization and the
- // contained type is an `extern fn`
-
- if variants.len() == 2 {
- let mut data_idx = 0;
-
- if variants[0].args.is_empty() {
- data_idx = 1;
- }
-
- if variants[data_idx].args.len() == 1 {
- match variants[data_idx].args[0].sty {
- TyBareFn(..) => { res = res - TC::ReachesFfiUnsafe; }
- _ => { }
- }
- }
- }
- }
- }
- }
-
-
apply_lang_items(cx, did, res)
}
result
}
- fn tc_mt<'tcx>(cx: &ctxt<'tcx>,
- mt: TypeAndMut<'tcx>,
- cache: &mut FnvHashMap<Ty<'tcx>, TypeContents>) -> TypeContents
- {
- let mc = TC::ReachesMutable.when(mt.mutbl == MutMutable);
- mc | tc_ty(cx, mt.ty, cache)
- }
-
fn apply_lang_items(cx: &ctxt, did: ast::DefId, tc: TypeContents)
-> TypeContents {
if Some(did) == cx.lang_items.unsafe_cell_type() {
tc
}
}
-
- /// Type contents due to containing a reference with
- /// the region `region` and borrow kind `bk`.
- fn borrowed_contents(region: ty::Region,
- mutbl: ast::Mutability)
- -> TypeContents {
- let b = match mutbl {
- ast::MutMutable => TC::ReachesMutable,
- ast::MutImmutable => TC::None,
- };
- b | (TC::ReachesBorrowed).when(region != ty::ReStatic)
- }
-
- fn object_contents(bounds: &ExistentialBounds) -> TypeContents {
- // These are the type contents of the (opaque) interior. We
- // make no assumptions (other than that it cannot have an
- // in-scope type parameter within, which makes no sense).
- let mut tc = TC::All - TC::InteriorParam;
- for bound in &bounds.builtin_bounds {
- tc = tc - match bound {
- BoundSync | BoundSend | BoundCopy => TC::None,
- BoundSized => TC::Nonsized,
- };
- }
- return tc;
- }
}
fn impls_bound<'a>(&'tcx self, param_env: &ParameterEnvironment<'a,'tcx>,
result
}
- pub fn is_ffi_safe(&'tcx self, cx: &ctxt<'tcx>) -> bool {
- !self.type_contents(cx).intersects(TC::ReachesFfiUnsafe)
- }
-
// True if instantiating an instance of `r_ty` requires an instance of `r_ty`.
pub fn is_instantiable(&'tcx self, cx: &ctxt<'tcx>) -> bool {
fn type_requires<'tcx>(cx: &ctxt<'tcx>, seen: &mut Vec<DefId>,
executables: true,
morestack: true,
has_rpath: true,
- archive_format: "bsd".to_string(),
+ archive_format: "gnu".to_string(),
.. Default::default()
}
#![allow(deprecated)]
use metadata::{csearch, decoder};
+use middle::{cfg, def, infer, pat_util, stability, traits};
use middle::def::*;
-use middle::infer;
use middle::subst::Substs;
use middle::ty::{self, Ty};
-use middle::traits;
-use middle::{def, pat_util, stability};
use middle::const_eval::{eval_const_expr_partial, ConstVal};
use middle::const_eval::EvalHint::ExprTypeChecked;
-use middle::cfg;
use rustc::ast_map;
-use util::nodemap::{FnvHashMap, NodeSet};
+use util::nodemap::{FnvHashMap, FnvHashSet, NodeSet};
use lint::{Level, Context, LintPass, LintArray, Lint};
use std::collections::{HashSet, BitSet};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::{cmp, slice};
use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
+use std::rc::Rc;
use syntax::{abi, ast};
use syntax::ast_util::{self, is_shift_binop, local_def};
cx: &'a Context<'a, 'tcx>
}
+enum FfiResult {
+ FfiSafe,
+ FfiUnsafe(&'static str),
+ FfiBadStruct(ast::DefId, &'static str),
+ FfiBadEnum(ast::DefId, &'static str)
+}
+
+/// Check if this enum can be safely exported based on the
+/// "nullable pointer optimization". Currently restricted
+/// to function pointers and references, but could be
+/// expanded to cover NonZero raw pointers and newtypes.
+/// FIXME: This duplicates code in trans.
+fn is_repr_nullable_ptr<'tcx>(variants: &Vec<Rc<ty::VariantInfo<'tcx>>>) -> bool {
+ if variants.len() == 2 {
+ let mut data_idx = 0;
+
+ if variants[0].args.is_empty() {
+ data_idx = 1;
+ } else if !variants[1].args.is_empty() {
+ return false;
+ }
+
+ if variants[data_idx].args.len() == 1 {
+ match variants[data_idx].args[0].sty {
+ ty::TyBareFn(None, _) => { return true; }
+ ty::TyRef(..) => { return true; }
+ _ => { }
+ }
+ }
+ }
+ false
+}
+
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
- fn check_def(&mut self, sp: Span, id: ast::NodeId) {
- match self.cx.tcx.def_map.borrow().get(&id).unwrap().full_def() {
- def::DefPrimTy(ast::TyInt(ast::TyIs)) => {
- self.cx.span_lint(IMPROPER_CTYPES, sp,
- "found rust type `isize` in foreign module, while \
- libc::c_int or libc::c_long should be used");
+ /// Check if the given type is "ffi-safe" (has a stable, well-defined
+ /// representation which can be exported to C code).
+ fn check_type_for_ffi(&self,
+ cache: &mut FnvHashSet<Ty<'tcx>>,
+ ty: Ty<'tcx>)
+ -> FfiResult {
+ use self::FfiResult::*;
+ let cx = &self.cx.tcx;
+
+ // Protect against infinite recursion, for example
+ // `struct S(*mut S);`.
+ // FIXME: A recursion limit is necessary as well, for irregular
+ // recusive types.
+ if !cache.insert(ty) {
+ return FfiSafe;
+ }
+
+ match ty.sty {
+ ty::TyStruct(did, substs) => {
+ if !cx.lookup_repr_hints(did).contains(&attr::ReprExtern) {
+ return FfiUnsafe(
+ "found struct without foreign-function-safe \
+ representation annotation in foreign module, \
+ consider adding a #[repr(C)] attribute to \
+ the type");
+ }
+
+ // We can't completely trust repr(C) markings; make sure the
+ // fields are actually safe.
+ let fields = cx.struct_fields(did, substs);
+
+ if fields.is_empty() {
+ return FfiUnsafe(
+ "found zero-size struct in foreign module, consider \
+ adding a member to this struct");
+ }
+
+ for field in fields {
+ let field_ty = infer::normalize_associated_type(cx, &field.mt.ty);
+ let r = self.check_type_for_ffi(cache, field_ty);
+ match r {
+ FfiSafe => {}
+ FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
+ FfiUnsafe(s) => { return FfiBadStruct(did, s); }
+ }
+ }
+ FfiSafe
}
- def::DefPrimTy(ast::TyUint(ast::TyUs)) => {
- self.cx.span_lint(IMPROPER_CTYPES, sp,
- "found rust type `usize` in foreign module, while \
- libc::c_uint or libc::c_ulong should be used");
+ ty::TyEnum(did, substs) => {
+ let variants = cx.substd_enum_variants(did, substs);
+ if variants.is_empty() {
+ // Empty enums are okay... although sort of useless.
+ return FfiSafe
+ }
+
+ // Check for a repr() attribute to specify the size of the
+ // discriminant.
+ let repr_hints = cx.lookup_repr_hints(did);
+ match &**repr_hints {
+ [] => {
+ // Special-case types like `Option<extern fn()>`.
+ if !is_repr_nullable_ptr(&variants) {
+ return FfiUnsafe(
+ "found enum without foreign-function-safe \
+ representation annotation in foreign module, \
+ consider adding a #[repr(...)] attribute to \
+ the type")
+ }
+ }
+ [ref hint] => {
+ if !hint.is_ffi_safe() {
+ // FIXME: This shouldn't be reachable: we should check
+ // this earlier.
+ return FfiUnsafe(
+ "enum has unexpected #[repr(...)] attribute")
+ }
+
+ // Enum with an explicitly sized discriminant; either
+ // a C-style enum or a discriminated union.
+
+ // The layout of enum variants is implicitly repr(C).
+ // FIXME: Is that correct?
+ }
+ _ => {
+ // FIXME: This shouldn't be reachable: we should check
+ // this earlier.
+ return FfiUnsafe(
+ "enum has too many #[repr(...)] attributes");
+ }
+ }
+
+ // Check the contained variants.
+ for variant in variants {
+ for arg in &variant.args {
+ let arg = infer::normalize_associated_type(cx, arg);
+ let r = self.check_type_for_ffi(cache, arg);
+ match r {
+ FfiSafe => {}
+ FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
+ FfiUnsafe(s) => { return FfiBadEnum(did, s); }
+ }
+ }
+ }
+ FfiSafe
+ }
+
+ ty::TyInt(ast::TyIs) => {
+ FfiUnsafe("found Rust type `isize` in foreign module, while \
+ `libc::c_int` or `libc::c_long` should be used")
+ }
+ ty::TyUint(ast::TyUs) => {
+ FfiUnsafe("found Rust type `usize` in foreign module, while \
+ `libc::c_uint` or `libc::c_ulong` should be used")
+ }
+ ty::TyChar => {
+ FfiUnsafe("found Rust type `char` in foreign module, while \
+ `u32` or `libc::wchar_t` should be used")
+ }
+
+ // Primitive types with a stable representation.
+ ty::TyBool | ty::TyInt(..) | ty::TyUint(..) |
+ ty::TyFloat(..) => FfiSafe,
+
+ ty::TyBox(..) => {
+ FfiUnsafe("found Rust type Box<_> in foreign module, \
+ consider using a raw pointer instead")
+ }
+
+ ty::TySlice(_) => {
+ FfiUnsafe("found Rust slice type in foreign module, \
+ consider using a raw pointer instead")
+ }
+
+ ty::TyTrait(..) => {
+ FfiUnsafe("found Rust trait type in foreign module, \
+ consider using a raw pointer instead")
+ }
+
+ ty::TyStr => {
+ FfiUnsafe("found Rust type `str` in foreign module; \
+ consider using a `*const libc::c_char`")
}
- def::DefTy(..) => {
- let tty = match self.cx.tcx.ast_ty_to_ty_cache.borrow().get(&id) {
- Some(&t) => t,
- None => panic!("ast_ty_to_ty_cache was incomplete after typeck!")
- };
- if !tty.is_ffi_safe(self.cx.tcx) {
- self.cx.span_lint(IMPROPER_CTYPES, sp,
- "found type without foreign-function-safe \
- representation annotation in foreign module, consider \
- adding a #[repr(...)] attribute to the type");
+ ty::TyTuple(_) => {
+ FfiUnsafe("found Rust tuple type in foreign module; \
+ consider using a struct instead`")
+ }
+
+ ty::TyRawPtr(ref m) | ty::TyRef(_, ref m) => {
+ self.check_type_for_ffi(cache, m.ty)
+ }
+
+ ty::TyArray(ty, _) => {
+ self.check_type_for_ffi(cache, ty)
+ }
+
+ ty::TyBareFn(None, bare_fn) => {
+ match bare_fn.abi {
+ abi::Rust |
+ abi::RustIntrinsic |
+ abi::RustCall => {
+ return FfiUnsafe(
+ "found function pointer with Rust calling \
+ convention in foreign module; consider using an \
+ `extern` function pointer")
+ }
+ _ => {}
}
+
+ let sig = cx.erase_late_bound_regions(&bare_fn.sig);
+ match sig.output {
+ ty::FnDiverging => {}
+ ty::FnConverging(output) => {
+ if !output.is_nil() {
+ let r = self.check_type_for_ffi(cache, output);
+ match r {
+ FfiSafe => {}
+ _ => { return r; }
+ }
+ }
+ }
+ }
+ for arg in sig.inputs {
+ let r = self.check_type_for_ffi(cache, arg);
+ match r {
+ FfiSafe => {}
+ _ => { return r; }
+ }
+ }
+ FfiSafe
+ }
+
+ ty::TyParam(..) | ty::TyInfer(..) | ty::TyError |
+ ty::TyClosure(..) | ty::TyProjection(..) |
+ ty::TyBareFn(Some(_), _) => {
+ panic!("Unexpected type in foreign function")
+ }
+ }
+ }
+
+ fn check_def(&mut self, sp: Span, id: ast::NodeId) {
+ let tty = match self.cx.tcx.ast_ty_to_ty_cache.borrow().get(&id) {
+ Some(&t) => t,
+ None => panic!("ast_ty_to_ty_cache was incomplete after typeck!")
+ };
+ let tty = infer::normalize_associated_type(self.cx.tcx, &tty);
+
+ match ImproperCTypesVisitor::check_type_for_ffi(self, &mut FnvHashSet(), tty) {
+ FfiResult::FfiSafe => {}
+ FfiResult::FfiUnsafe(s) => {
+ self.cx.span_lint(IMPROPER_CTYPES, sp, s);
+ }
+ FfiResult::FfiBadStruct(_, s) => {
+ // FIXME: This diagnostic is difficult to read, and doesn't
+ // point at the relevant field.
+ self.cx.span_lint(IMPROPER_CTYPES, sp,
+ &format!("found non-foreign-function-safe member in \
+ struct marked #[repr(C)]: {}", s));
+ }
+ FfiResult::FfiBadEnum(_, s) => {
+ // FIXME: This diagnostic is difficult to read, and doesn't
+ // point at the relevant variant.
+ self.cx.span_lint(IMPROPER_CTYPES, sp,
+ &format!("found non-foreign-function-safe member in \
+ enum: {}", s));
}
- _ => ()
}
}
}
impl<'a, 'tcx, 'v> Visitor<'v> for ImproperCTypesVisitor<'a, 'tcx> {
fn visit_ty(&mut self, ty: &ast::Ty) {
- if let ast::TyPath(..) = ty.node {
- self.check_def(ty.span, ty.id);
+ match ty.node {
+ ast::TyPath(..) |
+ ast::TyBareFn(..) => self.check_def(ty.span, ty.id),
+ ast::TyVec(..) => {
+ self.cx.span_lint(IMPROPER_CTYPES, ty.span,
+ "found Rust slice type in foreign module, consider \
+ using a raw pointer instead");
+ }
+ ast::TyFixedLengthVec(ref ty, _) => self.visit_ty(ty),
+ ast::TyTup(..) => {
+ self.cx.span_lint(IMPROPER_CTYPES, ty.span,
+ "found Rust tuple type in foreign module; \
+ consider using a struct instead`")
+ }
+ _ => visit::walk_ty(self, ty)
}
- visit::walk_ty(self, ty);
}
}
#![feature(ref_slice)]
#![feature(rustc_diagnostic_macros)]
#![feature(rustc_private)]
+#![feature(slice_patterns)]
#![feature(staged_api)]
#![feature(str_char)]
pub fn LLVMGetArrayLength(ArrayTy: TypeRef) -> c_uint;
pub fn LLVMGetPointerAddressSpace(PointerTy: TypeRef) -> c_uint;
pub fn LLVMGetPointerToGlobal(EE: ExecutionEngineRef, V: ValueRef)
- -> *const ();
+ -> *const c_void;
pub fn LLVMGetVectorSize(VectorTy: TypeRef) -> c_uint;
/* Operations on other types */
use llvm::{ModuleRef, TargetMachineRef, True, False};
use rustc::metadata::cstore;
use rustc::util::common::time;
+use back::write::{ModuleConfig, with_llvm_pmb};
use libc;
use flate;
use std::ffi::CString;
pub fn run(sess: &session::Session, llmod: ModuleRef,
- tm: TargetMachineRef, reachable: &[String]) {
+ tm: TargetMachineRef, reachable: &[String],
+ config: &ModuleConfig) {
if sess.opts.cg.prefer_dynamic {
sess.err("cannot prefer dynamic linking when performing LTO");
sess.note("only 'staticlib' and 'bin' outputs are supported with LTO");
llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod);
llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const _);
- let opt = match sess.opts.optimize {
- config::No => 0,
- config::Less => 1,
- config::Default => 2,
- config::Aggressive => 3,
- };
-
- let builder = llvm::LLVMPassManagerBuilderCreate();
- llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt);
- llvm::LLVMPassManagerBuilderPopulateLTOPassManager(builder, pm,
- /* Internalize = */ False,
- /* RunInliner = */ True);
- llvm::LLVMPassManagerBuilderDispose(builder);
+ with_llvm_pmb(llmod, config, &mut |b| {
+ llvm::LLVMPassManagerBuilderPopulateLTOPassManager(b, pm,
+ /* Internalize = */ False,
+ /* RunInliner = */ True);
+ });
llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const _);
use std::os::windows::prelude::*;
use std::ops::RangeFrom;
use libc::{DWORD, LPCWSTR, LONG, LPDWORD, LPBYTE, ERROR_SUCCESS};
+use libc::c_void;
const HKEY_LOCAL_MACHINE: HKEY = 0x80000002 as HKEY;
const KEY_WOW64_32KEY: REGSAM = 0x0200;
pub type PHKEY = *mut HKEY;
pub type REGSAM = DWORD;
pub type LPWSTR = *mut u16;
-pub type PFILETIME = *mut ();
+pub type PFILETIME = *mut c_void;
#[link(name = "advapi32")]
extern "system" {
/// Module-specific configuration for `optimize_and_codegen`.
#[derive(Clone)]
-struct ModuleConfig {
+pub struct ModuleConfig {
/// LLVM TargetMachine to use for codegen.
tm: TargetMachineRef,
/// Names of additional optimization passes to run.
llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr());
}
- match config.opt_level {
- Some(opt_level) => {
- // Create the two optimizing pass managers. These mirror what clang
- // does, and are by populated by LLVM's default PassManagerBuilder.
- // Each manager has a different set of passes, but they also share
- // some common passes.
- let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod);
- let mpm = llvm::LLVMCreatePassManager();
-
- // If we're verifying or linting, add them to the function pass
- // manager.
- let addpass = |pass: &str| {
- let pass = CString::new(pass).unwrap();
- llvm::LLVMRustAddPass(fpm, pass.as_ptr())
- };
+ if config.opt_level.is_some() {
+ // Create the two optimizing pass managers. These mirror what clang
+ // does, and are by populated by LLVM's default PassManagerBuilder.
+ // Each manager has a different set of passes, but they also share
+ // some common passes.
+ let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod);
+ let mpm = llvm::LLVMCreatePassManager();
+
+ // If we're verifying or linting, add them to the function pass
+ // manager.
+ let addpass = |pass: &str| {
+ let pass = CString::new(pass).unwrap();
+ llvm::LLVMRustAddPass(fpm, pass.as_ptr())
+ };
- if !config.no_verify { assert!(addpass("verify")); }
- if !config.no_prepopulate_passes {
- llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
- llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
- populate_llvm_passes(fpm, mpm, llmod, opt_level, &config);
- }
+ if !config.no_verify { assert!(addpass("verify")); }
+ if !config.no_prepopulate_passes {
+ llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
+ llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
+ with_llvm_pmb(llmod, &config, &mut |b| {
+ llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm);
+ llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm);
+ })
+ }
- for pass in &config.passes {
- if !addpass(pass) {
- cgcx.handler.warn(&format!("unknown pass `{}`, ignoring",
- pass));
- }
+ for pass in &config.passes {
+ if !addpass(pass) {
+ cgcx.handler.warn(&format!("unknown pass `{}`, ignoring",
+ pass));
}
+ }
- for pass in &cgcx.plugin_passes {
- if !addpass(pass) {
- cgcx.handler.err(&format!("a plugin asked for LLVM pass \
- `{}` but LLVM does not \
- recognize it", pass));
- }
+ for pass in &cgcx.plugin_passes {
+ if !addpass(pass) {
+ cgcx.handler.err(&format!("a plugin asked for LLVM pass \
+ `{}` but LLVM does not \
+ recognize it", pass));
}
+ }
- cgcx.handler.abort_if_errors();
+ cgcx.handler.abort_if_errors();
- // Finally, run the actual optimization passes
- time(config.time_passes, "llvm function passes", (), |()|
- llvm::LLVMRustRunFunctionPassManager(fpm, llmod));
- time(config.time_passes, "llvm module passes", (), |()|
- llvm::LLVMRunPassManager(mpm, llmod));
+ // Finally, run the actual optimization passes
+ time(config.time_passes, "llvm function passes", (), |()|
+ llvm::LLVMRustRunFunctionPassManager(fpm, llmod));
+ time(config.time_passes, "llvm module passes", (), |()|
+ llvm::LLVMRunPassManager(mpm, llmod));
- // Deallocate managers that we're now done with
- llvm::LLVMDisposePassManager(fpm);
- llvm::LLVMDisposePassManager(mpm);
+ // Deallocate managers that we're now done with
+ llvm::LLVMDisposePassManager(fpm);
+ llvm::LLVMDisposePassManager(mpm);
- match cgcx.lto_ctxt {
- Some((sess, reachable)) if sess.lto() => {
- time(sess.time_passes(), "all lto passes", (), |()|
- lto::run(sess, llmod, tm, reachable));
+ match cgcx.lto_ctxt {
+ Some((sess, reachable)) if sess.lto() => {
+ time(sess.time_passes(), "all lto passes", (), |()|
+ lto::run(sess, llmod, tm, reachable, &config));
- if config.emit_lto_bc {
- let name = format!("{}.lto.bc", name_extra);
- let out = output_names.with_extension(&name);
- let out = path2cstr(&out);
- llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr());
- }
- },
- _ => {},
- }
- },
- None => {},
+ if config.emit_lto_bc {
+ let name = format!("{}.lto.bc", name_extra);
+ let out = output_names.with_extension(&name);
+ let out = path2cstr(&out);
+ llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr());
+ }
+ },
+ _ => {},
+ }
}
// A codegen-specific pass manager is used to generate object
llvm_args.as_ptr());
}
-unsafe fn populate_llvm_passes(fpm: llvm::PassManagerRef,
- mpm: llvm::PassManagerRef,
- llmod: ModuleRef,
- opt: llvm::CodeGenOptLevel,
- config: &ModuleConfig) {
+pub unsafe fn with_llvm_pmb(llmod: ModuleRef,
+ config: &ModuleConfig,
+ f: &mut FnMut(llvm::PassManagerBuilderRef)) {
// Create the PassManagerBuilder for LLVM. We configure it with
// reasonable defaults and prepare it to actually populate the pass
// manager.
let builder = llvm::LLVMPassManagerBuilderCreate();
+ let opt = config.opt_level.unwrap_or(llvm::CodeGenLevelNone);
llvm::LLVMRustConfigurePassManagerBuilder(builder, opt,
config.merge_functions,
}
}
- // Use the builder to populate the function/module pass managers.
- llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(builder, fpm);
- llvm::LLVMPassManagerBuilderPopulateModulePassManager(builder, mpm);
+ f(builder);
llvm::LLVMPassManagerBuilderDispose(builder);
}
// FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow?
// FIXME(nagisa): investigate whether it can be changed into define_global
let c = declare::declare_global(ccx, &name[..], ty);
+
// Thread-local statics in some other crate need to *always* be linked
// against in a thread-local fashion, so we need to be sure to apply the
// thread-local attribute locally if it was present remotely. If we
llvm::set_thread_local(c, true);
}
}
- if ccx.use_dll_storage_attrs() {
+
+ // MSVC is a little ornery about how items are imported across dlls, and for
+ // lots more info on dllimport/dllexport see the large comment in
+ // SharedCrateContext::new. Unfortunately, unlike functions, statics
+ // imported from dlls *must* be tagged with dllimport (if you forget
+ // dllimport on a function then the linker fixes it up with an injected
+ // shim). This means that to link correctly to an upstream Rust dynamic
+ // library we need to make sure its statics are tagged with dllimport.
+ //
+ // Hence, if this translation is using dll storage attributes and the crate
+ // that this const originated from is being imported as a dylib at some
+ // point we tag this with dllimport.
+ //
+ // Note that this is not 100% correct for a variety of reasons:
+ //
+ // 1. If we are producing an rlib and linking to an upstream rlib, we'll
+ // omit the dllimport. It's a possibility, though, that some later
+ // downstream compilation will link the same upstream dependency as a
+ // dylib and use our rlib, causing linker errors because we didn't use
+ // dllimport.
+ // 2. We may have multiple crate output types. For example if we are
+ // emitting a statically linked binary as well as a dynamic library we'll
+ // want to omit dllimport for the binary but we need to have it for the
+ // dylib.
+ //
+ // For most every day uses, however, this should suffice. During the
+ // bootstrap we're almost always linking upstream to a dylib for some crate
+ // type output, so most imports will be tagged with dllimport (somewhat
+ // appropriately). Otherwise rust dylibs linking against rust dylibs is
+ // pretty rare in Rust so this will likely always be `false` and we'll never
+ // tag with dllimport.
+ //
+ // Note that we can't just blindly tag all constants with dllimport as can
+ // cause linkage errors when we're not actually linking against a dll. For
+ // more info on this see rust-lang/rust#26591.
+ if ccx.use_dll_storage_attrs() && ccx.upstream_dylib_used(did.krate) {
llvm::SetDLLStorageClass(c, llvm::DLLImportStorageClass);
}
ccx.externs().borrow_mut().insert(name.to_string(), c);
use llvm;
use llvm::{ContextRef, ModuleRef, ValueRef, BuilderRef};
use metadata::common::LinkMeta;
+use metadata::cstore;
use middle::def::ExportMap;
use middle::traits;
use trans::adt;
pub fn use_dll_storage_attrs(&self) -> bool {
self.shared.use_dll_storage_attrs()
}
+
+ /// Tests whether the given `krate` (an upstream crate) is ever used as a
+ /// dynamic library for the final linkage of this crate.
+ pub fn upstream_dylib_used(&self, krate: ast::CrateNum) -> bool {
+ let tcx = self.tcx();
+ let formats = tcx.dependency_formats.borrow();
+ tcx.sess.crate_types.borrow().iter().any(|ct| {
+ match formats[ct].get(krate as usize - 1) {
+ // If a crate is explicitly linked dynamically then we're
+ // definitely using it dynamically. If it's not being linked
+ // then currently that means it's being included through another
+ // dynamic library, so we're including it dynamically.
+ Some(&Some(cstore::RequireDynamic)) |
+ Some(&None) => true,
+
+ // Static linkage isn't included dynamically and if there's not
+ // an entry in the array then this crate type isn't actually
+ // doing much linkage so there's nothing dynamic going on.
+ Some(&Some(cstore::RequireStatic)) |
+ None => false,
+ }
+ })
+ }
}
/// Declare any llvm intrinsics that you might need
pub struct UnsafetyState {
pub def: ast::NodeId,
pub unsafety: ast::Unsafety,
+ pub unsafe_push_count: u32,
from_fn: bool
}
impl UnsafetyState {
pub fn function(unsafety: ast::Unsafety, def: ast::NodeId) -> UnsafetyState {
- UnsafetyState { def: def, unsafety: unsafety, from_fn: true }
+ UnsafetyState { def: def, unsafety: unsafety, unsafe_push_count: 0, from_fn: true }
}
pub fn recurse(&mut self, blk: &ast::Block) -> UnsafetyState {
ast::Unsafety::Unsafe if self.from_fn => *self,
unsafety => {
- let (unsafety, def) = match blk.rules {
- ast::UnsafeBlock(..) => (ast::Unsafety::Unsafe, blk.id),
- ast::DefaultBlock => (unsafety, self.def),
+ let (unsafety, def, count) = match blk.rules {
+ ast::PushUnsafeBlock(..) =>
+ (unsafety, blk.id, self.unsafe_push_count.checked_add(1).unwrap()),
+ ast::PopUnsafeBlock(..) =>
+ (unsafety, blk.id, self.unsafe_push_count.checked_sub(1).unwrap()),
+ ast::UnsafeBlock(..) =>
+ (ast::Unsafety::Unsafe, blk.id, self.unsafe_push_count),
+ ast::DefaultBlock =>
+ (unsafety, self.def, self.unsafe_push_count),
};
UnsafetyState{ def: def,
- unsafety: unsafety,
- from_fn: false }
+ unsafety: unsafety,
+ unsafe_push_count: count,
+ from_fn: false }
}
}
}
"move_val_init" => {
(1,
vec!(
- tcx.mk_mut_ref(tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
- ty::BrAnon(0))),
- param(ccx, 0)),
+ tcx.mk_mut_ptr(param(ccx, 0)),
param(ccx, 0)
),
tcx.mk_nil())
test(no_crate_inject, attr(deny(warnings))),
test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))))]
+#![cfg_attr(stage0, allow(unused_features))]
#![feature(alloc)]
#![feature(allow_internal_unstable)]
#![feature(associated_consts)]
#![feature(no_std)]
#![feature(oom)]
#![feature(optin_builtin_traits)]
+#![feature(placement_in_syntax)]
#![feature(rand)]
#![feature(raw)]
#![feature(reflect_marker)]
use rt::libunwind as uw;
use libc::{c_void, c_int};
- #[repr(C)]
- pub struct EXCEPTION_RECORD;
- #[repr(C)]
- pub struct CONTEXT;
- #[repr(C)]
- pub struct DISPATCHER_CONTEXT;
+ // Fake definitions; these are actually complicated structs,
+ // but we don't use the contents here.
+ pub type EXCEPTION_RECORD = c_void;
+ pub type CONTEXT = c_void;
+ pub type DISPATCHER_CONTEXT = c_void;
#[repr(C)]
#[derive(Copy, Clone)]
Handler { _data: 0 as *mut libc::c_void }
}
+#[repr(C)]
pub struct EXCEPTION_RECORD {
pub ExceptionCode: DWORD,
pub ExceptionFlags: DWORD,
pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS]
}
+#[repr(C)]
pub struct EXCEPTION_POINTERS {
pub ExceptionRecord: *mut EXCEPTION_RECORD,
pub ContextRecord: LPVOID
#[linkage = "extern_weak"]
static __dso_handle: *mut u8;
#[linkage = "extern_weak"]
- static __cxa_thread_atexit_impl: *const ();
+ static __cxa_thread_atexit_impl: *const libc::c_void;
}
if !__cxa_thread_atexit_impl.is_null() {
type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8),
arg: *mut u8,
dso_handle: *mut u8) -> libc::c_int;
- mem::transmute::<*const (), F>(__cxa_thread_atexit_impl)
+ mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)
(dtor, t, &__dso_handle as *const _ as *mut _);
return
}
pub enum BlockCheckMode {
DefaultBlock,
UnsafeBlock(UnsafeSource),
+ PushUnsafeBlock(UnsafeSource),
+ PopUnsafeBlock(UnsafeSource),
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
mac_span.lo <= span.lo && span.hi <= mac_span.hi
});
+ debug!("span_allows_unstable: span: {:?} call_site: {:?} callee: {:?}",
+ (span.lo, span.hi),
+ (info.call_site.lo, info.call_site.hi),
+ info.callee.span.map(|x| (x.lo, x.hi)));
debug!("span_allows_unstable: from this expansion? {}, allows unstable? {}",
span_comes_from_this_expansion,
info.callee.allow_internal_unstable);
syntax_expanders.insert(intern("cfg"),
builtin_normal_expander(
ext::cfg::expand_cfg));
+ syntax_expanders.insert(intern("push_unsafe"),
+ builtin_normal_expander(
+ ext::pushpop_safe::expand_push_unsafe));
+ syntax_expanders.insert(intern("pop_unsafe"),
+ builtin_normal_expander(
+ ext::pushpop_safe::expand_pop_unsafe));
syntax_expanders.insert(intern("trace_macros"),
builtin_normal_expander(
ext::trace_macros::expand_trace_macros));
use visit::Visitor;
use std_inject;
+// Given suffix ["b","c","d"], returns path `::std::b::c::d` when
+// `fld.cx.use_std`, and `::core::b::c::d` otherwise.
+fn mk_core_path(fld: &mut MacroExpander,
+ span: Span,
+ suffix: &[&'static str]) -> ast::Path {
+ let mut idents = vec![fld.cx.ident_of_std("core")];
+ for s in suffix.iter() { idents.push(fld.cx.ident_of(*s)); }
+ fld.cx.path_global(span, idents)
+}
+
pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
fn push_compiler_expansion(fld: &mut MacroExpander, span: Span, expansion_desc: &str) {
fld.cx.bt_push(ExpnInfo {
callee: NameAndSpan {
name: expansion_desc.to_string(),
format: CompilerExpansion,
+
+ // This does *not* mean code generated after
+ // `push_compiler_expansion` is automatically exempt
+ // from stability lints; must also tag such code with
+ // an appropriate span from `fld.cx.backtrace()`.
allow_internal_unstable: true,
+
span: None,
},
});
}
- e.and_then(|ast::Expr {id, node, span}| match node {
+ // Sets the expn_id so that we can use unstable methods.
+ fn allow_unstable(fld: &mut MacroExpander, span: Span) -> Span {
+ Span { expn_id: fld.cx.backtrace(), ..span }
+ }
+
+ let expr_span = e.span;
+ return e.and_then(|ast::Expr {id, node, span}| match node {
+
// expr_mac should really be expr_ext or something; it's the
// entry-point for all syntax extensions.
ast::ExprMac(mac) => {
})
}
+ // Desugar ExprBox: `in (PLACE) EXPR`
+ ast::ExprBox(Some(placer), value_expr) => {
+ // to:
+ //
+ // let p = PLACE;
+ // let mut place = Placer::make_place(p);
+ // let raw_place = Place::pointer(&mut place);
+ // push_unsafe!({
+ // std::intrinsics::move_val_init(raw_place, pop_unsafe!( EXPR ));
+ // InPlace::finalize(place)
+ // })
+
+ // Ensure feature-gate is enabled
+ feature_gate::check_for_placement_in(
+ fld.cx.ecfg.features,
+ &fld.cx.parse_sess.span_diagnostic,
+ expr_span);
+
+ push_compiler_expansion(fld, expr_span, "placement-in expansion");
+
+ let value_span = value_expr.span;
+ let placer_span = placer.span;
+
+ let placer_expr = fld.fold_expr(placer);
+ let value_expr = fld.fold_expr(value_expr);
+
+ let placer_ident = token::gensym_ident("placer");
+ let agent_ident = token::gensym_ident("place");
+ let p_ptr_ident = token::gensym_ident("p_ptr");
+
+ let placer = fld.cx.expr_ident(span, placer_ident);
+ let agent = fld.cx.expr_ident(span, agent_ident);
+ let p_ptr = fld.cx.expr_ident(span, p_ptr_ident);
+
+ let make_place = ["ops", "Placer", "make_place"];
+ let place_pointer = ["ops", "Place", "pointer"];
+ let move_val_init = ["intrinsics", "move_val_init"];
+ let inplace_finalize = ["ops", "InPlace", "finalize"];
+
+ let make_call = |fld: &mut MacroExpander, p, args| {
+ // We feed in the `expr_span` because codemap's span_allows_unstable
+ // allows the call_site span to inherit the `allow_internal_unstable`
+ // setting.
+ let span_unstable = allow_unstable(fld, expr_span);
+ let path = mk_core_path(fld, span_unstable, p);
+ let path = fld.cx.expr_path(path);
+ let expr_span_unstable = allow_unstable(fld, span);
+ fld.cx.expr_call(expr_span_unstable, path, args)
+ };
+
+ let stmt_let = |fld: &mut MacroExpander, bind, expr| {
+ fld.cx.stmt_let(placer_span, false, bind, expr)
+ };
+ let stmt_let_mut = |fld: &mut MacroExpander, bind, expr| {
+ fld.cx.stmt_let(placer_span, true, bind, expr)
+ };
+
+ // let placer = <placer_expr> ;
+ let s1 = stmt_let(fld, placer_ident, placer_expr);
+
+ // let mut place = Placer::make_place(placer);
+ let s2 = {
+ let call = make_call(fld, &make_place, vec![placer]);
+ stmt_let_mut(fld, agent_ident, call)
+ };
+
+ // let p_ptr = Place::pointer(&mut place);
+ let s3 = {
+ let args = vec![fld.cx.expr_mut_addr_of(placer_span, agent.clone())];
+ let call = make_call(fld, &place_pointer, args);
+ stmt_let(fld, p_ptr_ident, call)
+ };
+
+ // pop_unsafe!(EXPR));
+ let pop_unsafe_expr = pop_unsafe_expr(fld.cx, value_expr, value_span);
+
+ // push_unsafe!({
+ // ptr::write(p_ptr, pop_unsafe!(<value_expr>));
+ // InPlace::finalize(place)
+ // })
+ let expr = {
+ let call_move_val_init = StmtSemi(make_call(
+ fld, &move_val_init, vec![p_ptr, pop_unsafe_expr]), ast::DUMMY_NODE_ID);
+ let call_move_val_init = codemap::respan(value_span, call_move_val_init);
+
+ let call = make_call(fld, &inplace_finalize, vec![agent]);
+ Some(push_unsafe_expr(fld.cx, vec![P(call_move_val_init)], call, span))
+ };
+
+ let block = fld.cx.block_all(span, vec![s1, s2, s3], expr);
+ let result = fld.cx.expr_block(block);
+ fld.cx.bt_pop();
+ result
+ }
+
+ // Issue #22181:
+ // Eventually a desugaring for `box EXPR`
+ // (similar to the desugaring above for `in PLACE BLOCK`)
+ // should go here, desugaring
+ //
+ // to:
+ //
+ // let mut place = BoxPlace::make_place();
+ // let raw_place = Place::pointer(&mut place);
+ // let value = $value;
+ // unsafe {
+ // ::std::ptr::write(raw_place, value);
+ // Boxed::finalize(place)
+ // }
+ //
+ // But for now there are type-inference issues doing that.
+
ast::ExprWhile(cond, body, opt_ident) => {
let cond = fld.fold_expr(cond);
let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
span: span
}, fld))
}
- })
+ });
+
+ fn push_unsafe_expr(cx: &mut ExtCtxt, stmts: Vec<P<ast::Stmt>>,
+ expr: P<ast::Expr>, span: Span)
+ -> P<ast::Expr> {
+ let rules = ast::PushUnsafeBlock(ast::CompilerGenerated);
+ cx.expr_block(P(ast::Block {
+ rules: rules, span: span, id: ast::DUMMY_NODE_ID,
+ stmts: stmts, expr: Some(expr),
+ }))
+ }
+
+ fn pop_unsafe_expr(cx: &mut ExtCtxt, expr: P<ast::Expr>, span: Span)
+ -> P<ast::Expr> {
+ let rules = ast::PopUnsafeBlock(ast::CompilerGenerated);
+ cx.expr_block(P(ast::Block {
+ rules: rules, span: span, id: ast::DUMMY_NODE_ID,
+ stmts: vec![], expr: Some(expr),
+ }))
+ }
}
/// Expand a (not-ident-style) macro invocation. Returns the result
fn enable_trace_macros = allow_trace_macros,
fn enable_allow_internal_unstable = allow_internal_unstable,
fn enable_custom_derive = allow_custom_derive,
+ fn enable_pushpop_unsafe = allow_pushpop_unsafe,
}
}
--- /dev/null
+// Copyright 2015 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*
+ * The compiler code necessary to support the `push_unsafe!` and
+ * `pop_unsafe!` macros.
+ *
+ * This is a hack to allow a kind of "safety hygiene", where a macro
+ * can generate code with an interior expression that inherits the
+ * safety of some outer context.
+ *
+ * For example, in:
+ *
+ * ```rust
+ * fn foo() { push_unsafe!( { EXPR_1; pop_unsafe!( EXPR_2 ) } ) }
+ * ```
+ *
+ * the `EXPR_1` is considered to be in an `unsafe` context,
+ * but `EXPR_2` is considered to be in a "safe" (i.e. checked) context.
+ *
+ * For comparison, in:
+ *
+ * ```rust
+ * fn foo() { unsafe { push_unsafe!( { EXPR_1; pop_unsafe!( EXPR_2 ) } ) } }
+ * ```
+ *
+ * both `EXPR_1` and `EXPR_2` are considered to be in `unsafe`
+ * contexts.
+ *
+ */
+
+use ast;
+use codemap::Span;
+use ext::base::*;
+use ext::base;
+use ext::build::AstBuilder;
+use feature_gate;
+use ptr::P;
+
+enum PushPop { Push, Pop }
+
+pub fn expand_push_unsafe<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+ -> Box<base::MacResult+'cx> {
+ expand_pushpop_unsafe(cx, sp, tts, PushPop::Push)
+}
+
+pub fn expand_pop_unsafe<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+ -> Box<base::MacResult+'cx> {
+ expand_pushpop_unsafe(cx, sp, tts, PushPop::Pop)
+}
+
+fn expand_pushpop_unsafe<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree],
+ pp: PushPop) -> Box<base::MacResult+'cx> {
+ feature_gate::check_for_pushpop_syntax(
+ cx.ecfg.features, &cx.parse_sess.span_diagnostic, sp);
+
+ let mut exprs = match get_exprs_from_tts(cx, sp, tts) {
+ Some(exprs) => exprs.into_iter(),
+ None => return DummyResult::expr(sp),
+ };
+
+ let expr = match (exprs.next(), exprs.next()) {
+ (Some(expr), None) => expr,
+ _ => {
+ let msg = match pp {
+ PushPop::Push => "push_unsafe! takes 1 arguments",
+ PushPop::Pop => "pop_unsafe! takes 1 arguments",
+ };
+ cx.span_err(sp, msg);
+ return DummyResult::expr(sp);
+ }
+ };
+
+ let source = ast::UnsafeSource::CompilerGenerated;
+ let check_mode = match pp {
+ PushPop::Push => ast::BlockCheckMode::PushUnsafeBlock(source),
+ PushPop::Pop => ast::BlockCheckMode::PopUnsafeBlock(source),
+ };
+
+ MacEager::expr(cx.expr_block(P(ast::Block {
+ stmts: vec![],
+ expr: Some(expr),
+ id: ast::DUMMY_NODE_ID,
+ rules: check_mode,
+ span: sp
+ })))
+}
("visible_private_types", "1.0.0", Active),
("slicing_syntax", "1.0.0", Accepted),
("box_syntax", "1.0.0", Active),
+ ("placement_in_syntax", "1.0.0", Active),
+ ("pushpop_unsafe", "1.2.0", Active),
("on_unimplemented", "1.0.0", Active),
("simd_ffi", "1.0.0", Active),
("allocator", "1.0.0", Active),
pub allow_trace_macros: bool,
pub allow_internal_unstable: bool,
pub allow_custom_derive: bool,
+ pub allow_placement_in: bool,
+ pub allow_box: bool,
+ pub allow_pushpop_unsafe: bool,
pub simd_ffi: bool,
pub unmarked_api: bool,
pub negate_unsigned: bool,
allow_trace_macros: false,
allow_internal_unstable: false,
allow_custom_derive: false,
+ allow_placement_in: false,
+ allow_box: false,
+ allow_pushpop_unsafe: false,
simd_ffi: false,
unmarked_api: false,
negate_unsigned: false,
}
}
+const EXPLAIN_BOX_SYNTAX: &'static str =
+ "box expression syntax is experimental; you can call `Box::new` instead.";
+
+const EXPLAIN_PLACEMENT_IN: &'static str =
+ "placement-in expression syntax is experimental and subject to change.";
+
+const EXPLAIN_PUSHPOP_UNSAFE: &'static str =
+ "push/pop_unsafe macros are experimental and subject to change.";
+
+pub fn check_for_box_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) {
+ if let Some(&Features { allow_box: true, .. }) = f {
+ return;
+ }
+ emit_feature_err(diag, "box_syntax", span, EXPLAIN_BOX_SYNTAX);
+}
+
+pub fn check_for_placement_in(f: Option<&Features>, diag: &SpanHandler, span: Span) {
+ if let Some(&Features { allow_placement_in: true, .. }) = f {
+ return;
+ }
+ emit_feature_err(diag, "placement_in_syntax", span, EXPLAIN_PLACEMENT_IN);
+}
+
+pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) {
+ if let Some(&Features { allow_pushpop_unsafe: true, .. }) = f {
+ return;
+ }
+ emit_feature_err(diag, "pushpop_unsafe", span, EXPLAIN_PUSHPOP_UNSAFE);
+}
+
struct Context<'a> {
features: Vec<&'static str>,
span_handler: &'a SpanHandler,
}
impl<'a> Context<'a> {
+ fn enable_feature(&mut self, feature: &'static str) {
+ debug!("enabling feature: {}", feature);
+ self.features.push(feature);
+ }
+
fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
let has_feature = self.has_feature(feature);
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_feature);
fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
self.context.check_attribute(attr, true);
}
+
+ fn visit_expr(&mut self, e: &ast::Expr) {
+ // Issue 22181: overloaded-`box` and placement-`in` are
+ // implemented via a desugaring expansion, so their feature
+ // gates go into MacroVisitor since that works pre-expansion.
+ //
+ // Issue 22234: we also check during expansion as well.
+ // But we keep these checks as a pre-expansion check to catch
+ // uses in e.g. conditionalized code.
+
+ if let ast::ExprBox(None, _) = e.node {
+ self.context.gate_feature("box_syntax", e.span, EXPLAIN_BOX_SYNTAX);
+ }
+
+ if let ast::ExprBox(Some(_), _) = e.node {
+ self.context.gate_feature("placement_in_syntax", e.span, EXPLAIN_PLACEMENT_IN);
+ }
+
+ visit::walk_expr(self, e);
+ }
}
struct PostExpansionVisitor<'a> {
match KNOWN_FEATURES.iter()
.find(|& &(n, _, _)| name == n) {
Some(&(name, _, Active)) => {
- cx.features.push(name);
+ cx.enable_feature(name);
}
Some(&(_, _, Removed)) => {
span_handler.span_err(mi.span, "feature has been removed");
allow_trace_macros: cx.has_feature("trace_macros"),
allow_internal_unstable: cx.has_feature("allow_internal_unstable"),
allow_custom_derive: cx.has_feature("custom_derive"),
+ allow_placement_in: cx.has_feature("placement_in_syntax"),
+ allow_box: cx.has_feature("box_syntax"),
+ allow_pushpop_unsafe: cx.has_feature("pushpop_unsafe"),
simd_ffi: cx.has_feature("simd_ffi"),
unmarked_api: cx.has_feature("unmarked_api"),
negate_unsigned: cx.has_feature("negate_unsigned"),
pub mod log_syntax;
pub mod mtwt;
pub mod quote;
+ pub mod pushpop_safe;
pub mod source_util;
pub mod trace_macros;
ex = ExprAddrOf(m, e);
}
token::Ident(_, _) => {
- if !self.check_keyword(keywords::Box) {
+ if !self.check_keyword(keywords::Box) && !self.check_keyword(keywords::In) {
return self.parse_dot_or_call_expr();
}
let lo = self.span.lo;
- let box_hi = self.span.hi;
+ let keyword_hi = self.span.hi;
+ let is_in = self.token.is_keyword(keywords::In);
try!(self.bump());
- // Check for a place: `box(PLACE) EXPR`.
+ if is_in {
+ let place = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL));
+ let blk = try!(self.parse_block());
+ hi = blk.span.hi;
+ let blk_expr = self.mk_expr(blk.span.lo, blk.span.hi, ExprBlock(blk));
+ ex = ExprBox(Some(place), blk_expr);
+ return Ok(self.mk_expr(lo, hi, ex));
+ }
+
+ // FIXME (#22181) Remove `box (PLACE) EXPR` support
+ // entirely after next release (enabling `(box (EXPR))`),
+ // since it will be replaced by `in PLACE { EXPR }`, ...
+ //
+ // ... but for now: check for a place: `box(PLACE) EXPR`.
+
if try!(self.eat(&token::OpenDelim(token::Paren)) ){
- // Support `box() EXPR` as the default.
+ // SNAP d4432b3
+ // Enable this warning after snapshot ...
+ //
+ // let box_span = mk_sp(lo, self.last_span.hi);
+ // self.span_warn(
+ // box_span,
+ // "deprecated syntax; use the `in` keyword now \
+ // (e.g. change `box (<expr>) <expr>` to \
+ // `in <expr> { <expr> }`)");
+
+ // Continue supporting `box () EXPR` (temporarily)
if !try!(self.eat(&token::CloseDelim(token::Paren)) ){
let place = try!(self.parse_expr_nopanic());
try!(self.expect(&token::CloseDelim(token::Paren)));
self.span_err(span,
&format!("expected expression, found `{}`",
this_token_to_string));
- let box_span = mk_sp(lo, box_hi);
+
+ // Spanning just keyword avoids constructing
+ // printout of arg expression (which starts
+ // with parenthesis, as established above).
+
+ let box_span = mk_sp(lo, keyword_hi);
self.span_suggestion(box_span,
- "try using `box()` instead:",
- "box()".to_string());
+ "try using `box ()` instead:",
+ format!("box ()"));
self.abort_if_errors();
}
let subexpression = try!(self.parse_prefix_expr());
// Otherwise, we use the unique pointer default.
let subexpression = try!(self.parse_prefix_expr());
hi = subexpression.span.hi;
+
// FIXME (pnkfelix): After working out kinks with box
// desugaring, should be `ExprBox(None, subexpression)`
// instead.
attrs: &[ast::Attribute],
close_box: bool) -> io::Result<()> {
match blk.rules {
- ast::UnsafeBlock(..) => try!(self.word_space("unsafe")),
- ast::DefaultBlock => ()
+ ast::UnsafeBlock(..) | ast::PushUnsafeBlock(..) => try!(self.word_space("unsafe")),
+ ast::DefaultBlock | ast::PopUnsafeBlock(..) => ()
}
try!(self.maybe_print_comment(blk.span.lo));
try!(self.ann.pre(self, NodeBlock(blk)));
Members.push_back(NewArchiveIterator(Member->child, Member->name));
}
}
- auto pair = writeArchive(Dst, Members, WriteSymbtab, Kind, false);
+ auto pair = writeArchive(Dst, Members, WriteSymbtab, Kind, true);
if (!pair.second)
return 0;
LLVMRustSetLastError(pair.second.message().c_str());
--- /dev/null
+// Copyright 2015 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![crate_type = "rlib"]
+
+pub static FOO: u8 = 8;
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-fn main() {
- use std::boxed::HEAP;
+// Check that `box EXPR` is feature-gated.
+//
+// See also feature-gate-placement-expr.rs
+//
+// (Note that the two tests are separated since the checks appear to
+// be performed at distinct phases, with an abort_if_errors call
+// separating them.)
+fn main() {
let x = box 'c'; //~ ERROR box expression syntax is experimental
println!("x: {}", x);
let x = box () 'c'; //~ ERROR box expression syntax is experimental
println!("x: {}", x);
-
- let x = box (HEAP) 'c'; //~ ERROR box expression syntax is experimental
- println!("x: {}", x);
}
--- /dev/null
+// Copyright 2015 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that `in PLACE { EXPR }` is feature-gated.
+//
+// See also feature-gate-box-expr.rs
+//
+// (Note that the two tests are separated since the checks appear to
+// be performed at distinct phases, with an abort_if_errors call
+// separating them.)
+
+fn main() {
+ use std::boxed::HEAP;
+
+ let x = box (HEAP) 'c'; //~ ERROR placement-in expression syntax is experimental
+ println!("x: {}", x);
+
+ let x = in HEAP { 'c' }; //~ ERROR placement-in expression syntax is experimental
+ println!("x: {}", x);
+}
--- /dev/null
+// Copyright 2015 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+ let c = push_unsafe!('c'); //~ ERROR push/pop_unsafe macros are experimental
+ let c = pop_unsafe!('c'); //~ ERROR push/pop_unsafe macros are experimental
+}
// except according to those terms.
#![feature(box_syntax)]
+#![feature(placement_in_syntax)]
fn main() {
box ( () ) 0;
- //~^ ERROR: only the exchange heap is currently supported
+ //~^ ERROR: the trait `core::ops::Placer<_>` is not implemented
+ //~| ERROR: the trait `core::ops::Placer<_>` is not implemented
}
}
extern "C" {
- fn foo(x: A); //~ ERROR found type without foreign-function-safe
+ fn foo(x: A); //~ ERROR found struct without foreign-function-safe
fn bar(x: B); //~ ERROR foreign-function-safe
fn baz(x: C);
fn qux(x: A2); //~ ERROR foreign-function-safe
#![deny(warnings)]
extern {
- pub fn foo(x: (isize)); //~ ERROR found rust type `isize` in foreign module
+ pub fn foo(x: (isize)); //~ ERROR found Rust type `isize` in foreign module
}
fn main() {
extern {
fn zf(x: Z);
- fn uf(x: U); //~ ERROR found type without foreign-function-safe
- fn bf(x: B); //~ ERROR found type without foreign-function-safe
- fn tf(x: T); //~ ERROR found type without foreign-function-safe
+ fn uf(x: U); //~ ERROR found enum without foreign-function-safe
+ fn bf(x: B); //~ ERROR found enum without foreign-function-safe
+ fn tf(x: T); //~ ERROR found enum without foreign-function-safe
}
pub fn main() { }
extern crate libc;
+trait Mirror { type It; }
+impl<T> Mirror for T { type It = Self; }
+#[repr(C)]
+pub struct StructWithProjection(*mut <StructWithProjection as Mirror>::It);
+#[repr(C)]
+pub struct StructWithProjectionAndLifetime<'a>(
+ &'a mut <StructWithProjectionAndLifetime<'a> as Mirror>::It
+);
+pub type I32Pair = (i32, i32);
+#[repr(C)]
+pub struct ZeroSize;
+pub type RustFn = fn();
+pub type RustBadRet = extern fn() -> Box<u32>;
+
extern {
- pub fn bare_type1(size: isize); //~ ERROR: found rust type
- pub fn bare_type2(size: usize); //~ ERROR: found rust type
- pub fn ptr_type1(size: *const isize); //~ ERROR: found rust type
- pub fn ptr_type2(size: *const usize); //~ ERROR: found rust type
+ pub fn bare_type1(size: isize); //~ ERROR: found Rust type
+ pub fn bare_type2(size: usize); //~ ERROR: found Rust type
+ pub fn ptr_type1(size: *const isize); //~ ERROR: found Rust type
+ pub fn ptr_type2(size: *const usize); //~ ERROR: found Rust type
+ pub fn slice_type(p: &[u32]); //~ ERROR: found Rust slice type
+ pub fn str_type(p: &str); //~ ERROR: found Rust type
+ pub fn box_type(p: Box<u32>); //~ ERROR found Rust type
+ pub fn char_type(p: char); //~ ERROR found Rust type
+ pub fn trait_type(p: &Clone); //~ ERROR found Rust trait type
+ pub fn tuple_type(p: (i32, i32)); //~ ERROR found Rust tuple type
+ pub fn tuple_type2(p: I32Pair); //~ ERROR found Rust tuple type
+ pub fn zero_size(p: ZeroSize); //~ ERROR found zero-size struct
+ pub fn fn_type(p: RustFn); //~ ERROR found function pointer with Rust
+ pub fn fn_type2(p: fn()); //~ ERROR found function pointer with Rust
+ pub fn fn_contained(p: RustBadRet); //~ ERROR: found Rust type
pub fn good1(size: *const libc::c_int);
pub fn good2(size: *const libc::c_uint);
+ pub fn good3(fptr: Option<extern fn()>);
+ pub fn good4(aptr: &[u8; 4 as usize]);
+ pub fn good5(s: StructWithProjection);
+ pub fn good6(s: StructWithProjectionAndLifetime);
+ pub fn good7(fptr: extern fn() -> ());
+ pub fn good8(fptr: extern fn() -> !);
}
fn main() {
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Basic sanity check for `push_unsafe!(EXPR)` and
+// `pop_unsafe!(EXPR)`: we can call unsafe code when there are a
+// positive number of pushes in the stack, or if we are within a
+// normal `unsafe` block, but otherwise cannot.
+
+#![feature(pushpop_unsafe)]
+
+static mut X: i32 = 0;
+
+unsafe fn f() { X += 1; return; }
+fn g() { unsafe { X += 1_000; } return; }
+
+fn main() {
+ push_unsafe!( {
+ f(); pop_unsafe!({
+ f() //~ ERROR: call to unsafe function
+ })
+ } );
+
+ push_unsafe!({
+ f();
+ pop_unsafe!({
+ g();
+ f(); //~ ERROR: call to unsafe function
+ })
+ } );
+
+ push_unsafe!({
+ g(); pop_unsafe!({
+ unsafe {
+ f();
+ }
+ f(); //~ ERROR: call to unsafe function
+ })
+ });
+
+
+ // Note: For implementation simplicity the compiler just
+ // ICE's if you underflow the push_unsafe stack.
+ //
+ // Thus all of the following cases cause an ICE.
+ //
+ // (The "ERROR" notes are from an earlier version
+ // that used saturated arithmetic rather than checked
+ // arithmetic.)
+
+ // pop_unsafe!{ g() };
+ //
+ // push_unsafe!({
+ // pop_unsafe!(pop_unsafe!{ g() })
+ // });
+ //
+ // push_unsafe!({
+ // g();
+ // pop_unsafe!(pop_unsafe!({
+ // f() // ERROR: call to unsafe function
+ // }))
+ // });
+ //
+ // pop_unsafe!({
+ // f(); // ERROR: call to unsafe function
+ // })
+
+}
mod xx {
extern {
- pub fn strlen(str: *const u8) -> usize; //~ ERROR found rust type `usize`
- pub fn foo(x: isize, y: usize); //~ ERROR found rust type `isize`
- //~^ ERROR found rust type `usize`
+ pub fn strlen(str: *const u8) -> usize; //~ ERROR found Rust type `usize`
+ pub fn foo(x: isize, y: usize); //~ ERROR found Rust type `isize`
+ //~^ ERROR found Rust type `usize`
}
}
fn main() {
box (1 + 1)
- //~^ HELP try using `box()` instead:
- //~| SUGGESTION box() (1 + 1)
+ //~^ HELP try using `box ()` instead:
+ //~| SUGGESTION box () (1 + 1)
; //~ ERROR expected expression, found `;`
}
// except according to those terms.
#![feature(rustc_private)]
+#![feature(libc)]
+extern crate libc;
extern crate rustc;
extern crate rustc_driver;
extern crate rustc_lint;
use rustc::session::build_session;
use rustc_driver::driver;
use rustc_resolve::MakeGlobMap;
+use libc::c_void;
use syntax::diagnostics::registry::Registry;
}
/// Returns a raw pointer to the named function.
- pub fn get_function(&mut self, name: &str) -> Option<*const ()> {
+ pub fn get_function(&mut self, name: &str) -> Option<*const c_void> {
let s = CString::new(name.as_bytes()).unwrap();
for &m in &self.modules {
}
/// Returns a raw pointer to the named global item.
- pub fn get_global(&mut self, name: &str) -> Option<*const ()> {
+ pub fn get_global(&mut self, name: &str) -> Option<*const c_void> {
let s = CString::new(name.as_bytes()).unwrap();
for &m in &self.modules {
mod rusti {
extern "rust-intrinsic" {
pub fn init<T>() -> T;
- pub fn move_val_init<T>(dst: &mut T, src: T);
+ pub fn move_val_init<T>(dst: *mut T, src: T);
}
}
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
-#![allow(warnings)]
+#![allow(dead_code, unused_variables)]
#![feature(box_syntax, box_heap)]
+#![feature(placement_in_syntax)]
+
+// during check-pretty, the expanded code needs to opt into these
+// features
+#![feature(placement_new_protocol, core_intrinsics)]
// Tests that the new `box` syntax works with unique pointers.
let y: Box<isize> = box 2;
let b: Box<isize> = box()(1 + 2);
let c = box()(3 + 4);
+
+ let s: Box<Structure> = box Structure {
+ x: 3,
+ y: 4,
+ };
}
--- /dev/null
+// Copyright 2015 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(dead_code, unused_variables)]
+#![feature(box_heap)]
+#![feature(placement_in_syntax)]
+
+// Tests that the new `in` syntax works with unique pointers.
+//
+// Compare with new-box-syntax.rs
+
+use std::boxed::{Box, HEAP};
+
+struct Structure {
+ x: isize,
+ y: isize,
+}
+
+pub fn main() {
+ let x: Box<isize> = in HEAP { 2 };
+ let b: Box<isize> = in HEAP { 1 + 2 };
+ let c = in HEAP { 3 + 4 };
+
+ let s: Box<Structure> = in HEAP {
+ Structure {
+ x: 3,
+ y: 4,
+ }
+ };
+}
--- /dev/null
+// Copyright 2015 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Basic sanity check for `push_unsafe!(EXPR)` and
+// `pop_unsafe!(EXPR)`: we can call unsafe code when there are a
+// positive number of pushes in the stack, or if we are within a
+// normal `unsafe` block, but otherwise cannot.
+
+// ignore-pretty because the `push_unsafe!` and `pop_unsafe!` macros
+// are not integrated with the pretty-printer.
+
+#![feature(pushpop_unsafe)]
+
+static mut X: i32 = 0;
+
+unsafe fn f() { X += 1; return; }
+fn g() { unsafe { X += 1_000; } return; }
+
+fn check_reset_x(x: i32) -> bool {
+ #![allow(unused_parens)] // dont you judge my style choices!
+ unsafe {
+ let ret = (x == X);
+ X = 0;
+ ret
+ }
+}
+
+fn main() {
+ // double-check test infrastructure
+ assert!(check_reset_x(0));
+ unsafe { f(); }
+ assert!(check_reset_x(1));
+ assert!(check_reset_x(0));
+ { g(); }
+ assert!(check_reset_x(1000));
+ assert!(check_reset_x(0));
+ unsafe { f(); g(); g(); }
+ assert!(check_reset_x(2001));
+
+ push_unsafe!( { f(); pop_unsafe!( g() ) } );
+ assert!(check_reset_x(1_001));
+ push_unsafe!( { g(); pop_unsafe!( unsafe { f(); f(); } ) } );
+ assert!(check_reset_x(1_002));
+
+ unsafe { push_unsafe!( { f(); pop_unsafe!( { f(); f(); } ) } ); }
+ assert!(check_reset_x(3));
+ push_unsafe!( { f(); push_unsafe!( { pop_unsafe!( { f(); f(); f(); } ) } ); } );
+ assert!(check_reset_x(4));
+}
--- /dev/null
+// Copyright 2015 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:xcrate-static.rs
+
+extern crate xcrate_static;
+
+fn main() {
+ println!("{}", xcrate_static::FOO);
+}