diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index e6bae4864a4e1..502090731f4a1 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1,6 +1,22 @@ //! Compiler intrinsics. //! //! The corresponding definitions are in `librustc_codegen_llvm/intrinsic.rs`. +//! The corresponding const implementations are in `librustc_mir/interpret/intrinsics.rs` +//! +//! # Const intrinsics +//! +//! Note: any changes to the constness of intrinsics should be discussed with the language team. +//! This includes changes in the stability of the constness. +//! +//! In order to make an intrinsic usable at compile-time, one needs to copy the implementation +//! from https://github.com/rust-lang/miri/blob/master/src/shims/intrinsics.rs to +//! `librustc_mir/interpret/intrinsics.rs` and add a +//! `#[rustc_const_unstable(feature = "foo", issue = "01234")]` to the intrinsic. +//! +//! If an intrinsic is supposed to be used from a `const fn` with a `rustc_const_stable` attribute, +//! the intrinsic's attribute must be `rustc_const_stable`, too. Such a change should not be done +//! without T-lang consulation, because it bakes a feature into the language that cannot be +//! replicated in user code without compiler support. //! //! # Volatiles //! @@ -671,6 +687,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is /// [`std::mem::size_of`](../../std/mem/fn.size_of.html). + #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")] pub fn size_of() -> usize; /// Moves a value to an uninitialized memory location. @@ -678,7 +695,9 @@ extern "rust-intrinsic" { /// Drop glue is not run on the destination. pub fn move_val_init(dst: *mut T, src: T); + #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")] pub fn min_align_of() -> usize; + #[rustc_const_unstable(feature = "const_pref_align_of", issue = "0")] pub fn pref_align_of() -> usize; /// The size of the referenced value in bytes. @@ -689,11 +708,13 @@ extern "rust-intrinsic" { pub fn min_align_of_val(_: &T) -> usize; /// Gets a static string slice containing the name of a type. + #[rustc_const_unstable(feature = "const_type_name", issue = "0")] pub fn type_name() -> &'static str; /// Gets an identifier which is globally unique to the specified type. This /// function will return the same value for a type regardless of whichever /// crate it is invoked in. + #[rustc_const_unstable(feature = "const_type_id", issue = "0")] pub fn type_id() -> u64; /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: @@ -701,6 +722,7 @@ extern "rust-intrinsic" { pub fn panic_if_uninhabited(); /// Gets a reference to a static `Location` indicating where it was called. + #[rustc_const_unstable(feature = "const_caller_location", issue = "47809")] pub fn caller_location() -> &'static crate::panic::Location<'static>; /// Creates a value initialized to zero. @@ -957,6 +979,7 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is /// [`std::mem::needs_drop`](../../std/mem/fn.needs_drop.html). + #[rustc_const_stable(feature = "const_needs_drop", since = "1.40.0")] pub fn needs_drop() -> bool; /// Calculates the offset from a pointer. @@ -1154,6 +1177,7 @@ extern "rust-intrinsic" { pub fn float_to_int_approx_unchecked(value: Float) -> Int; /// Returns the number of bits set in an integer type `T` + #[rustc_const_stable(feature = "const_ctpop", since = "1.40.0")] pub fn ctpop(x: T) -> T; /// Returns the number of leading unset bits (zeroes) in an integer type `T`. @@ -1181,6 +1205,7 @@ extern "rust-intrinsic" { /// let num_leading = ctlz(x); /// assert_eq!(num_leading, 16); /// ``` + #[rustc_const_stable(feature = "const_ctlz", since = "1.40.0")] pub fn ctlz(x: T) -> T; /// Like `ctlz`, but extra-unsafe as it returns `undef` when @@ -1197,6 +1222,7 @@ extern "rust-intrinsic" { /// let num_leading = unsafe { ctlz_nonzero(x) }; /// assert_eq!(num_leading, 3); /// ``` + #[rustc_const_unstable(feature = "constctlz", issue = "0")] pub fn ctlz_nonzero(x: T) -> T; /// Returns the number of trailing unset bits (zeroes) in an integer type `T`. @@ -1224,6 +1250,7 @@ extern "rust-intrinsic" { /// let num_trailing = cttz(x); /// assert_eq!(num_trailing, 16); /// ``` + #[rustc_const_stable(feature = "const_cttz", since = "1.40.0")] pub fn cttz(x: T) -> T; /// Like `cttz`, but extra-unsafe as it returns `undef` when @@ -1240,30 +1267,36 @@ extern "rust-intrinsic" { /// let num_trailing = unsafe { cttz_nonzero(x) }; /// assert_eq!(num_trailing, 3); /// ``` + #[rustc_const_unstable(feature = "const_cttz", issue = "0")] pub fn cttz_nonzero(x: T) -> T; /// Reverses the bytes in an integer type `T`. + #[rustc_const_stable(feature = "const_bswap", since = "1.40.0")] pub fn bswap(x: T) -> T; /// Reverses the bits in an integer type `T`. + #[rustc_const_stable(feature = "const_bitreverse", since = "1.40.0")] pub fn bitreverse(x: T) -> T; /// Performs checked integer addition. /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `overflowing_add` method. For example, /// [`std::u32::overflowing_add`](../../std/primitive.u32.html#method.overflowing_add) + #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] pub fn add_with_overflow(x: T, y: T) -> (T, bool); /// Performs checked integer subtraction /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `overflowing_sub` method. For example, /// [`std::u32::overflowing_sub`](../../std/primitive.u32.html#method.overflowing_sub) + #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] pub fn sub_with_overflow(x: T, y: T) -> (T, bool); /// Performs checked integer multiplication /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `overflowing_mul` method. For example, /// [`std::u32::overflowing_mul`](../../std/primitive.u32.html#method.overflowing_mul) + #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")] pub fn mul_with_overflow(x: T, y: T) -> (T, bool); /// Performs an exact division, resulting in undefined behavior where @@ -1279,9 +1312,11 @@ extern "rust-intrinsic" { /// Performs an unchecked left shift, resulting in undefined behavior when /// y < 0 or y >= N, where N is the width of T in bits. + #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] pub fn unchecked_shl(x: T, y: T) -> T; /// Performs an unchecked right shift, resulting in undefined behavior when /// y < 0 or y >= N, where N is the width of T in bits. + #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] pub fn unchecked_shr(x: T, y: T) -> T; /// Returns the result of an unchecked addition, resulting in @@ -1300,39 +1335,46 @@ extern "rust-intrinsic" { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `rotate_left` method. For example, /// [`std::u32::rotate_left`](../../std/primitive.u32.html#method.rotate_left) + #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")] pub fn rotate_left(x: T, y: T) -> T; /// Performs rotate right. /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `rotate_right` method. For example, /// [`std::u32::rotate_right`](../../std/primitive.u32.html#method.rotate_right) + #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")] pub fn rotate_right(x: T, y: T) -> T; /// Returns (a + b) mod 2N, where N is the width of T in bits. /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `wrapping_add` method. For example, /// [`std::u32::wrapping_add`](../../std/primitive.u32.html#method.wrapping_add) + #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] pub fn wrapping_add(a: T, b: T) -> T; /// Returns (a - b) mod 2N, where N is the width of T in bits. /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `wrapping_sub` method. For example, /// [`std::u32::wrapping_sub`](../../std/primitive.u32.html#method.wrapping_sub) + #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] pub fn wrapping_sub(a: T, b: T) -> T; /// Returns (a * b) mod 2N, where N is the width of T in bits. /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `wrapping_mul` method. For example, /// [`std::u32::wrapping_mul`](../../std/primitive.u32.html#method.wrapping_mul) + #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] pub fn wrapping_mul(a: T, b: T) -> T; /// Computes `a + b`, while saturating at numeric bounds. /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `saturating_add` method. For example, /// [`std::u32::saturating_add`](../../std/primitive.u32.html#method.saturating_add) + #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")] pub fn saturating_add(a: T, b: T) -> T; /// Computes `a - b`, while saturating at numeric bounds. /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `saturating_sub` method. For example, /// [`std::u32::saturating_sub`](../../std/primitive.u32.html#method.saturating_sub) + #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")] pub fn saturating_sub(a: T, b: T) -> T; /// Returns the value of the discriminant for the variant in 'v', @@ -1354,6 +1396,7 @@ extern "rust-intrinsic" { pub fn nontemporal_store(ptr: *mut T, val: T); /// See documentation of `<*const T>::offset_from` for details. + #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "0")] pub fn ptr_offset_from(ptr: *const T, base: *const T) -> isize; /// Internal hook used by Miri to implement unwinding. diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 7c5e06ace6729..d12aebb87b975 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -127,6 +127,8 @@ #![feature(maybe_uninit_slice)] #![feature(external_doc)] #![feature(associated_type_bounds)] +#![feature(const_type_id)] +#![feature(const_caller_location)] #[prelude_import] #[allow(unused)] diff --git a/src/libcore/macros/mod.rs b/src/libcore/macros/mod.rs index 6c7ec2cbfe321..1a3d338fb9876 100644 --- a/src/libcore/macros/mod.rs +++ b/src/libcore/macros/mod.rs @@ -5,6 +5,7 @@ // the `caller_location` intrinsic, but once `#[track_caller]` is implemented, // `panicking::{panic, panic_fmt}` can use that instead of a `Location` argument. core_intrinsics, + const_caller_location, )] #[stable(feature = "core", since = "1.6.0")] macro_rules! panic { diff --git a/src/libcore/ops/drop.rs b/src/libcore/ops/drop.rs index eae63ea2390a8..5233b475c4646 100644 --- a/src/libcore/ops/drop.rs +++ b/src/libcore/ops/drop.rs @@ -99,9 +99,15 @@ pub trait Drop { /// Given that a [`panic!`] will call `drop` as it unwinds, any [`panic!`] /// in a `drop` implementation will likely abort. /// + /// Note that even if this panics, the value is considered to be dropped; + /// you must not cause `drop` to be called again. This is normally automatically + /// handled by the compiler, but when using unsafe code, can sometimes occur + /// unintentionally, particularly when using [`std::ptr::drop_in_place`]. + /// /// [E0040]: ../../error-index.html#E0040 /// [`panic!`]: ../macro.panic.html /// [`std::mem::drop`]: ../../std/mem/fn.drop.html + /// [`std::ptr::drop_in_place`]: ../../std/ptr/fn.drop_in_place.html #[stable(feature = "rust1", since = "1.0.0")] fn drop(&mut self); } diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index dc2008fdd9743..a7a8673d49eb1 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -1043,9 +1043,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { walk_list!(visitor, visit_label, opt_label); visitor.visit_block(block); } - ExprKind::Assign(ref left_hand_expression, ref right_hand_expression) => { - visitor.visit_expr(right_hand_expression); - visitor.visit_expr(left_hand_expression) + ExprKind::Assign(ref lhs, ref rhs, _) => { + visitor.visit_expr(rhs); + visitor.visit_expr(lhs) } ExprKind::AssignOp(_, ref left_expression, ref right_expression) => { visitor.visit_expr(right_expression); diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 83869951ea2a1..a702eb839845e 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -3437,65 +3437,3 @@ fn body_ids(bodies: &BTreeMap>) -> Vec body_ids.sort_by_key(|b| bodies[b].value.span); body_ids } - -/// Checks if the specified expression is a built-in range literal. -/// (See: `LoweringContext::lower_expr()`). -pub fn is_range_literal(sess: &Session, expr: &hir::Expr) -> bool { - use hir::{Path, QPath, ExprKind, TyKind}; - - // Returns whether the given path represents a (desugared) range, - // either in std or core, i.e. has either a `::std::ops::Range` or - // `::core::ops::Range` prefix. - fn is_range_path(path: &Path) -> bool { - let segs: Vec<_> = path.segments.iter().map(|seg| seg.ident.to_string()).collect(); - let segs: Vec<_> = segs.iter().map(|seg| &**seg).collect(); - - // "{{root}}" is the equivalent of `::` prefix in `Path`. - if let ["{{root}}", std_core, "ops", range] = segs.as_slice() { - (*std_core == "std" || *std_core == "core") && range.starts_with("Range") - } else { - false - } - }; - - // Check whether a span corresponding to a range expression is a - // range literal, rather than an explicit struct or `new()` call. - fn is_lit(sess: &Session, span: &Span) -> bool { - let source_map = sess.source_map(); - let end_point = source_map.end_point(*span); - - if let Ok(end_string) = source_map.span_to_snippet(end_point) { - !(end_string.ends_with("}") || end_string.ends_with(")")) - } else { - false - } - }; - - match expr.kind { - // All built-in range literals but `..=` and `..` desugar to `Struct`s. - ExprKind::Struct(ref qpath, _, _) => { - if let QPath::Resolved(None, ref path) = **qpath { - return is_range_path(&path) && is_lit(sess, &expr.span); - } - } - - // `..` desugars to its struct path. - ExprKind::Path(QPath::Resolved(None, ref path)) => { - return is_range_path(&path) && is_lit(sess, &expr.span); - } - - // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. - ExprKind::Call(ref func, _) => { - if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.kind { - if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.kind { - let new_call = segment.ident.name == sym::new; - return is_range_path(&path) && is_lit(sess, &expr.span) && new_call; - } - } - } - - _ => {} - } - - false -} diff --git a/src/librustc/hir/lowering/expr.rs b/src/librustc/hir/lowering/expr.rs index 8939b5eef2660..8311b9168e455 100644 --- a/src/librustc/hir/lowering/expr.rs +++ b/src/librustc/hir/lowering/expr.rs @@ -122,8 +122,8 @@ impl LoweringContext<'_, '_> { self.lower_block(blk, opt_label.is_some()), self.lower_label(opt_label), ), - ExprKind::Assign(ref el, ref er) => { - hir::ExprKind::Assign(P(self.lower_expr(el)), P(self.lower_expr(er))) + ExprKind::Assign(ref el, ref er, span) => { + hir::ExprKind::Assign(P(self.lower_expr(el)), P(self.lower_expr(er)), span) } ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp( self.lower_binop(op), @@ -994,8 +994,11 @@ impl LoweringContext<'_, '_> { let (val_pat, val_pat_hid) = self.pat_ident(pat.span, val_ident); let val_expr = P(self.expr_ident(pat.span, val_ident, val_pat_hid)); let next_expr = P(self.expr_ident(pat.span, next_ident, next_pat_hid)); - let assign = - P(self.expr(pat.span, hir::ExprKind::Assign(next_expr, val_expr), ThinVec::new())); + let assign = P(self.expr( + pat.span, + hir::ExprKind::Assign(next_expr, val_expr, pat.span), + ThinVec::new(), + )); let some_pat = self.pat_some(pat.span, val_pat); self.arm(some_pat, assign) }; diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 4aa8c12a219ca..bf95324d776dc 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -29,10 +29,10 @@ use syntax::ast::{AttrVec, Attribute, FloatTy, IntTy, Label, LitKind, StrStyle, pub use syntax::ast::{BorrowKind, ImplPolarity, IsAuto}; pub use syntax::ast::{CaptureBy, Constness, Movability, Mutability, Unsafety}; use syntax::attr::{InlineAttr, OptimizeAttr}; -use syntax::source_map::Spanned; -use syntax::symbol::{kw, Symbol}; use syntax::tokenstream::TokenStream; use syntax::util::parser::ExprPrecedence; +use syntax_pos::source_map::{SourceMap, Spanned}; +use syntax_pos::symbol::{kw, sym, Symbol}; use syntax_pos::{MultiSpan, Span, DUMMY_SP}; /// HIR doesn't commit to a concrete storage type and has its own alias for a vector. @@ -1564,6 +1564,68 @@ impl fmt::Debug for Expr { } } +/// Checks if the specified expression is a built-in range literal. +/// (See: `LoweringContext::lower_expr()`). +/// +/// FIXME(#60607): This function is a hack. If and when we have `QPath::Lang(...)`, +/// we can use that instead as simpler, more reliable mechanism, as opposed to using `SourceMap`. +pub fn is_range_literal(sm: &SourceMap, expr: &Expr) -> bool { + // Returns whether the given path represents a (desugared) range, + // either in std or core, i.e. has either a `::std::ops::Range` or + // `::core::ops::Range` prefix. + fn is_range_path(path: &Path) -> bool { + let segs: Vec<_> = path.segments.iter().map(|seg| seg.ident.to_string()).collect(); + let segs: Vec<_> = segs.iter().map(|seg| &**seg).collect(); + + // "{{root}}" is the equivalent of `::` prefix in `Path`. + if let ["{{root}}", std_core, "ops", range] = segs.as_slice() { + (*std_core == "std" || *std_core == "core") && range.starts_with("Range") + } else { + false + } + }; + + // Check whether a span corresponding to a range expression is a + // range literal, rather than an explicit struct or `new()` call. + fn is_lit(sm: &SourceMap, span: &Span) -> bool { + let end_point = sm.end_point(*span); + + if let Ok(end_string) = sm.span_to_snippet(end_point) { + !(end_string.ends_with("}") || end_string.ends_with(")")) + } else { + false + } + }; + + match expr.kind { + // All built-in range literals but `..=` and `..` desugar to `Struct`s. + ExprKind::Struct(ref qpath, _, _) => { + if let QPath::Resolved(None, ref path) = **qpath { + return is_range_path(&path) && is_lit(sm, &expr.span); + } + } + + // `..` desugars to its struct path. + ExprKind::Path(QPath::Resolved(None, ref path)) => { + return is_range_path(&path) && is_lit(sm, &expr.span); + } + + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. + ExprKind::Call(ref func, _) => { + if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.kind { + if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.kind { + let new_call = segment.ident.name == sym::new; + return is_range_path(&path) && is_lit(sm, &expr.span) && new_call; + } + } + } + + _ => {} + } + + false +} + #[derive(RustcEncodable, RustcDecodable, Debug, HashStable)] pub enum ExprKind { /// A `box x` expression. @@ -1628,7 +1690,8 @@ pub enum ExprKind { Block(P, Option