E0753: include_str!("./error_codes/E0753.md"),
E0754: include_str!("./error_codes/E0754.md"),
E0755: include_str!("./error_codes/E0755.md"),
+E0756: include_str!("./error_codes/E0756.md"),
E0758: include_str!("./error_codes/E0758.md"),
E0759: include_str!("./error_codes/E0759.md"),
E0760: include_str!("./error_codes/E0760.md"),
E0722, // Malformed `#[optimize]` attribute
E0726, // non-explicit (not `'_`) elided lifetime in unsupported position
// E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
- E0756, // `#[ffi_const]` is only allowed on foreign functions
E0757, // `#[ffi_const]` functions cannot be `#[ffi_pure]`
E0772, // `'static' obligation coming from `impl dyn Trait {}` or `impl Foo for dyn Bar {}`.
}
--- /dev/null
+The `ffi_const` attribute was used on something other than a foreign function
+declaration.
+
+Erroneous code example:
+
+```compile_fail,E0756
+#![feature(ffi_const)]
+
+#[ffi_const] // error!
+pub fn foo() {}
+# fn main() {}
+```
+
+The `ffi_const` attribute can only be used on foreign function declarations
+which have no side effects except for their return value:
+
+```
+#![feature(ffi_const)]
+
+extern "C" {
+ #[ffi_const] // ok!
+ pub fn strlen(s: *const i8) -> i32;
+}
+# fn main() {}
+```
+
+You can get more information about it in the [unstable Rust Book].
+
+[unstable Rust Book]: https://doc.rust-lang.org/nightly/unstable-book/language-features/ffi-const.html
}
fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
- match t.kind {
- TyKind::TraitObject(
- poly_trait_refs,
- Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. },
- ) => {
- for ptr in poly_trait_refs {
- if Some(self.1) == ptr.trait_ref.trait_def_id() {
- self.0.push(ptr.span);
- }
+ if let TyKind::TraitObject(
+ poly_trait_refs,
+ Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. },
+ ) = t.kind
+ {
+ for ptr in poly_trait_refs {
+ if Some(self.1) == ptr.trait_ref.trait_def_id() {
+ self.0.push(ptr.span);
}
}
- _ => {}
}
walk_ty(self, t);
}
);
}
+ fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'_>) {
+ let def_id = cx.tcx.hir().local_def_id(foreign_item.hir_id);
+ let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id());
+ self.check_missing_docs_attrs(
+ cx,
+ Some(foreign_item.hir_id),
+ &foreign_item.attrs,
+ foreign_item.span,
+ article,
+ desc,
+ );
+ }
+
fn check_struct_field(&mut self, cx: &LateContext<'_>, sf: &hir::StructField<'_>) {
if !sf.is_positional() {
self.check_missing_docs_attrs(
use rustc_ast::{Item, ItemKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
+use rustc_hir::def::Res;
use rustc_hir::{GenericArg, HirId, MutTy, Mutability, Path, PathSegment, QPath, Ty, TyKind};
+use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::symbol::{sym, Ident, Symbol};
fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, ty: &Ty<'_>) -> Option<String> {
if let TyKind::Path(qpath) = &ty.kind {
if let QPath::Resolved(_, path) = qpath {
- let did = path.res.opt_def_id()?;
- if cx.tcx.is_diagnostic_item(sym::Ty, did) {
- return Some(format!("Ty{}", gen_args(path.segments.last().unwrap())));
- } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) {
- return Some(format!("TyCtxt{}", gen_args(path.segments.last().unwrap())));
+ match path.res {
+ Res::Def(_, did) => {
+ if cx.tcx.is_diagnostic_item(sym::Ty, did) {
+ return Some(format!("Ty{}", gen_args(path.segments.last().unwrap())));
+ } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) {
+ return Some(format!("TyCtxt{}", gen_args(path.segments.last().unwrap())));
+ }
+ }
+ // Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait.
+ Res::SelfTy(None, Some((did, _))) => {
+ if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() {
+ if cx.tcx.is_diagnostic_item(sym::Ty, adt.did) {
+ // NOTE: This path is currently unreachable as `Ty<'tcx>` is
+ // defined as a type alias meaning that `impl<'tcx> Ty<'tcx>`
+ // is not actually allowed.
+ //
+ // I(@lcnr) still kept this branch in so we don't miss this
+ // if we ever change it in the future.
+ return Some(format!("Ty<{}>", substs[0]));
+ } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, adt.did) {
+ return Some(format!("TyCtxt<{}>", substs[0]));
+ }
+ }
+ }
+ _ => (),
}
}
}
self.mk_const(ty::Const { val: ty::ConstKind::Error(DelaySpanBugEmitted(())), ty })
}
- pub fn consider_optimizing<T: Fn() -> String>(&self, msg: T) -> bool {
+ pub fn consider_optimizing<T: Fn() -> String>(self, msg: T) -> bool {
let cname = self.crate_name(LOCAL_CRATE).as_str();
self.sess.consider_optimizing(&cname, msg)
}
kind: hir::ItemKind::Impl { items, .. }, ..
})) => {
for item in &items[..] {
- match item.kind {
- hir::AssocItemKind::Type => {
- if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found {
- db.span_label(item.span, "expected this associated type");
- return true;
- }
+ if let hir::AssocItemKind::Type = item.kind {
+ if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found {
+ db.span_label(item.span, "expected this associated type");
+ return true;
}
- _ => {}
}
}
}
/// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref`
/// requirement, provide a strucuted suggestion to constrain it to a given type `ty`.
fn constrain_generic_bound_associated_type_structured_suggestion(
- &self,
+ self,
db: &mut DiagnosticBuilder<'_>,
trait_ref: &ty::TraitRef<'tcx>,
bounds: hir::GenericBounds<'_>,
/// Given a span corresponding to a bound, provide a structured suggestion to set an
/// associated type to a given type `ty`.
fn constrain_associated_type_structured_suggestion(
- &self,
+ self,
db: &mut DiagnosticBuilder<'_>,
span: Span,
assoc: &ty::AssocItem,
tcx.layout_raw(param_env.and(normalized))?
}
- ty::Bound(..) | ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) => {
+ ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) => {
bug!("Layout::compute: unexpected type `{}`", ty)
}
- ty::Param(_) | ty::Error(_) => {
+ ty::Bound(..) | ty::Param(_) | ty::Error(_) => {
return Err(LayoutError::Unknown(ty));
}
})
// Iterate all local crate items no matter where they are defined.
let hir = tcx.hir();
for item in hir.krate().items.values() {
- if item.ident.name.as_str().is_empty() {
+ if item.ident.name.as_str().is_empty() || matches!(item.kind, ItemKind::Use(_, _)) {
continue;
}
- match item.kind {
- ItemKind::Use(_, _) => {
- continue;
- }
- _ => {}
- }
-
if let Some(local_def_id) = hir.definitions().opt_hir_id_to_local_def_id(item.hir_id) {
let def_id = local_def_id.to_def_id();
let ns = tcx.def_kind(def_id).ns().unwrap_or(Namespace::TypeNS);
};
// Early-return cases.
let val_val = match val.val {
- ty::ConstKind::Param(_) => throw_inval!(TooGeneric),
+ ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric),
ty::ConstKind::Error(_) => throw_inval!(TypeckError(ErrorReported)),
ty::ConstKind::Unevaluated(def, substs, promoted) => {
let instance = self.resolve(def, substs)?;
return Ok(self.eval_to_allocation(GlobalId { instance, promoted })?.into());
}
- ty::ConstKind::Infer(..)
- | ty::ConstKind::Bound(..)
- | ty::ConstKind::Placeholder(..) => {
+ ty::ConstKind::Infer(..) | ty::ConstKind::Placeholder(..) => {
span_bug!(self.cur_span(), "const_to_op: Unexpected ConstKind {:?}", val)
}
ty::ConstKind::Value(val_val) => val_val,
//! has interior mutability or needs to be dropped, as well as the visitor that emits errors when
//! it finds operations that are invalid in a certain context.
+use rustc_attr as attr;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::mir;
use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::Symbol;
pub use self::qualifs::Qualif;
pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
Some(def_id) == tcx.lang_items().panic_fn() || Some(def_id) == tcx.lang_items().begin_panic_fn()
}
+
+pub fn allow_internal_unstable(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool {
+ let attrs = tcx.get_attrs(def_id);
+ attr::allow_internal_unstable(&tcx.sess, attrs)
+ .map_or(false, |mut features| features.any(|name| name == feature_gate))
+}
//! Concrete error types for all operations which may be invalid in a certain const context.
-use rustc_errors::struct_span_err;
+use rustc_errors::{struct_span_err, Applicability};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_session::config::nightly_options;
pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) {
debug!("illegal_op: op={:?}", op);
- if op.is_allowed_in_item(ccx) {
- return;
- }
+ let gate = match op.status_in_item(ccx) {
+ Status::Allowed => return,
+
+ Status::Unstable(gate) if ccx.tcx.features().enabled(gate) => {
+ let unstable_in_stable = ccx.const_kind() == hir::ConstContext::ConstFn
+ && ccx.tcx.features().enabled(sym::staged_api)
+ && !ccx.tcx.has_attr(ccx.def_id.to_def_id(), sym::rustc_const_unstable)
+ && !super::allow_internal_unstable(ccx.tcx, ccx.def_id.to_def_id(), gate);
+
+ if unstable_in_stable {
+ ccx.tcx.sess
+ .struct_span_err(span, &format!("`#[feature({})]` cannot be depended on in a const-stable function", gate.as_str()))
+ .span_suggestion(
+ ccx.body.span,
+ "if it is not part of the public API, make this function unstably const",
+ concat!(r#"#[rustc_const_unstable(feature = "...", issue = "...")]"#, '\n').to_owned(),
+ Applicability::HasPlaceholders,
+ )
+ .help("otherwise `#[allow_internal_unstable]` can be used to bypass stability checks")
+ .emit();
+ }
+
+ return;
+ }
+
+ Status::Unstable(gate) => Some(gate),
+ Status::Forbidden => None,
+ };
if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
- ccx.tcx.sess.miri_unleashed_feature(span, O::feature_gate());
+ ccx.tcx.sess.miri_unleashed_feature(span, gate);
return;
}
op.emit_error(ccx, span);
}
+pub enum Status {
+ Allowed,
+ Unstable(Symbol),
+ Forbidden,
+}
+
/// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug {
- /// Returns the `Symbol` corresponding to the feature gate that would enable this operation,
- /// or `None` if such a feature gate does not exist.
- fn feature_gate() -> Option<Symbol> {
- None
- }
-
- /// Returns `true` if this operation is allowed in the given item.
- ///
- /// This check should assume that we are not in a non-const `fn`, where all operations are
- /// legal.
- ///
- /// By default, it returns `true` if and only if this operation has a corresponding feature
- /// gate and that gate is enabled.
- fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
- Self::feature_gate().map_or(false, |gate| ccx.tcx.features().enabled(gate))
+ /// Returns an enum indicating whether this operation is allowed within the given item.
+ fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
+ Status::Forbidden
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
"{} contains unimplemented expression type",
ccx.const_kind()
);
- if let Some(feat) = Self::feature_gate() {
- err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feat));
+
+ if let Status::Unstable(gate) = self.status_in_item(ccx) {
+ if !ccx.tcx.features().enabled(gate) && nightly_options::is_nightly_build() {
+ err.help(&format!("add `#![feature({})]` to the crate attributes to enable", gate));
+ }
}
+
if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"A function call isn't allowed in the const's initialization expression \
impl NonConstOp for InlineAsm {}
#[derive(Debug)]
-pub struct LiveDrop(pub Option<Span>);
+pub struct LiveDrop {
+ pub dropped_at: Option<Span>,
+}
impl NonConstOp for LiveDrop {
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
let mut diagnostic = struct_span_err!(
"destructors cannot be evaluated at compile-time"
);
diagnostic.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind()));
- if let Some(span) = self.0 {
+ if let Some(span) = self.dropped_at {
diagnostic.span_label(span, "value is dropped here");
}
diagnostic.emit();
#[derive(Debug)]
pub struct MutBorrow;
impl NonConstOp for MutBorrow {
- fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
- // Forbid everywhere except in const fn
- ccx.const_kind() == hir::ConstContext::ConstFn
- && ccx.tcx.features().enabled(Self::feature_gate().unwrap())
- }
-
- fn feature_gate() -> Option<Symbol> {
- Some(sym::const_mut_refs)
+ fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+ // Forbid everywhere except in const fn with a feature gate
+ if ccx.const_kind() == hir::ConstContext::ConstFn {
+ Status::Unstable(sym::const_mut_refs)
+ } else {
+ Status::Forbidden
+ }
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
&format!("mutable references are not allowed in {}s", ccx.const_kind()),
)
} else {
- struct_span_err!(
+ let mut err = struct_span_err!(
ccx.tcx.sess,
span,
E0764,
"mutable references are not allowed in {}s",
ccx.const_kind(),
- )
+ );
+ err.span_label(span, format!("`&mut` is only allowed in `const fn`"));
+ err
};
- err.span_label(span, "`&mut` is only allowed in `const fn`".to_string());
if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"References in statics and constants may only refer \
}
}
+// FIXME(ecstaticmorse): Unify this with `MutBorrow`. It has basically the same issues.
#[derive(Debug)]
pub struct MutAddressOf;
impl NonConstOp for MutAddressOf {
- fn feature_gate() -> Option<Symbol> {
- Some(sym::const_mut_refs)
+ fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+ // Forbid everywhere except in const fn with a feature gate
+ if ccx.const_kind() == hir::ConstContext::ConstFn {
+ Status::Unstable(sym::const_mut_refs)
+ } else {
+ Status::Forbidden
+ }
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
#[derive(Debug)]
pub struct MutDeref;
impl NonConstOp for MutDeref {
- fn feature_gate() -> Option<Symbol> {
- Some(sym::const_mut_refs)
+ fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
+ Status::Unstable(sym::const_mut_refs)
}
}
#[derive(Debug)]
pub struct Panic;
impl NonConstOp for Panic {
- fn feature_gate() -> Option<Symbol> {
- Some(sym::const_panic)
+ fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
+ Status::Unstable(sym::const_panic)
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
#[derive(Debug)]
pub struct RawPtrDeref;
impl NonConstOp for RawPtrDeref {
- fn feature_gate() -> Option<Symbol> {
- Some(sym::const_raw_ptr_deref)
+ fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
+ Status::Unstable(sym::const_raw_ptr_deref)
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
#[derive(Debug)]
pub struct RawPtrToIntCast;
impl NonConstOp for RawPtrToIntCast {
- fn feature_gate() -> Option<Symbol> {
- Some(sym::const_raw_ptr_to_usize_cast)
+ fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
+ Status::Unstable(sym::const_raw_ptr_to_usize_cast)
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
#[derive(Debug)]
pub struct StaticAccess;
impl NonConstOp for StaticAccess {
- fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
- matches!(ccx.const_kind(), hir::ConstContext::Static(_))
+ fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+ if let hir::ConstContext::Static(_) = ccx.const_kind() {
+ Status::Allowed
+ } else {
+ Status::Forbidden
+ }
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
#[derive(Debug)]
pub struct UnionAccess;
impl NonConstOp for UnionAccess {
- fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
+ fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
// Union accesses are stable in all contexts except `const fn`.
- ccx.const_kind() != hir::ConstContext::ConstFn
- || ccx.tcx.features().enabled(Self::feature_gate().unwrap())
- }
-
- fn feature_gate() -> Option<Symbol> {
- Some(sym::const_fn_union)
+ if ccx.const_kind() != hir::ConstContext::ConstFn {
+ Status::Allowed
+ } else {
+ Status::Unstable(sym::const_fn_union)
+ }
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{self, BasicBlock, Location};
use rustc_middle::ty::TyCtxt;
-use rustc_span::Span;
+use rustc_span::{sym, Span};
use super::ops;
use super::qualifs::{NeedsDrop, Qualif};
/// Returns `true` if we should use the more precise live drop checker that runs after drop
/// elaboration.
-pub fn checking_enabled(tcx: TyCtxt<'tcx>) -> bool {
+pub fn checking_enabled(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool {
+ // Const-stable functions must always use the stable live drop checker.
+ if tcx.features().staged_api && !tcx.has_attr(def_id.to_def_id(), sym::rustc_const_unstable) {
+ return false;
+ }
+
tcx.features().const_precise_live_drops
}
return;
}
- if !checking_enabled(tcx) {
+ if !checking_enabled(tcx, def_id) {
return;
}
impl CheckLiveDrops<'mir, 'tcx> {
fn check_live_drop(&self, span: Span) {
- ops::non_const(self.ccx, ops::LiveDrop(None), span);
+ ops::non_const(self.ccx, ops::LiveDrop { dropped_at: None }, span);
}
}
| TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
// If we are checking live drops after drop-elaboration, don't emit duplicate
// errors here.
- if super::post_drop_elaboration::checking_enabled(self.tcx) {
+ if super::post_drop_elaboration::checking_enabled(self.tcx, self.def_id) {
return;
}
if needs_drop {
self.check_op_spanned(
- ops::LiveDrop(Some(terminator.source_info.span)),
+ ops::LiveDrop { dropped_at: Some(terminator.source_info.span) },
err_span,
);
}
}
TerminatorKind::InlineAsm { ref operands, .. } => {
for (index, op) in operands.iter().enumerate() {
- match op {
- InlineAsmOperand::Const { .. } => {
- self.candidates.push(Candidate::InlineAsm { bb: location.block, index })
- }
- _ => {}
+ if let InlineAsmOperand::Const { .. } = op {
+ self.candidates.push(Candidate::InlineAsm { bb: location.block, index })
}
}
}
let operand_ty = operand.ty(self.body, self.tcx);
let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
- match (cast_in, cast_out) {
- (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
- // ptr-to-int casts are not possible in consts and thus not promotable
- return Err(Unpromotable);
- }
- _ => {}
+ if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) {
+ // ptr-to-int casts are not possible in consts and thus not promotable
+ return Err(Unpromotable);
}
}
-use rustc_attr as attr;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::*;
// However, we cannot allow stable `const fn`s to use unstable features without an explicit
// opt-in via `allow_internal_unstable`.
- attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id))
- .map_or(false, |mut features| features.any(|name| name == feature_gate))
+ super::check_consts::allow_internal_unstable(tcx, def_id, feature_gate)
}
/// Returns `true` if the given library feature gate is allowed within the function with the given `DefId`.
// However, we cannot allow stable `const fn`s to use unstable features without an explicit
// opt-in via `allow_internal_unstable`.
- attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id))
- .map_or(false, |mut features| features.any(|name| name == feature_gate))
+ super::check_consts::allow_internal_unstable(tcx, def_id, feature_gate)
}
fn check_terminator(
}
}
- match (open_brace, delim) {
- //only add braces
- (DelimToken::Brace, DelimToken::Brace) => {
- self.matching_block_spans.push((open_brace_span, close_brace_span));
- }
- _ => {}
+ //only add braces
+ if let (DelimToken::Brace, DelimToken::Brace) = (open_brace, delim) {
+ self.matching_block_spans.push((open_brace_span, close_brace_span));
}
if self.open_braces.is_empty() {
#![feature(bool_to_option)]
#![feature(crate_visibility_modifier)]
#![feature(bindings_after_at)]
+#![feature(iter_order_by)]
#![feature(or_patterns)]
use rustc_ast as ast;
// Break tokens after we expand any nonterminals, so that we break tokens
// that are produced as a result of nonterminal expansion.
- let mut t1 = first.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens);
- let mut t2 = other.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens);
- for (t1, t2) in t1.by_ref().zip(t2.by_ref()) {
- if !tokentree_probably_equal_for_proc_macro(&t1, &t2, sess) {
- return false;
- }
- }
- t1.next().is_none() && t2.next().is_none()
+ let t1 = first.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens);
+ let t2 = other.trees().filter(semantic_tree).flat_map(expand_nt).flat_map(break_tokens);
+
+ t1.eq_by(t2, |t1, t2| tokentree_probably_equal_for_proc_macro(&t1, &t2, sess))
}
// See comments in `Nonterminal::to_tokenstream` for why we care about
// fill character
if let Some(&(_, c)) = self.cur.peek() {
- match self.cur.clone().nth(1) {
- Some((_, '>' | '<' | '^')) => {
- spec.fill = Some(c);
- self.cur.next();
- }
- _ => {}
+ if let Some((_, '>' | '<' | '^')) = self.cur.clone().nth(1) {
+ spec.fill = Some(c);
+ self.cur.next();
}
}
// Alignment
if ns != TypeNS {
return;
}
- match binding.res() {
- Res::Def(DefKind::Trait | DefKind::TraitAlias, _) => {
- collected_traits.push((name, binding))
- }
- _ => (),
+ if let Res::Def(DefKind::Trait | DefKind::TraitAlias, _) = binding.res() {
+ collected_traits.push((name, binding))
}
});
*traits = Some(collected_traits.into_boxed_slice());
fn to_u32(&self) -> u32;
}
-/// A byte offset. Keep this small (currently 32-bits), as AST contains
-/// a lot of them.
-#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
-pub struct BytePos(pub u32);
-
-/// A character offset. Because of multibyte UTF-8 characters, a byte offset
-/// is not equivalent to a character offset. The `SourceMap` will convert `BytePos`
-/// values to `CharPos` values as necessary.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
-pub struct CharPos(pub usize);
+macro_rules! impl_pos {
+ (
+ $(
+ $(#[$attr:meta])*
+ $vis:vis struct $ident:ident($inner_vis:vis $inner_ty:ty);
+ )*
+ ) => {
+ $(
+ $(#[$attr])*
+ $vis struct $ident($inner_vis $inner_ty);
+
+ impl Pos for $ident {
+ #[inline(always)]
+ fn from_usize(n: usize) -> $ident {
+ $ident(n as $inner_ty)
+ }
-// FIXME: lots of boilerplate in these impls, but so far my attempts to fix
-// have been unsuccessful.
+ #[inline(always)]
+ fn to_usize(&self) -> usize {
+ self.0 as usize
+ }
-impl Pos for BytePos {
- #[inline(always)]
- fn from_usize(n: usize) -> BytePos {
- BytePos(n as u32)
- }
+ #[inline(always)]
+ fn from_u32(n: u32) -> $ident {
+ $ident(n as $inner_ty)
+ }
- #[inline(always)]
- fn to_usize(&self) -> usize {
- self.0 as usize
- }
+ #[inline(always)]
+ fn to_u32(&self) -> u32 {
+ self.0 as u32
+ }
+ }
- #[inline(always)]
- fn from_u32(n: u32) -> BytePos {
- BytePos(n)
- }
+ impl Add for $ident {
+ type Output = $ident;
- #[inline(always)]
- fn to_u32(&self) -> u32 {
- self.0
- }
-}
+ #[inline(always)]
+ fn add(self, rhs: $ident) -> $ident {
+ $ident(self.0 + rhs.0)
+ }
+ }
-impl Add for BytePos {
- type Output = BytePos;
+ impl Sub for $ident {
+ type Output = $ident;
- #[inline(always)]
- fn add(self, rhs: BytePos) -> BytePos {
- BytePos((self.to_usize() + rhs.to_usize()) as u32)
- }
+ #[inline(always)]
+ fn sub(self, rhs: $ident) -> $ident {
+ $ident(self.0 - rhs.0)
+ }
+ }
+ )*
+ };
}
-impl Sub for BytePos {
- type Output = BytePos;
+impl_pos! {
+ /// A byte offset. Keep this small (currently 32-bits), as AST contains
+ /// a lot of them.
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+ pub struct BytePos(pub u32);
- #[inline(always)]
- fn sub(self, rhs: BytePos) -> BytePos {
- BytePos((self.to_usize() - rhs.to_usize()) as u32)
- }
+ /// A character offset. Because of multibyte UTF-8 characters, a byte offset
+ /// is not equivalent to a character offset. The `SourceMap` will convert `BytePos`
+ /// values to `CharPos` values as necessary.
+ #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
+ pub struct CharPos(pub usize);
}
impl<S: rustc_serialize::Encoder> Encodable<S> for BytePos {
}
}
-impl Pos for CharPos {
- #[inline(always)]
- fn from_usize(n: usize) -> CharPos {
- CharPos(n)
- }
-
- #[inline(always)]
- fn to_usize(&self) -> usize {
- self.0
- }
-
- #[inline(always)]
- fn from_u32(n: u32) -> CharPos {
- CharPos(n as usize)
- }
-
- #[inline(always)]
- fn to_u32(&self) -> u32 {
- self.0 as u32
- }
-}
-
-impl Add for CharPos {
- type Output = CharPos;
-
- #[inline(always)]
- fn add(self, rhs: CharPos) -> CharPos {
- CharPos(self.to_usize() + rhs.to_usize())
- }
-}
-
-impl Sub for CharPos {
- type Output = CharPos;
-
- #[inline(always)]
- fn sub(self, rhs: CharPos) -> CharPos {
- CharPos(self.to_usize() - rhs.to_usize())
- }
-}
-
// _____________________________________________________________________________
// Loc, SourceFileAndLine, SourceFileAndBytePos
//
let _ = write!(self.out, "{}", ident.len());
// Write a separating `_` if necessary (leading digit or `_`).
- match ident.chars().next() {
- Some('_' | '0'..='9') => {
- self.push("_");
- }
- _ => {}
+ if let Some('_' | '0'..='9') = ident.chars().next() {
+ self.push("_");
}
self.push(ident);
/// # }
/// ```
pub fn reserve(&mut self, len: usize, additional: usize) {
- match self.try_reserve(len, additional) {
- Err(CapacityOverflow) => capacity_overflow(),
- Err(AllocError { layout, .. }) => handle_alloc_error(layout),
- Ok(()) => { /* yay */ }
- }
+ handle_reserve(self.try_reserve(len, additional));
}
/// The same as `reserve`, but returns on errors instead of panicking or aborting.
///
/// Aborts on OOM.
pub fn reserve_exact(&mut self, len: usize, additional: usize) {
- match self.try_reserve_exact(len, additional) {
- Err(CapacityOverflow) => capacity_overflow(),
- Err(AllocError { layout, .. }) => handle_alloc_error(layout),
- Ok(()) => { /* yay */ }
- }
+ handle_reserve(self.try_reserve_exact(len, additional));
}
/// The same as `reserve_exact`, but returns on errors instead of panicking or aborting.
///
/// Aborts on OOM.
pub fn shrink_to_fit(&mut self, amount: usize) {
- match self.shrink(amount) {
- Err(CapacityOverflow) => capacity_overflow(),
- Err(AllocError { layout, .. }) => handle_alloc_error(layout),
- Ok(()) => { /* yay */ }
- }
+ handle_reserve(self.shrink(amount));
}
}
}
}
+// Central function for reserve error handling.
+#[inline]
+fn handle_reserve(result: Result<(), TryReserveError>) {
+ match result {
+ Err(CapacityOverflow) => capacity_overflow(),
+ Err(AllocError { layout, .. }) => handle_alloc_error(layout),
+ Ok(()) => { /* yay */ }
+ }
+}
+
// We need to guarantee the following:
// * We don't ever allocate `> isize::MAX` byte-size objects.
// * We don't overflow `usize::MAX` and actually allocate too little.
#![feature(const_int_pow)]
#![feature(constctlz)]
#![feature(const_panic)]
+#![feature(const_pin)]
#![feature(const_fn_union)]
#![feature(const_generics)]
#![feature(const_option)]
/// be exposed through a public safe API.
/// Correspondingly, `ManuallyDrop::drop` is unsafe.
///
-/// # Examples
+/// # `ManuallyDrop` and drop order.
///
-/// This wrapper can be used to enforce a particular drop order on fields, regardless
-/// of how they are defined in the struct:
+/// Rust has a well-defined [drop order] of values. To make sure that fields or
+/// locals are dropped in a specific order, reorder the declarations such that
+/// the implicit drop order is the correct one.
///
-/// ```rust
-/// use std::mem::ManuallyDrop;
-/// struct Peach;
-/// struct Banana;
-/// struct Melon;
-/// struct FruitBox {
-/// // Immediately clear there’s something non-trivial going on with these fields.
-/// peach: ManuallyDrop<Peach>,
-/// melon: Melon, // Field that’s independent of the other two.
-/// banana: ManuallyDrop<Banana>,
-/// }
+/// It is possible to use `ManuallyDrop` to control the drop order, but this
+/// requires unsafe code and is hard to do correctly in the presence of
+/// unwinding.
///
-/// impl Drop for FruitBox {
-/// fn drop(&mut self) {
-/// unsafe {
-/// // Explicit ordering in which field destructors are run specified in the intuitive
-/// // location – the destructor of the structure containing the fields.
-/// // Moreover, one can now reorder fields within the struct however much they want.
-/// ManuallyDrop::drop(&mut self.peach);
-/// ManuallyDrop::drop(&mut self.banana);
-/// }
-/// // After destructor for `FruitBox` runs (this function), the destructor for Melon gets
-/// // invoked in the usual manner, as it is not wrapped in `ManuallyDrop`.
-/// }
-/// }
-/// ```
+/// For example, if you want to make sure that a specific field is dropped after
+/// the others, make it the last field of a struct:
///
-/// However, care should be taken when using this pattern as it can lead to *leak amplification*.
-/// In this example, if the `Drop` implementation for `Peach` were to panic, the `banana` field
-/// would also be leaked.
+/// ```
+/// struct Context;
///
-/// In contrast, the automatically-generated compiler drop implementation would have ensured
-/// that all fields are dropped even in the presence of panics. This is especially important when
-/// working with [pinned] data, where reusing the memory without calling the destructor could lead
-/// to Undefined Behaviour.
+/// struct Widget {
+/// children: Vec<Widget>,
+/// // `context` will be dropped after `children`.
+/// // Rust guarantees that fields are dropped in the order of declaration.
+/// context: Context,
+/// }
+/// ```
///
+/// [drop order]: https://doc.rust-lang.org/reference/destructors.html
/// [`mem::zeroed`]: crate::mem::zeroed
/// [`MaybeUninit<T>`]: crate::mem::MaybeUninit
-/// [pinned]: crate::pin
#[stable(feature = "manually_drop", since = "1.20.0")]
#[lang = "manually_drop"]
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
///
/// Unlike `Pin::new_unchecked`, this method is safe because the pointer
/// `P` dereferences to an [`Unpin`] type, which cancels the pinning guarantees.
- #[stable(feature = "pin", since = "1.33.0")]
#[inline(always)]
- pub fn new(pointer: P) -> Pin<P> {
+ #[rustc_const_unstable(feature = "const_pin", issue = "76654")]
+ #[stable(feature = "pin", since = "1.33.0")]
+ pub const fn new(pointer: P) -> Pin<P> {
// SAFETY: the value pointed to is `Unpin`, and so has no requirements
// around pinning.
unsafe { Pin::new_unchecked(pointer) }
///
/// This requires that the data inside this `Pin` is [`Unpin`] so that we
/// can ignore the pinning invariants when unwrapping it.
- #[stable(feature = "pin_into_inner", since = "1.39.0")]
#[inline(always)]
- pub fn into_inner(pin: Pin<P>) -> P {
+ #[rustc_const_unstable(feature = "const_pin", issue = "76654")]
+ #[stable(feature = "pin_into_inner", since = "1.39.0")]
+ pub const fn into_inner(pin: Pin<P>) -> P {
pin.pointer
}
}
///
/// [`mem::swap`]: crate::mem::swap
#[lang = "new_unchecked"]
- #[stable(feature = "pin", since = "1.33.0")]
#[inline(always)]
- pub unsafe fn new_unchecked(pointer: P) -> Pin<P> {
+ #[rustc_const_unstable(feature = "const_pin", issue = "76654")]
+ #[stable(feature = "pin", since = "1.33.0")]
+ pub const unsafe fn new_unchecked(pointer: P) -> Pin<P> {
Pin { pointer }
}
///
/// If the underlying data is [`Unpin`], [`Pin::into_inner`] should be used
/// instead.
- #[stable(feature = "pin_into_inner", since = "1.39.0")]
#[inline(always)]
- pub unsafe fn into_inner_unchecked(pin: Pin<P>) -> P {
+ #[rustc_const_unstable(feature = "const_pin", issue = "76654")]
+ #[stable(feature = "pin_into_inner", since = "1.39.0")]
+ pub const unsafe fn into_inner_unchecked(pin: Pin<P>) -> P {
pin.pointer
}
}
/// with the same lifetime as the original `Pin`.
///
/// ["pinning projections"]: self#projections-and-structural-pinning
- #[stable(feature = "pin", since = "1.33.0")]
#[inline(always)]
- pub fn get_ref(self) -> &'a T {
+ #[rustc_const_unstable(feature = "const_pin", issue = "76654")]
+ #[stable(feature = "pin", since = "1.33.0")]
+ pub const fn get_ref(self) -> &'a T {
self.pointer
}
}
impl<'a, T: ?Sized> Pin<&'a mut T> {
/// Converts this `Pin<&mut T>` into a `Pin<&T>` with the same lifetime.
- #[stable(feature = "pin", since = "1.33.0")]
#[inline(always)]
- pub fn into_ref(self) -> Pin<&'a T> {
+ #[rustc_const_unstable(feature = "const_pin", issue = "76654")]
+ #[stable(feature = "pin", since = "1.33.0")]
+ pub const fn into_ref(self) -> Pin<&'a T> {
Pin { pointer: self.pointer }
}
/// that lives for as long as the borrow of the `Pin`, not the lifetime of
/// the `Pin` itself. This method allows turning the `Pin` into a reference
/// with the same lifetime as the original `Pin`.
- #[stable(feature = "pin", since = "1.33.0")]
#[inline(always)]
- pub fn get_mut(self) -> &'a mut T
+ #[stable(feature = "pin", since = "1.33.0")]
+ #[rustc_const_unstable(feature = "const_pin", issue = "76654")]
+ pub const fn get_mut(self) -> &'a mut T
where
T: Unpin,
{
///
/// If the underlying data is `Unpin`, `Pin::get_mut` should be used
/// instead.
- #[stable(feature = "pin", since = "1.33.0")]
#[inline(always)]
- pub unsafe fn get_unchecked_mut(self) -> &'a mut T {
+ #[stable(feature = "pin", since = "1.33.0")]
+ #[rustc_const_unstable(feature = "const_pin", issue = "76654")]
+ pub const unsafe fn get_unchecked_mut(self) -> &'a mut T {
self.pointer
}
#![feature(iter_order_by)]
#![feature(cmp_min_max_by)]
#![feature(iter_map_while)]
+#![feature(const_mut_refs)]
+#![feature(const_pin)]
#![feature(const_slice_from_raw_parts)]
#![feature(const_raw_ptr_deref)]
#![feature(never_type)]
mod ops;
mod option;
mod pattern;
+mod pin;
mod ptr;
mod result;
mod slice;
--- /dev/null
+use core::pin::Pin;
+
+#[test]
+fn pin_const() {
+ // test that the methods of `Pin` are usable in a const context
+
+ const POINTER: &'static usize = &2;
+
+ const PINNED: Pin<&'static usize> = Pin::new(POINTER);
+ const PINNED_UNCHECKED: Pin<&'static usize> = unsafe { Pin::new_unchecked(POINTER) };
+ assert_eq!(PINNED_UNCHECKED, PINNED);
+
+ const INNER: &'static usize = Pin::into_inner(PINNED);
+ assert_eq!(INNER, POINTER);
+
+ const INNER_UNCHECKED: &'static usize = unsafe { Pin::into_inner_unchecked(PINNED) };
+ assert_eq!(INNER_UNCHECKED, POINTER);
+
+ const REF: &'static usize = PINNED.get_ref();
+ assert_eq!(REF, POINTER);
+
+ // Note: `pin_mut_const` tests that the methods of `Pin<&mut T>` are usable in a const context.
+ // A const fn is used because `&mut` is not (yet) usable in constants.
+ const fn pin_mut_const() {
+ let _ = Pin::new(&mut 2).into_ref();
+ let _ = Pin::new(&mut 2).get_mut();
+ let _ = unsafe { Pin::new(&mut 2).get_unchecked_mut() };
+ }
+
+ pin_mut_const();
+}
#[stable(feature = "rust1", since = "1.0.0")]
impl Write for Stdout {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ (&*self).write(buf)
+ }
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ (&*self).write_vectored(bufs)
+ }
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ io::Write::is_write_vectored(&&*self)
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ (&*self).flush()
+ }
+ fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+ (&*self).write_all(buf)
+ }
+ fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
+ (&*self).write_all_vectored(bufs)
+ }
+ fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
+ (&*self).write_fmt(args)
+ }
+}
+
+#[stable(feature = "write_mt", since = "1.48.0")]
+impl Write for &Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.lock().write(buf)
}
self.lock().write_fmt(args)
}
}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl Write for StdoutLock<'_> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
#[stable(feature = "rust1", since = "1.0.0")]
impl Write for Stderr {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ (&*self).write(buf)
+ }
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ (&*self).write_vectored(bufs)
+ }
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ io::Write::is_write_vectored(&&*self)
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ (&*self).flush()
+ }
+ fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+ (&*self).write_all(buf)
+ }
+ fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
+ (&*self).write_all_vectored(bufs)
+ }
+ fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
+ (&*self).write_fmt(args)
+ }
+}
+
+#[stable(feature = "write_mt", since = "1.48.0")]
+impl Write for &Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.lock().write(buf)
}
self.lock().write_fmt(args)
}
}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl Write for StderrLock<'_> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
}
}
+#[stable(feature = "write_mt", since = "1.48.0")]
+impl Write for &Sink {
+ #[inline]
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ Ok(buf.len())
+ }
+
+ #[inline]
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ let total_len = bufs.iter().map(|b| b.len()).sum();
+ Ok(total_len)
+ }
+
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ #[inline]
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Sink {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[stable(feature = "process", since = "1.0.0")]
impl Write for ChildStdin {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ (&*self).write(buf)
+ }
+
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ (&*self).write_vectored(bufs)
+ }
+
+ fn is_write_vectored(&self) -> bool {
+ io::Write::is_write_vectored(&&*self)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ (&*self).flush()
+ }
+}
+
+#[stable(feature = "write_mt", since = "1.48.0")]
+impl Write for &ChildStdin {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
}
}
- let for_ = if let Some(did) = did.as_local() {
- let hir_id = tcx.hir().local_def_id_to_hir_id(did);
- match tcx.hir().expect_item(hir_id).kind {
- hir::ItemKind::Impl { self_ty, .. } => self_ty.clean(cx),
- _ => panic!("did given to build_impl was not an impl"),
+ let impl_item = match did.as_local() {
+ Some(did) => {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(did);
+ match tcx.hir().expect_item(hir_id).kind {
+ hir::ItemKind::Impl { self_ty, ref generics, ref items, .. } => {
+ Some((self_ty, generics, items))
+ }
+ _ => panic!("`DefID` passed to `build_impl` is not an `impl"),
+ }
}
- } else {
- tcx.type_of(did).clean(cx)
+ None => None,
+ };
+
+ let for_ = match impl_item {
+ Some((self_ty, _, _)) => self_ty.clean(cx),
+ None => tcx.type_of(did).clean(cx),
};
// Only inline impl if the implementing type is
}
let predicates = tcx.explicit_predicates_of(did);
- let (trait_items, generics) = if let Some(did) = did.as_local() {
- let hir_id = tcx.hir().local_def_id_to_hir_id(did);
- match tcx.hir().expect_item(hir_id).kind {
- hir::ItemKind::Impl { ref generics, ref items, .. } => (
- items.iter().map(|item| tcx.hir().impl_item(item.id).clean(cx)).collect::<Vec<_>>(),
- generics.clean(cx),
- ),
- _ => panic!("did given to build_impl was not an impl"),
- }
- } else {
- (
+ let (trait_items, generics) = match impl_item {
+ Some((_, generics, items)) => (
+ items.iter().map(|item| tcx.hir().impl_item(item.id).clean(cx)).collect::<Vec<_>>(),
+ generics.clean(cx),
+ ),
+ None => (
tcx.associated_items(did)
.in_definition_order()
.filter_map(|item| {
})
.collect::<Vec<_>>(),
clean::enter_impl_trait(cx, || (tcx.generics_of(did), predicates).clean(cx)),
- )
+ ),
};
let polarity = tcx.impl_polarity(did);
let trait_ = associated_trait.clean(cx).map(|bound| match bound {
--- /dev/null
+// NOTE: This test doesn't actually require `fulldeps`
+// so we could instead use it as an `ui` test.
+//
+// Considering that all other `internal-lints` are tested here
+// this seems like the cleaner solution though.
+#![feature(rustc_attrs)]
+#![deny(rustc::ty_pass_by_reference)]
+#![allow(unused)]
+
+#[rustc_diagnostic_item = "TyCtxt"]
+struct TyCtxt<'tcx> {
+ inner: &'tcx (),
+}
+
+impl<'tcx> TyCtxt<'tcx> {
+ fn by_value(self) {} // OK
+ fn by_ref(&self) {} //~ ERROR passing `TyCtxt<'tcx>` by reference
+}
+
+
+struct TyS<'tcx> {
+ inner: &'tcx (),
+}
+
+#[rustc_diagnostic_item = "Ty"]
+type Ty<'tcx> = &'tcx TyS<'tcx>;
+
+impl<'tcx> TyS<'tcx> {
+ fn by_value(self: Ty<'tcx>) {}
+ fn by_ref(self: &Ty<'tcx>) {} //~ ERROR passing `Ty<'tcx>` by reference
+}
+
+fn main() {}
--- /dev/null
+error: passing `TyCtxt<'tcx>` by reference
+ --> $DIR/pass_ty_by_ref_self.rs:17:15
+ |
+LL | fn by_ref(&self) {}
+ | ^^^^^ help: try passing by value: `TyCtxt<'tcx>`
+ |
+note: the lint level is defined here
+ --> $DIR/pass_ty_by_ref_self.rs:7:9
+ |
+LL | #![deny(rustc::ty_pass_by_reference)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: passing `Ty<'tcx>` by reference
+ --> $DIR/pass_ty_by_ref_self.rs:30:21
+ |
+LL | fn by_ref(self: &Ty<'tcx>) {}
+ | ^^^^^^^^^ help: try passing by value: `Ty<'tcx>`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+// compile-flags: -Zsave-analysis
+
+#![feature(const_generics)]
+#![allow(incomplete_features)]
+struct Arr<const N: usize>
+where Assert::<{N < usize::max_value() / 2}>: IsTrue, //~ ERROR constant expression
+{
+}
+
+enum Assert<const CHECK: bool> {}
+
+trait IsTrue {}
+
+impl IsTrue for Assert<true> {}
+
+fn main() {
+ let x: Arr<{usize::max_value()}> = Arr {};
+ //~^ ERROR mismatched types
+ //~| ERROR mismatched types
+}
--- /dev/null
+error: constant expression depends on a generic parameter
+ --> $DIR/issue-73260.rs:6:47
+ |
+LL | where Assert::<{N < usize::max_value() / 2}>: IsTrue,
+ | ^^^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error[E0308]: mismatched types
+ --> $DIR/issue-73260.rs:17:12
+ |
+LL | let x: Arr<{usize::max_value()}> = Arr {};
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `false`, found `true`
+ |
+ = note: expected type `false`
+ found type `true`
+
+error[E0308]: mismatched types
+ --> $DIR/issue-73260.rs:17:40
+ |
+LL | let x: Arr<{usize::max_value()}> = Arr {};
+ | ^^^ expected `false`, found `true`
+ |
+ = note: expected type `false`
+ found type `true`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+#![feature(const_generics)]
+#![allow(incomplete_features)]
+
+trait If<const COND: bool> {}
+impl If<true> for () {}
+
+trait IsZero<const N: u8> {
+ type Answer;
+}
+
+struct True;
+struct False;
+
+impl<const N: u8> IsZero<N> for ()
+where (): If<{N == 0}> { //~ERROR constant expression
+ type Answer = True;
+}
+
+trait Foobar<const N: u8> {}
+
+impl<const N: u8> Foobar<N> for ()
+where (): IsZero<N, Answer = True> {}
+
+impl<const N: u8> Foobar<N> for ()
+where (): IsZero<N, Answer = False> {}
+
+fn main() {}
--- /dev/null
+error: constant expression depends on a generic parameter
+ --> $DIR/issue-74634.rs:15:11
+ |
+LL | where (): If<{N == 0}> {
+ | ^^^^^^^^^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: aborting due to previous error
+
--- /dev/null
+#![feature(const_generics, const_evaluatable_checked)]
+#![allow(incomplete_features)]
+
+struct Bool<const B: bool>;
+
+trait True {}
+
+impl True for Bool<true> {}
+
+fn test<T, const P: usize>() where Bool<{core::mem::size_of::<T>() > 4}>: True {
+ todo!()
+}
+
+fn main() {
+ test::<2>();
+ //~^ ERROR wrong number of type
+ //~| ERROR constant expression depends
+}
--- /dev/null
+error[E0107]: wrong number of type arguments: expected 1, found 0
+ --> $DIR/issue-76595.rs:15:5
+ |
+LL | test::<2>();
+ | ^^^^^^^^^ expected 1 type argument
+
+error: constant expression depends on a generic parameter
+ --> $DIR/issue-76595.rs:15:5
+ |
+LL | fn test<T, const P: usize>() where Bool<{core::mem::size_of::<T>() > 4}>: True {
+ | ---- required by this bound in `test`
+...
+LL | test::<2>();
+ | ^^^^^^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0107`.
|
LL | &mut *(box 0)
| ^^^^^^^^^^^^^
-help: skipping check for `const_mut_refs` feature
+help: skipping check that does not even have a feature gate
--> $DIR/box.rs:10:5
|
LL | &mut *(box 0)
warning: skipping const checks
|
-help: skipping check for `const_mut_refs` feature
+help: skipping check that does not even have a feature gate
--> $DIR/mutable_references.rs:9:26
|
LL | static FOO: &&mut u32 = &&mut 42;
| ^^^^^^^
-help: skipping check for `const_mut_refs` feature
+help: skipping check that does not even have a feature gate
--> $DIR/mutable_references.rs:13:23
|
LL | static BAR: &mut () = &mut ();
| ^^^^^^^
-help: skipping check for `const_mut_refs` feature
+help: skipping check that does not even have a feature gate
--> $DIR/mutable_references.rs:18:28
|
LL | static BOO: &mut Foo<()> = &mut Foo(());
|
LL | x: &UnsafeCell::new(42),
| ^^^^^^^^^^^^^^^^^^^^
-help: skipping check for `const_mut_refs` feature
+help: skipping check that does not even have a feature gate
--> $DIR/mutable_references.rs:30:27
|
LL | static OH_YES: &mut i32 = &mut 42;
|
LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-help: skipping check for `const_mut_refs` feature
+help: skipping check that does not even have a feature gate
--> $DIR/mutable_references_err.rs:30:25
|
LL | const BLUNT: &mut i32 = &mut 42;
--- /dev/null
+#![stable(feature = "core", since = "1.6.0")]
+#![feature(staged_api)]
+#![feature(const_precise_live_drops, const_fn)]
+
+enum Either<T, S> {
+ Left(T),
+ Right(S),
+}
+
+impl<T> Either<T, T> {
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_stable(feature = "foo", since = "1.0.0")]
+ pub const fn unwrap(self) -> T {
+ //~^ ERROR destructors cannot be evaluated at compile-time
+ match self {
+ Self::Left(t) => t,
+ Self::Right(t) => t,
+ }
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0493]: destructors cannot be evaluated at compile-time
+ --> $DIR/stable-precise-live-drops-in-libcore.rs:13:25
+ |
+LL | pub const fn unwrap(self) -> T {
+ | ^^^^ constant functions cannot evaluate destructors
+...
+LL | }
+ | - value is dropped here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0493`.
--- /dev/null
+// check-pass
+
+#![stable(feature = "core", since = "1.6.0")]
+#![feature(staged_api)]
+#![feature(const_precise_live_drops)]
+
+enum Either<T, S> {
+ Left(T),
+ Right(S),
+}
+
+impl<T> Either<T, T> {
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "foo", issue = "none")]
+ pub const fn unwrap(self) -> T {
+ match self {
+ Self::Left(t) => t,
+ Self::Right(t) => t,
+ }
+ }
+}
+
+fn main() {}
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0756`.
// injected intrinsics by the compiler.
#![deny(missing_docs)]
#![allow(dead_code)]
-#![feature(associated_type_defaults)]
+#![feature(associated_type_defaults, extern_types)]
//! Some garbage docs for the crate here
#![doc="More garbage"]
pub use internal_impl::globbed::*;
}
+extern "C" {
+ /// dox
+ pub fn extern_fn_documented(f: f32) -> f32;
+ pub fn extern_fn_undocumented(f: f32) -> f32;
+ //~^ ERROR: missing documentation for a function
+
+ /// dox
+ pub static EXTERN_STATIC_DOCUMENTED: u8;
+ pub static EXTERN_STATIC_UNDOCUMENTED: u8;
+ //~^ ERROR: missing documentation for a static
+
+ /// dox
+ pub type ExternTyDocumented;
+ pub type ExternTyUndocumented;
+ //~^ ERROR: missing documentation for a foreign type
+}
+
fn main() {}
LL | pub fn also_undocumented1() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 19 previous errors
+error: missing documentation for a function
+ --> $DIR/lint-missing-doc.rs:189:5
+ |
+LL | pub fn extern_fn_undocumented(f: f32) -> f32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a static
+ --> $DIR/lint-missing-doc.rs:194:5
+ |
+LL | pub static EXTERN_STATIC_UNDOCUMENTED: u8;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a foreign type
+ --> $DIR/lint-missing-doc.rs:199:5
+ |
+LL | pub type ExternTyUndocumented;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 22 previous errors