Auto merge of #66242 - Centril:rollup-h73ztr1, r=Centril
Rollup of 6 pull requests Successful merges: - #65949 (Move promotion into its own pass) - #65994 (Point at where clauses where the associated item was restricted) - #66050 (Fix C aggregate-passing ABI on powerpc) - #66134 (Point at formatting descriptor string when it is invalid) - #66172 (Stabilize @file command line arguments) - #66226 (add link to unstable book for asm! macro) Failed merges: r? @ghost
This commit is contained in:
commit
5a5027519a
|
@ -1274,6 +1274,10 @@ pub(crate) mod builtin {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inline assembly.
|
/// Inline assembly.
|
||||||
|
///
|
||||||
|
/// Read the [unstable book] for the usage.
|
||||||
|
///
|
||||||
|
/// [unstable book]: ../unstable-book/library-features/asm.html
|
||||||
#[unstable(feature = "asm", issue = "29722",
|
#[unstable(feature = "asm", issue = "29722",
|
||||||
reason = "inline assembly is not stable enough for use and is subject to change")]
|
reason = "inline assembly is not stable enough for use and is subject to change")]
|
||||||
#[rustc_builtin_macro]
|
#[rustc_builtin_macro]
|
||||||
|
|
|
@ -35,7 +35,7 @@ impl InnerOffset {
|
||||||
|
|
||||||
/// A piece is a portion of the format string which represents the next part
|
/// A piece is a portion of the format string which represents the next part
|
||||||
/// to emit. These are emitted as a stream by the `Parser` class.
|
/// to emit. These are emitted as a stream by the `Parser` class.
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum Piece<'a> {
|
pub enum Piece<'a> {
|
||||||
/// A literal string which should directly be emitted
|
/// A literal string which should directly be emitted
|
||||||
String(&'a str),
|
String(&'a str),
|
||||||
|
@ -45,7 +45,7 @@ pub enum Piece<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Representation of an argument specification.
|
/// Representation of an argument specification.
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct Argument<'a> {
|
pub struct Argument<'a> {
|
||||||
/// Where to find this argument
|
/// Where to find this argument
|
||||||
pub position: Position,
|
pub position: Position,
|
||||||
|
@ -54,7 +54,7 @@ pub struct Argument<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specification for the formatting of an argument in the format string.
|
/// Specification for the formatting of an argument in the format string.
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct FormatSpec<'a> {
|
pub struct FormatSpec<'a> {
|
||||||
/// Optionally specified character to fill alignment with.
|
/// Optionally specified character to fill alignment with.
|
||||||
pub fill: Option<char>,
|
pub fill: Option<char>,
|
||||||
|
@ -74,10 +74,12 @@ pub struct FormatSpec<'a> {
|
||||||
/// this argument, this can be empty or any number of characters, although
|
/// this argument, this can be empty or any number of characters, although
|
||||||
/// it is required to be one word.
|
/// it is required to be one word.
|
||||||
pub ty: &'a str,
|
pub ty: &'a str,
|
||||||
|
/// The span of the descriptor string (for diagnostics).
|
||||||
|
pub ty_span: Option<InnerSpan>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enum describing where an argument for a format can be located.
|
/// Enum describing where an argument for a format can be located.
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum Position {
|
pub enum Position {
|
||||||
/// The argument is implied to be located at an index
|
/// The argument is implied to be located at an index
|
||||||
ArgumentImplicitlyIs(usize),
|
ArgumentImplicitlyIs(usize),
|
||||||
|
@ -97,7 +99,7 @@ impl Position {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enum of alignments which are supported.
|
/// Enum of alignments which are supported.
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum Alignment {
|
pub enum Alignment {
|
||||||
/// The value will be aligned to the left.
|
/// The value will be aligned to the left.
|
||||||
AlignLeft,
|
AlignLeft,
|
||||||
|
@ -111,7 +113,7 @@ pub enum Alignment {
|
||||||
|
|
||||||
/// Various flags which can be applied to format strings. The meaning of these
|
/// Various flags which can be applied to format strings. The meaning of these
|
||||||
/// flags is defined by the formatters themselves.
|
/// flags is defined by the formatters themselves.
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum Flag {
|
pub enum Flag {
|
||||||
/// A `+` will be used to denote positive numbers.
|
/// A `+` will be used to denote positive numbers.
|
||||||
FlagSignPlus,
|
FlagSignPlus,
|
||||||
|
@ -131,7 +133,7 @@ pub enum Flag {
|
||||||
|
|
||||||
/// A count is used for the precision and width parameters of an integer, and
|
/// A count is used for the precision and width parameters of an integer, and
|
||||||
/// can reference either an argument or a literal integer.
|
/// can reference either an argument or a literal integer.
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum Count {
|
pub enum Count {
|
||||||
/// The count is specified explicitly.
|
/// The count is specified explicitly.
|
||||||
CountIs(usize),
|
CountIs(usize),
|
||||||
|
@ -475,6 +477,7 @@ impl<'a> Parser<'a> {
|
||||||
width: CountImplied,
|
width: CountImplied,
|
||||||
width_span: None,
|
width_span: None,
|
||||||
ty: &self.input[..0],
|
ty: &self.input[..0],
|
||||||
|
ty_span: None,
|
||||||
};
|
};
|
||||||
if !self.consume(':') {
|
if !self.consume(':') {
|
||||||
return spec;
|
return spec;
|
||||||
|
@ -548,6 +551,7 @@ impl<'a> Parser<'a> {
|
||||||
spec.precision_span = sp;
|
spec.precision_span = sp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let ty_span_start = self.cur.peek().map(|(pos, _)| *pos);
|
||||||
// Optional radix followed by the actual format specifier
|
// Optional radix followed by the actual format specifier
|
||||||
if self.consume('x') {
|
if self.consume('x') {
|
||||||
if self.consume('?') {
|
if self.consume('?') {
|
||||||
|
@ -567,6 +571,12 @@ impl<'a> Parser<'a> {
|
||||||
spec.ty = "?";
|
spec.ty = "?";
|
||||||
} else {
|
} else {
|
||||||
spec.ty = self.word();
|
spec.ty = self.word();
|
||||||
|
let ty_span_end = self.cur.peek().map(|(pos, _)| *pos);
|
||||||
|
if !spec.ty.is_empty() {
|
||||||
|
spec.ty_span = ty_span_start
|
||||||
|
.and_then(|s| ty_span_end.map(|e| (s, e)))
|
||||||
|
.map(|(start, end)| self.to_span_index(start).to(self.to_span_index(end)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
spec
|
spec
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use super::*;
|
||||||
|
|
||||||
fn same(fmt: &'static str, p: &[Piece<'static>]) {
|
fn same(fmt: &'static str, p: &[Piece<'static>]) {
|
||||||
let parser = Parser::new(fmt, None, vec![], false);
|
let parser = Parser::new(fmt, None, vec![], false);
|
||||||
assert!(parser.collect::<Vec<Piece<'static>>>() == p);
|
assert_eq!(parser.collect::<Vec<Piece<'static>>>(), p);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmtdflt() -> FormatSpec<'static> {
|
fn fmtdflt() -> FormatSpec<'static> {
|
||||||
|
@ -15,6 +15,7 @@ fn fmtdflt() -> FormatSpec<'static> {
|
||||||
precision_span: None,
|
precision_span: None,
|
||||||
width_span: None,
|
width_span: None,
|
||||||
ty: "",
|
ty: "",
|
||||||
|
ty_span: None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +83,7 @@ fn format_position_nothing_else() {
|
||||||
#[test]
|
#[test]
|
||||||
fn format_type() {
|
fn format_type() {
|
||||||
same(
|
same(
|
||||||
"{3:a}",
|
"{3:x}",
|
||||||
&[NextArgument(Argument {
|
&[NextArgument(Argument {
|
||||||
position: ArgumentIs(3),
|
position: ArgumentIs(3),
|
||||||
format: FormatSpec {
|
format: FormatSpec {
|
||||||
|
@ -93,7 +94,8 @@ fn format_type() {
|
||||||
width: CountImplied,
|
width: CountImplied,
|
||||||
precision_span: None,
|
precision_span: None,
|
||||||
width_span: None,
|
width_span: None,
|
||||||
ty: "a",
|
ty: "x",
|
||||||
|
ty_span: None,
|
||||||
},
|
},
|
||||||
})]);
|
})]);
|
||||||
}
|
}
|
||||||
|
@ -112,6 +114,7 @@ fn format_align_fill() {
|
||||||
precision_span: None,
|
precision_span: None,
|
||||||
width_span: None,
|
width_span: None,
|
||||||
ty: "",
|
ty: "",
|
||||||
|
ty_span: None,
|
||||||
},
|
},
|
||||||
})]);
|
})]);
|
||||||
same(
|
same(
|
||||||
|
@ -127,6 +130,7 @@ fn format_align_fill() {
|
||||||
precision_span: None,
|
precision_span: None,
|
||||||
width_span: None,
|
width_span: None,
|
||||||
ty: "",
|
ty: "",
|
||||||
|
ty_span: None,
|
||||||
},
|
},
|
||||||
})]);
|
})]);
|
||||||
same(
|
same(
|
||||||
|
@ -142,6 +146,7 @@ fn format_align_fill() {
|
||||||
precision_span: None,
|
precision_span: None,
|
||||||
width_span: None,
|
width_span: None,
|
||||||
ty: "abcd",
|
ty: "abcd",
|
||||||
|
ty_span: Some(InnerSpan::new(6, 10)),
|
||||||
},
|
},
|
||||||
})]);
|
})]);
|
||||||
}
|
}
|
||||||
|
@ -150,7 +155,7 @@ fn format_counts() {
|
||||||
use syntax_pos::{GLOBALS, Globals, edition};
|
use syntax_pos::{GLOBALS, Globals, edition};
|
||||||
GLOBALS.set(&Globals::new(edition::DEFAULT_EDITION), || {
|
GLOBALS.set(&Globals::new(edition::DEFAULT_EDITION), || {
|
||||||
same(
|
same(
|
||||||
"{:10s}",
|
"{:10x}",
|
||||||
&[NextArgument(Argument {
|
&[NextArgument(Argument {
|
||||||
position: ArgumentImplicitlyIs(0),
|
position: ArgumentImplicitlyIs(0),
|
||||||
format: FormatSpec {
|
format: FormatSpec {
|
||||||
|
@ -161,11 +166,12 @@ fn format_counts() {
|
||||||
width: CountIs(10),
|
width: CountIs(10),
|
||||||
precision_span: None,
|
precision_span: None,
|
||||||
width_span: None,
|
width_span: None,
|
||||||
ty: "s",
|
ty: "x",
|
||||||
|
ty_span: None,
|
||||||
},
|
},
|
||||||
})]);
|
})]);
|
||||||
same(
|
same(
|
||||||
"{:10$.10s}",
|
"{:10$.10x}",
|
||||||
&[NextArgument(Argument {
|
&[NextArgument(Argument {
|
||||||
position: ArgumentImplicitlyIs(0),
|
position: ArgumentImplicitlyIs(0),
|
||||||
format: FormatSpec {
|
format: FormatSpec {
|
||||||
|
@ -176,11 +182,12 @@ fn format_counts() {
|
||||||
width: CountIsParam(10),
|
width: CountIsParam(10),
|
||||||
precision_span: None,
|
precision_span: None,
|
||||||
width_span: Some(InnerSpan::new(3, 6)),
|
width_span: Some(InnerSpan::new(3, 6)),
|
||||||
ty: "s",
|
ty: "x",
|
||||||
|
ty_span: None,
|
||||||
},
|
},
|
||||||
})]);
|
})]);
|
||||||
same(
|
same(
|
||||||
"{:.*s}",
|
"{:.*x}",
|
||||||
&[NextArgument(Argument {
|
&[NextArgument(Argument {
|
||||||
position: ArgumentImplicitlyIs(1),
|
position: ArgumentImplicitlyIs(1),
|
||||||
format: FormatSpec {
|
format: FormatSpec {
|
||||||
|
@ -191,11 +198,12 @@ fn format_counts() {
|
||||||
width: CountImplied,
|
width: CountImplied,
|
||||||
precision_span: Some(InnerSpan::new(3, 5)),
|
precision_span: Some(InnerSpan::new(3, 5)),
|
||||||
width_span: None,
|
width_span: None,
|
||||||
ty: "s",
|
ty: "x",
|
||||||
|
ty_span: None,
|
||||||
},
|
},
|
||||||
})]);
|
})]);
|
||||||
same(
|
same(
|
||||||
"{:.10$s}",
|
"{:.10$x}",
|
||||||
&[NextArgument(Argument {
|
&[NextArgument(Argument {
|
||||||
position: ArgumentImplicitlyIs(0),
|
position: ArgumentImplicitlyIs(0),
|
||||||
format: FormatSpec {
|
format: FormatSpec {
|
||||||
|
@ -206,11 +214,12 @@ fn format_counts() {
|
||||||
width: CountImplied,
|
width: CountImplied,
|
||||||
precision_span: Some(InnerSpan::new(3, 7)),
|
precision_span: Some(InnerSpan::new(3, 7)),
|
||||||
width_span: None,
|
width_span: None,
|
||||||
ty: "s",
|
ty: "x",
|
||||||
|
ty_span: None,
|
||||||
},
|
},
|
||||||
})]);
|
})]);
|
||||||
same(
|
same(
|
||||||
"{:a$.b$s}",
|
"{:a$.b$?}",
|
||||||
&[NextArgument(Argument {
|
&[NextArgument(Argument {
|
||||||
position: ArgumentImplicitlyIs(0),
|
position: ArgumentImplicitlyIs(0),
|
||||||
format: FormatSpec {
|
format: FormatSpec {
|
||||||
|
@ -221,7 +230,8 @@ fn format_counts() {
|
||||||
width: CountIsName(Symbol::intern("a")),
|
width: CountIsName(Symbol::intern("a")),
|
||||||
precision_span: None,
|
precision_span: None,
|
||||||
width_span: None,
|
width_span: None,
|
||||||
ty: "s",
|
ty: "?",
|
||||||
|
ty_span: None,
|
||||||
},
|
},
|
||||||
})]);
|
})]);
|
||||||
});
|
});
|
||||||
|
@ -241,6 +251,7 @@ fn format_flags() {
|
||||||
precision_span: None,
|
precision_span: None,
|
||||||
width_span: None,
|
width_span: None,
|
||||||
ty: "",
|
ty: "",
|
||||||
|
ty_span: None,
|
||||||
},
|
},
|
||||||
})]);
|
})]);
|
||||||
same(
|
same(
|
||||||
|
@ -256,13 +267,14 @@ fn format_flags() {
|
||||||
precision_span: None,
|
precision_span: None,
|
||||||
width_span: None,
|
width_span: None,
|
||||||
ty: "",
|
ty: "",
|
||||||
|
ty_span: None,
|
||||||
},
|
},
|
||||||
})]);
|
})]);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn format_mixture() {
|
fn format_mixture() {
|
||||||
same(
|
same(
|
||||||
"abcd {3:a} efg",
|
"abcd {3:x} efg",
|
||||||
&[
|
&[
|
||||||
String("abcd "),
|
String("abcd "),
|
||||||
NextArgument(Argument {
|
NextArgument(Argument {
|
||||||
|
@ -275,7 +287,8 @@ fn format_mixture() {
|
||||||
width: CountImplied,
|
width: CountImplied,
|
||||||
precision_span: None,
|
precision_span: None,
|
||||||
width_span: None,
|
width_span: None,
|
||||||
ty: "a",
|
ty: "x",
|
||||||
|
ty_span: None,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
String(" efg"),
|
String(" efg"),
|
||||||
|
|
|
@ -93,7 +93,7 @@ rustc_queries! {
|
||||||
/// Maps DefId's that have an associated `mir::Body` to the result
|
/// Maps DefId's that have an associated `mir::Body` to the result
|
||||||
/// of the MIR qualify_consts pass. The actual meaning of
|
/// of the MIR qualify_consts pass. The actual meaning of
|
||||||
/// the value isn't known except to the pass itself.
|
/// the value isn't known except to the pass itself.
|
||||||
query mir_const_qualif(key: DefId) -> (u8, &'tcx BitSet<mir::Local>) {
|
query mir_const_qualif(key: DefId) -> u8 {
|
||||||
desc { |tcx| "const checking `{}`", tcx.def_path_str(key) }
|
desc { |tcx| "const checking `{}`", tcx.def_path_str(key) }
|
||||||
cache_on_disk_if { key.is_local() }
|
cache_on_disk_if { key.is_local() }
|
||||||
}
|
}
|
||||||
|
|
|
@ -2287,11 +2287,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ObligationCauseCode::AssocTypeBound(impl_span, orig) => {
|
ObligationCauseCode::AssocTypeBound(ref data) => {
|
||||||
err.span_label(orig, "associated type defined here");
|
err.span_label(data.original, "associated type defined here");
|
||||||
if let Some(sp) = impl_span {
|
if let Some(sp) = data.impl_span {
|
||||||
err.span_label(sp, "in this `impl` item");
|
err.span_label(sp, "in this `impl` item");
|
||||||
}
|
}
|
||||||
|
for sp in &data.bounds {
|
||||||
|
err.span_label(*sp, "restricted in this bound");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -276,7 +276,14 @@ pub enum ObligationCauseCode<'tcx> {
|
||||||
/// #[feature(trivial_bounds)] is not enabled
|
/// #[feature(trivial_bounds)] is not enabled
|
||||||
TrivialBound,
|
TrivialBound,
|
||||||
|
|
||||||
AssocTypeBound(/*impl*/ Option<Span>, /*original*/ Span),
|
AssocTypeBound(Box<AssocTypeBoundData>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct AssocTypeBoundData {
|
||||||
|
pub impl_span: Option<Span>,
|
||||||
|
pub original: Span,
|
||||||
|
pub bounds: Vec<Span>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger.
|
// `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger.
|
||||||
|
|
|
@ -549,7 +549,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
|
||||||
super::MethodReceiver => Some(super::MethodReceiver),
|
super::MethodReceiver => Some(super::MethodReceiver),
|
||||||
super::BlockTailExpression(id) => Some(super::BlockTailExpression(id)),
|
super::BlockTailExpression(id) => Some(super::BlockTailExpression(id)),
|
||||||
super::TrivialBound => Some(super::TrivialBound),
|
super::TrivialBound => Some(super::TrivialBound),
|
||||||
super::AssocTypeBound(impl_sp, sp) => Some(super::AssocTypeBound(impl_sp, sp)),
|
super::AssocTypeBound(ref data) => Some(super::AssocTypeBound(data.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,6 @@ use crate::util::common::ErrorReported;
|
||||||
use crate::util::profiling::ProfileCategory::*;
|
use crate::util::profiling::ProfileCategory::*;
|
||||||
|
|
||||||
use rustc_data_structures::svh::Svh;
|
use rustc_data_structures::svh::Svh;
|
||||||
use rustc_index::bit_set::BitSet;
|
|
||||||
use rustc_index::vec::IndexVec;
|
use rustc_index::vec::IndexVec;
|
||||||
use rustc_data_structures::fx::{FxIndexMap, FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxIndexMap, FxHashMap, FxHashSet};
|
||||||
use rustc_data_structures::stable_hasher::StableVec;
|
use rustc_data_structures::stable_hasher::StableVec;
|
||||||
|
|
|
@ -2,9 +2,10 @@ use crate::hir;
|
||||||
use crate::hir::def_id::DefId;
|
use crate::hir::def_id::DefId;
|
||||||
use crate::infer::InferCtxt;
|
use crate::infer::InferCtxt;
|
||||||
use crate::ty::subst::SubstsRef;
|
use crate::ty::subst::SubstsRef;
|
||||||
use crate::traits;
|
use crate::traits::{self, AssocTypeBoundData};
|
||||||
use crate::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
|
use crate::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
|
use syntax::symbol::{kw, Ident};
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
use crate::middle::lang_items;
|
use crate::middle::lang_items;
|
||||||
use crate::mir::interpret::ConstValue;
|
use crate::mir::interpret::ConstValue;
|
||||||
|
@ -176,6 +177,23 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
||||||
pred: &ty::Predicate<'_>,
|
pred: &ty::Predicate<'_>,
|
||||||
trait_assoc_items: ty::AssocItemsIterator<'_>,
|
trait_assoc_items: ty::AssocItemsIterator<'_>,
|
||||||
| {
|
| {
|
||||||
|
let trait_item = tcx.hir().as_local_hir_id(trait_ref.def_id).and_then(|trait_id| {
|
||||||
|
tcx.hir().find(trait_id)
|
||||||
|
});
|
||||||
|
let (trait_name, trait_generics) = match trait_item {
|
||||||
|
Some(hir::Node::Item(hir::Item {
|
||||||
|
ident,
|
||||||
|
kind: hir::ItemKind::Trait(.., generics, _, _),
|
||||||
|
..
|
||||||
|
})) |
|
||||||
|
Some(hir::Node::Item(hir::Item {
|
||||||
|
ident,
|
||||||
|
kind: hir::ItemKind::TraitAlias(generics, _),
|
||||||
|
..
|
||||||
|
})) => (Some(ident), Some(generics)),
|
||||||
|
_ => (None, None),
|
||||||
|
};
|
||||||
|
|
||||||
let item_span = item.map(|i| tcx.sess.source_map().def_span(i.span));
|
let item_span = item.map(|i| tcx.sess.source_map().def_span(i.span));
|
||||||
match pred {
|
match pred {
|
||||||
ty::Predicate::Projection(proj) => {
|
ty::Predicate::Projection(proj) => {
|
||||||
|
@ -226,10 +244,11 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
||||||
item.ident == trait_assoc_item.ident
|
item.ident == trait_assoc_item.ident
|
||||||
}).next() {
|
}).next() {
|
||||||
cause.span = impl_item.span;
|
cause.span = impl_item.span;
|
||||||
cause.code = traits::AssocTypeBound(
|
cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData {
|
||||||
item_span,
|
impl_span: item_span,
|
||||||
trait_assoc_item.ident.span,
|
original: trait_assoc_item.ident.span,
|
||||||
);
|
bounds: vec![],
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,14 +270,13 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
||||||
// LL | type Assoc = bool;
|
// LL | type Assoc = bool;
|
||||||
// | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
|
// | ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
|
||||||
//
|
//
|
||||||
// FIXME: if the obligation comes from the where clause in the `trait`, we
|
// If the obligation comes from the where clause in the `trait`, we point at it:
|
||||||
// should point at it:
|
|
||||||
//
|
//
|
||||||
// error[E0277]: the trait bound `bool: Bar` is not satisfied
|
// error[E0277]: the trait bound `bool: Bar` is not satisfied
|
||||||
// --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5
|
// --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5
|
||||||
// |
|
// |
|
||||||
// | trait Foo where <Self as Foo>>::Assoc: Bar {
|
// | trait Foo where <Self as Foo>>::Assoc: Bar {
|
||||||
// | -------------------------- obligation set here
|
// | -------------------------- restricted in this bound
|
||||||
// LL | type Assoc;
|
// LL | type Assoc;
|
||||||
// | ----- associated type defined here
|
// | ----- associated type defined here
|
||||||
// ...
|
// ...
|
||||||
|
@ -278,11 +296,17 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
||||||
.next()
|
.next()
|
||||||
.map(|impl_item| (impl_item, trait_assoc_item)))
|
.map(|impl_item| (impl_item, trait_assoc_item)))
|
||||||
{
|
{
|
||||||
|
let bounds = trait_generics.map(|generics| get_generic_bound_spans(
|
||||||
|
&generics,
|
||||||
|
trait_name,
|
||||||
|
trait_assoc_item.ident,
|
||||||
|
)).unwrap_or_else(Vec::new);
|
||||||
cause.span = impl_item.span;
|
cause.span = impl_item.span;
|
||||||
cause.code = traits::AssocTypeBound(
|
cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData {
|
||||||
item_span,
|
impl_span: item_span,
|
||||||
trait_assoc_item.ident.span,
|
original: trait_assoc_item.ident.span,
|
||||||
);
|
bounds,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,3 +690,56 @@ pub fn object_region_bounds<'tcx>(
|
||||||
|
|
||||||
tcx.required_region_bounds(open_ty, predicates)
|
tcx.required_region_bounds(open_ty, predicates)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find the span of a generic bound affecting an associated type.
|
||||||
|
fn get_generic_bound_spans(
|
||||||
|
generics: &hir::Generics,
|
||||||
|
trait_name: Option<&Ident>,
|
||||||
|
assoc_item_name: Ident,
|
||||||
|
) -> Vec<Span> {
|
||||||
|
let mut bounds = vec![];
|
||||||
|
for clause in generics.where_clause.predicates.iter() {
|
||||||
|
if let hir::WherePredicate::BoundPredicate(pred) = clause {
|
||||||
|
match &pred.bounded_ty.kind {
|
||||||
|
hir::TyKind::Path(hir::QPath::Resolved(Some(ty), path)) => {
|
||||||
|
let mut s = path.segments.iter();
|
||||||
|
if let (a, Some(b), None) = (s.next(), s.next(), s.next()) {
|
||||||
|
if a.map(|s| &s.ident) == trait_name
|
||||||
|
&& b.ident == assoc_item_name
|
||||||
|
&& is_self_path(&ty.kind)
|
||||||
|
{
|
||||||
|
// `<Self as Foo>::Bar`
|
||||||
|
bounds.push(pred.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hir::TyKind::Path(hir::QPath::TypeRelative(ty, segment)) => {
|
||||||
|
if segment.ident == assoc_item_name {
|
||||||
|
if is_self_path(&ty.kind) {
|
||||||
|
// `Self::Bar`
|
||||||
|
bounds.push(pred.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_self_path(kind: &hir::TyKind) -> bool {
|
||||||
|
match kind {
|
||||||
|
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
|
||||||
|
let mut s = path.segments.iter();
|
||||||
|
if let (Some(segment), None) = (s.next(), s.next()) {
|
||||||
|
if segment.ident.name == kw::SelfUpper {
|
||||||
|
// `type(Self)`
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
|
@ -3,22 +3,12 @@ use std::fmt;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
|
|
||||||
static USED_ARGSFILE_FEATURE: AtomicBool = AtomicBool::new(false);
|
|
||||||
|
|
||||||
pub fn used_unstable_argsfile() -> bool {
|
|
||||||
USED_ARGSFILE_FEATURE.load(Ordering::Relaxed)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn arg_expand(arg: String) -> Result<Vec<String>, Error> {
|
pub fn arg_expand(arg: String) -> Result<Vec<String>, Error> {
|
||||||
if arg.starts_with("@") {
|
if arg.starts_with("@") {
|
||||||
let path = &arg[1..];
|
let path = &arg[1..];
|
||||||
let file = match fs::read_to_string(path) {
|
let file = match fs::read_to_string(path) {
|
||||||
Ok(file) => {
|
Ok(file) => file,
|
||||||
USED_ARGSFILE_FEATURE.store(true, Ordering::Relaxed);
|
|
||||||
file
|
|
||||||
}
|
|
||||||
Err(ref err) if err.kind() == io::ErrorKind::InvalidData => {
|
Err(ref err) if err.kind() == io::ErrorKind::InvalidData => {
|
||||||
return Err(Error::Utf8Error(Some(path.to_string())));
|
return Err(Error::Utf8Error(Some(path.to_string())));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1044,12 +1044,6 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
|
||||||
// (unstable option being used on stable)
|
// (unstable option being used on stable)
|
||||||
nightly_options::check_nightly_options(&matches, &config::rustc_optgroups());
|
nightly_options::check_nightly_options(&matches, &config::rustc_optgroups());
|
||||||
|
|
||||||
// Late check to see if @file was used without unstable options enabled
|
|
||||||
if crate::args::used_unstable_argsfile() && !nightly_options::is_unstable_enabled(&matches) {
|
|
||||||
early_error(ErrorOutputType::default(),
|
|
||||||
"@path is unstable - use -Z unstable-options to enable its use");
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches.opt_present("h") || matches.opt_present("help") {
|
if matches.opt_present("h") || matches.opt_present("help") {
|
||||||
// Only show unstable options in --help if we accept unstable options.
|
// Only show unstable options in --help if we accept unstable options.
|
||||||
usage(matches.opt_present("verbose"), nightly_options::is_unstable_enabled(&matches));
|
usage(matches.opt_present("verbose"), nightly_options::is_unstable_enabled(&matches));
|
||||||
|
|
|
@ -32,7 +32,6 @@ use syntax::parse::parser::emit_unclosed_delims;
|
||||||
use syntax::source_map::Spanned;
|
use syntax::source_map::Spanned;
|
||||||
use syntax::symbol::Symbol;
|
use syntax::symbol::Symbol;
|
||||||
use syntax_pos::{Span, FileName};
|
use syntax_pos::{Span, FileName};
|
||||||
use rustc_index::bit_set::BitSet;
|
|
||||||
|
|
||||||
macro_rules! provide {
|
macro_rules! provide {
|
||||||
(<$lt:tt> $tcx:ident, $def_id:ident, $other:ident, $cdata:ident,
|
(<$lt:tt> $tcx:ident, $def_id:ident, $other:ident, $cdata:ident,
|
||||||
|
@ -122,9 +121,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
|
||||||
}
|
}
|
||||||
optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) }
|
optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) }
|
||||||
promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) }
|
promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) }
|
||||||
mir_const_qualif => {
|
mir_const_qualif => { cdata.mir_const_qualif(def_id.index) }
|
||||||
(cdata.mir_const_qualif(def_id.index), tcx.arena.alloc(BitSet::new_empty(0)))
|
|
||||||
}
|
|
||||||
fn_sig => { cdata.fn_sig(def_id.index, tcx) }
|
fn_sig => { cdata.fn_sig(def_id.index, tcx) }
|
||||||
inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) }
|
inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) }
|
||||||
is_const_fn_raw => { cdata.is_const_fn_raw(def_id.index) }
|
is_const_fn_raw => { cdata.is_const_fn_raw(def_id.index) }
|
||||||
|
|
|
@ -955,7 +955,7 @@ impl EncodeContext<'tcx> {
|
||||||
record!(self.per_def.kind[def_id] <- match impl_item.kind {
|
record!(self.per_def.kind[def_id] <- match impl_item.kind {
|
||||||
ty::AssocKind::Const => {
|
ty::AssocKind::Const => {
|
||||||
if let hir::ImplItemKind::Const(_, body_id) = ast_item.kind {
|
if let hir::ImplItemKind::Const(_, body_id) = ast_item.kind {
|
||||||
let mir = self.tcx.at(ast_item.span).mir_const_qualif(def_id).0;
|
let mir = self.tcx.at(ast_item.span).mir_const_qualif(def_id);
|
||||||
|
|
||||||
EntryKind::AssocConst(container,
|
EntryKind::AssocConst(container,
|
||||||
ConstQualif { mir },
|
ConstQualif { mir },
|
||||||
|
@ -1089,7 +1089,7 @@ impl EncodeContext<'tcx> {
|
||||||
hir::ItemKind::Static(_, hir::MutMutable, _) => EntryKind::MutStatic,
|
hir::ItemKind::Static(_, hir::MutMutable, _) => EntryKind::MutStatic,
|
||||||
hir::ItemKind::Static(_, hir::MutImmutable, _) => EntryKind::ImmStatic,
|
hir::ItemKind::Static(_, hir::MutImmutable, _) => EntryKind::ImmStatic,
|
||||||
hir::ItemKind::Const(_, body_id) => {
|
hir::ItemKind::Const(_, body_id) => {
|
||||||
let mir = self.tcx.at(item.span).mir_const_qualif(def_id).0;
|
let mir = self.tcx.at(item.span).mir_const_qualif(def_id);
|
||||||
EntryKind::Const(
|
EntryKind::Const(
|
||||||
ConstQualif { mir },
|
ConstQualif { mir },
|
||||||
self.encode_rendered_const_for_body(body_id)
|
self.encode_rendered_const_for_body(body_id)
|
||||||
|
@ -1368,7 +1368,7 @@ impl EncodeContext<'tcx> {
|
||||||
let id = self.tcx.hir().as_local_hir_id(def_id).unwrap();
|
let id = self.tcx.hir().as_local_hir_id(def_id).unwrap();
|
||||||
let body_id = self.tcx.hir().body_owned_by(id);
|
let body_id = self.tcx.hir().body_owned_by(id);
|
||||||
let const_data = self.encode_rendered_const_for_body(body_id);
|
let const_data = self.encode_rendered_const_for_body(body_id);
|
||||||
let mir = self.tcx.mir_const_qualif(def_id).0;
|
let mir = self.tcx.mir_const_qualif(def_id);
|
||||||
|
|
||||||
record!(self.per_def.kind[def_id] <- EntryKind::Const(ConstQualif { mir }, const_data));
|
record!(self.per_def.kind[def_id] <- EntryKind::Const(ConstQualif { mir }, const_data));
|
||||||
record!(self.per_def.visibility[def_id] <- ty::Visibility::Public);
|
record!(self.per_def.visibility[def_id] <- ty::Visibility::Public);
|
||||||
|
|
|
@ -123,7 +123,7 @@ pub trait Qualif {
|
||||||
if cx.tcx.trait_of_item(def_id).is_some() {
|
if cx.tcx.trait_of_item(def_id).is_some() {
|
||||||
Self::in_any_value_of_ty(cx, constant.literal.ty)
|
Self::in_any_value_of_ty(cx, constant.literal.ty)
|
||||||
} else {
|
} else {
|
||||||
let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id);
|
let bits = cx.tcx.at(constant.span).mir_const_qualif(def_id);
|
||||||
|
|
||||||
let qualif = QualifSet(bits).contains::<Self>();
|
let qualif = QualifSet(bits).contains::<Self>();
|
||||||
|
|
||||||
|
|
|
@ -210,13 +210,14 @@ fn mir_validated(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut body = tcx.mir_const(def_id).steal();
|
let mut body = tcx.mir_const(def_id).steal();
|
||||||
let qualify_and_promote_pass = qualify_consts::QualifyAndPromoteConstants::default();
|
let promote_pass = promote_consts::PromoteTemps::default();
|
||||||
run_passes(tcx, &mut body, InstanceDef::Item(def_id), None, MirPhase::Validated, &[
|
run_passes(tcx, &mut body, InstanceDef::Item(def_id), None, MirPhase::Validated, &[
|
||||||
// What we need to run borrowck etc.
|
// What we need to run borrowck etc.
|
||||||
&qualify_and_promote_pass,
|
&qualify_consts::QualifyAndPromoteConstants::default(),
|
||||||
|
&promote_pass,
|
||||||
&simplify::SimplifyCfg::new("qualify-consts"),
|
&simplify::SimplifyCfg::new("qualify-consts"),
|
||||||
]);
|
]);
|
||||||
let promoted = qualify_and_promote_pass.promoted.into_inner();
|
let promoted = promote_pass.promoted_fragments.into_inner();
|
||||||
(tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
|
(tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ use rustc::mir::*;
|
||||||
use rustc::mir::interpret::ConstValue;
|
use rustc::mir::interpret::ConstValue;
|
||||||
use rustc::mir::visit::{PlaceContext, MutatingUseContext, MutVisitor, Visitor};
|
use rustc::mir::visit::{PlaceContext, MutatingUseContext, MutVisitor, Visitor};
|
||||||
use rustc::mir::traversal::ReversePostorder;
|
use rustc::mir::traversal::ReversePostorder;
|
||||||
use rustc::ty::{self, List, TyCtxt};
|
use rustc::ty::{self, List, TyCtxt, TypeFoldable};
|
||||||
use rustc::ty::subst::InternalSubsts;
|
use rustc::ty::subst::InternalSubsts;
|
||||||
use rustc::ty::cast::CastTy;
|
use rustc::ty::cast::CastTy;
|
||||||
use syntax::ast::LitKind;
|
use syntax::ast::LitKind;
|
||||||
|
@ -25,12 +25,68 @@ use syntax::symbol::sym;
|
||||||
use syntax_pos::{Span, DUMMY_SP};
|
use syntax_pos::{Span, DUMMY_SP};
|
||||||
|
|
||||||
use rustc_index::vec::{IndexVec, Idx};
|
use rustc_index::vec::{IndexVec, Idx};
|
||||||
|
use rustc_index::bit_set::HybridBitSet;
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
use std::{iter, mem, usize};
|
use std::{iter, mem, usize};
|
||||||
|
|
||||||
|
use crate::transform::{MirPass, MirSource};
|
||||||
use crate::transform::check_consts::{qualifs, Item, ConstKind, is_lang_panic_fn};
|
use crate::transform::check_consts::{qualifs, Item, ConstKind, is_lang_panic_fn};
|
||||||
|
|
||||||
|
/// A `MirPass` for promotion.
|
||||||
|
///
|
||||||
|
/// In this case, "promotion" entails the following:
|
||||||
|
/// - Extract promotable temps in `fn` and `const fn` into their own MIR bodies.
|
||||||
|
/// - Extend lifetimes in `const` and `static` by removing `Drop` and `StorageDead`.
|
||||||
|
/// - Emit errors if the requirements of `#[rustc_args_required_const]` are not met.
|
||||||
|
///
|
||||||
|
/// After this pass is run, `promoted_fragments` will hold the MIR body corresponding to each
|
||||||
|
/// newly created `StaticKind::Promoted`.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct PromoteTemps<'tcx> {
|
||||||
|
pub promoted_fragments: Cell<IndexVec<Promoted, Body<'tcx>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
|
||||||
|
fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
|
||||||
|
// There's not really any point in promoting errorful MIR.
|
||||||
|
//
|
||||||
|
// This does not include MIR that failed const-checking, which we still try to promote.
|
||||||
|
if body.return_ty().references_error() {
|
||||||
|
tcx.sess.delay_span_bug(body.span, "PromoteTemps: MIR had errors");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if src.promoted.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let def_id = src.def_id();
|
||||||
|
|
||||||
|
let item = Item::new(tcx, def_id, body);
|
||||||
|
let mut rpo = traversal::reverse_postorder(body);
|
||||||
|
let (temps, all_candidates) = collect_temps_and_candidates(tcx, body, &mut rpo);
|
||||||
|
|
||||||
|
let promotable_candidates = validate_candidates(tcx, body, def_id, &temps, &all_candidates);
|
||||||
|
|
||||||
|
// For now, lifetime extension is done in `const` and `static`s without creating promoted
|
||||||
|
// MIR fragments by removing `Drop` and `StorageDead` for each referent. However, this will
|
||||||
|
// not work inside loops when they are allowed in `const`s.
|
||||||
|
//
|
||||||
|
// FIXME: use promoted MIR fragments everywhere?
|
||||||
|
let promoted_fragments = if should_create_promoted_mir_fragments(item.const_kind) {
|
||||||
|
promote_candidates(def_id, body, tcx, temps, promotable_candidates)
|
||||||
|
} else {
|
||||||
|
// FIXME: promote const array initializers in consts.
|
||||||
|
remove_drop_and_storage_dead_on_promoted_locals(tcx, body, &promotable_candidates);
|
||||||
|
IndexVec::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
self.promoted_fragments.set(promoted_fragments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// State of a temporary during collection and promotion.
|
/// State of a temporary during collection and promotion.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum TempState {
|
pub enum TempState {
|
||||||
|
@ -538,7 +594,7 @@ impl<'tcx> Validator<'_, 'tcx> {
|
||||||
// is gone - we can always promote constants even if they
|
// is gone - we can always promote constants even if they
|
||||||
// fail to pass const-checking, as compilation would've
|
// fail to pass const-checking, as compilation would've
|
||||||
// errored independently and promotion can't change that.
|
// errored independently and promotion can't change that.
|
||||||
let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id);
|
let bits = self.tcx.at(constant.span).mir_const_qualif(def_id);
|
||||||
if bits == super::qualify_consts::QUALIF_ERROR_BIT {
|
if bits == super::qualify_consts::QUALIF_ERROR_BIT {
|
||||||
self.tcx.sess.delay_span_bug(
|
self.tcx.sess.delay_span_bug(
|
||||||
constant.span,
|
constant.span,
|
||||||
|
@ -1154,3 +1210,83 @@ crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>(
|
||||||
should_promote={:?} feature_flag={:?}", mir_def_id, should_promote, feature_flag);
|
should_promote={:?} feature_flag={:?}", mir_def_id, should_promote, feature_flag);
|
||||||
should_promote && !feature_flag
|
should_promote && !feature_flag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn should_create_promoted_mir_fragments(const_kind: Option<ConstKind>) -> bool {
|
||||||
|
match const_kind {
|
||||||
|
Some(ConstKind::ConstFn) | None => true,
|
||||||
|
Some(ConstKind::Const) | Some(ConstKind::Static) | Some(ConstKind::StaticMut) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// In `const` and `static` everything without `StorageDead`
|
||||||
|
/// is `'static`, we don't have to create promoted MIR fragments,
|
||||||
|
/// just remove `Drop` and `StorageDead` on "promoted" locals.
|
||||||
|
fn remove_drop_and_storage_dead_on_promoted_locals(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
body: &mut Body<'tcx>,
|
||||||
|
promotable_candidates: &[Candidate],
|
||||||
|
) {
|
||||||
|
debug!("run_pass: promotable_candidates={:?}", promotable_candidates);
|
||||||
|
|
||||||
|
// Removing `StorageDead` will cause errors for temps declared inside a loop body. For now we
|
||||||
|
// simply skip promotion if a loop exists, since loops are not yet allowed in a `const`.
|
||||||
|
//
|
||||||
|
// FIXME: Just create MIR fragments for `const`s instead of using this hackish approach?
|
||||||
|
if body.is_cfg_cyclic() {
|
||||||
|
tcx.sess.delay_span_bug(body.span, "Control-flow cycle detected in `const`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The underlying local for promotion contexts like `&temp` and `&(temp.proj)`.
|
||||||
|
let mut requires_lifetime_extension = HybridBitSet::new_empty(body.local_decls.len());
|
||||||
|
|
||||||
|
promotable_candidates
|
||||||
|
.iter()
|
||||||
|
.filter_map(|c| {
|
||||||
|
match c {
|
||||||
|
Candidate::Ref(loc) => Some(loc),
|
||||||
|
Candidate::Repeat(_) | Candidate::Argument { .. } => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|&Location { block, statement_index }| {
|
||||||
|
// FIXME: store the `Local` for each `Candidate` when it is created.
|
||||||
|
let place = match &body[block].statements[statement_index].kind {
|
||||||
|
StatementKind::Assign(box ( _, Rvalue::Ref(_, _, place))) => place,
|
||||||
|
_ => bug!("`Candidate::Ref` without corresponding assignment"),
|
||||||
|
};
|
||||||
|
|
||||||
|
match place.base {
|
||||||
|
PlaceBase::Local(local) => local,
|
||||||
|
PlaceBase::Static(_) => bug!("`Candidate::Ref` for a non-local"),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.for_each(|local| {
|
||||||
|
requires_lifetime_extension.insert(local);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove `Drop` terminators and `StorageDead` statements for all promotable temps that require
|
||||||
|
// lifetime extension.
|
||||||
|
for block in body.basic_blocks_mut() {
|
||||||
|
block.statements.retain(|statement| {
|
||||||
|
match statement.kind {
|
||||||
|
StatementKind::StorageDead(index) => !requires_lifetime_extension.contains(index),
|
||||||
|
_ => true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let terminator = block.terminator_mut();
|
||||||
|
match &terminator.kind {
|
||||||
|
TerminatorKind::Drop {
|
||||||
|
location,
|
||||||
|
target,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if let Some(index) = location.as_local() {
|
||||||
|
if requires_lifetime_extension.contains(index) {
|
||||||
|
terminator.kind = TerminatorKind::Goto { target: *target };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ use rustc::ty::cast::CastTy;
|
||||||
use rustc::ty::query::Providers;
|
use rustc::ty::query::Providers;
|
||||||
use rustc::mir::*;
|
use rustc::mir::*;
|
||||||
use rustc::mir::interpret::ConstValue;
|
use rustc::mir::interpret::ConstValue;
|
||||||
use rustc::mir::traversal::ReversePostorder;
|
|
||||||
use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
|
use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
|
||||||
use rustc::middle::lang_items;
|
use rustc::middle::lang_items;
|
||||||
use rustc::session::config::nightly_options;
|
use rustc::session::config::nightly_options;
|
||||||
|
@ -31,7 +30,6 @@ use std::usize;
|
||||||
|
|
||||||
use rustc::hir::HirId;
|
use rustc::hir::HirId;
|
||||||
use crate::transform::{MirPass, MirSource};
|
use crate::transform::{MirPass, MirSource};
|
||||||
use super::promote_consts::{self, Candidate, TempState};
|
|
||||||
use crate::transform::check_consts::ops::{self, NonConstOp};
|
use crate::transform::check_consts::ops::{self, NonConstOp};
|
||||||
|
|
||||||
/// What kind of item we are in.
|
/// What kind of item we are in.
|
||||||
|
@ -258,7 +256,7 @@ trait Qualif {
|
||||||
if cx.tcx.trait_of_item(def_id).is_some() {
|
if cx.tcx.trait_of_item(def_id).is_some() {
|
||||||
Self::in_any_value_of_ty(cx, constant.literal.ty).unwrap_or(false)
|
Self::in_any_value_of_ty(cx, constant.literal.ty).unwrap_or(false)
|
||||||
} else {
|
} else {
|
||||||
let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id);
|
let bits = cx.tcx.at(constant.span).mir_const_qualif(def_id);
|
||||||
|
|
||||||
let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX];
|
let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX];
|
||||||
|
|
||||||
|
@ -477,10 +475,6 @@ struct Checker<'a, 'tcx> {
|
||||||
|
|
||||||
span: Span,
|
span: Span,
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
rpo: ReversePostorder<'a, 'tcx>,
|
|
||||||
|
|
||||||
temp_promotion_state: IndexVec<Local, TempState>,
|
|
||||||
unchecked_promotion_candidates: Vec<Candidate>,
|
|
||||||
|
|
||||||
/// If `true`, do not emit errors to the user, merely collect them in `errors`.
|
/// If `true`, do not emit errors to the user, merely collect them in `errors`.
|
||||||
suppress_errors: bool,
|
suppress_errors: bool,
|
||||||
|
@ -509,10 +503,6 @@ impl Deref for Checker<'a, 'tcx> {
|
||||||
impl<'a, 'tcx> Checker<'a, 'tcx> {
|
impl<'a, 'tcx> Checker<'a, 'tcx> {
|
||||||
fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>, mode: Mode) -> Self {
|
fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>, mode: Mode) -> Self {
|
||||||
assert!(def_id.is_local());
|
assert!(def_id.is_local());
|
||||||
let mut rpo = traversal::reverse_postorder(body);
|
|
||||||
let (temps, unchecked_promotion_candidates) =
|
|
||||||
promote_consts::collect_temps_and_candidates(tcx, body, &mut rpo);
|
|
||||||
rpo.reset();
|
|
||||||
|
|
||||||
let param_env = tcx.param_env(def_id);
|
let param_env = tcx.param_env(def_id);
|
||||||
|
|
||||||
|
@ -539,9 +529,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
|
||||||
cx,
|
cx,
|
||||||
span: body.span,
|
span: body.span,
|
||||||
def_id,
|
def_id,
|
||||||
rpo,
|
|
||||||
temp_promotion_state: temps,
|
|
||||||
unchecked_promotion_candidates,
|
|
||||||
errors: vec![],
|
errors: vec![],
|
||||||
suppress_errors: false,
|
suppress_errors: false,
|
||||||
}
|
}
|
||||||
|
@ -662,14 +649,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
|
||||||
let kind = self.body.local_kind(index);
|
let kind = self.body.local_kind(index);
|
||||||
debug!("store to {:?} {:?}", kind, index);
|
debug!("store to {:?} {:?}", kind, index);
|
||||||
|
|
||||||
// Only handle promotable temps in non-const functions.
|
|
||||||
if self.mode == Mode::NonConstFn {
|
|
||||||
if kind != LocalKind::Temp ||
|
|
||||||
!self.temp_promotion_state[index].is_promotable() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is overly restrictive, because even full assignments do not clear the qualif
|
// this is overly restrictive, because even full assignments do not clear the qualif
|
||||||
// While we could special case full assignments, this would be inconsistent with
|
// While we could special case full assignments, this would be inconsistent with
|
||||||
// aggregates where we overwrite all fields via assignments, which would not get
|
// aggregates where we overwrite all fields via assignments, which would not get
|
||||||
|
@ -682,7 +661,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check a whole const, static initializer or const fn.
|
/// Check a whole const, static initializer or const fn.
|
||||||
fn check_const(&mut self) -> (u8, &'tcx BitSet<Local>) {
|
fn check_const(&mut self) -> u8 {
|
||||||
use crate::transform::check_consts as new_checker;
|
use crate::transform::check_consts as new_checker;
|
||||||
|
|
||||||
debug!("const-checking {} {:?}", self.mode, self.def_id);
|
debug!("const-checking {} {:?}", self.mode, self.def_id);
|
||||||
|
@ -704,7 +683,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
|
||||||
|
|
||||||
let mut seen_blocks = BitSet::new_empty(body.basic_blocks().len());
|
let mut seen_blocks = BitSet::new_empty(body.basic_blocks().len());
|
||||||
let mut bb = START_BLOCK;
|
let mut bb = START_BLOCK;
|
||||||
let mut has_controlflow_error = false;
|
|
||||||
loop {
|
loop {
|
||||||
seen_blocks.insert(bb.index());
|
seen_blocks.insert(bb.index());
|
||||||
|
|
||||||
|
@ -745,7 +723,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
|
||||||
bb = target;
|
bb = target;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
has_controlflow_error = true;
|
|
||||||
self.not_const(ops::Loop);
|
self.not_const(ops::Loop);
|
||||||
validator.check_op(ops::Loop);
|
validator.check_op(ops::Loop);
|
||||||
break;
|
break;
|
||||||
|
@ -772,51 +749,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect all the temps we need to promote.
|
self.qualifs_in_local(RETURN_PLACE).encode_to_bits()
|
||||||
let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
|
|
||||||
|
|
||||||
// HACK: if parts of the control-flow graph were skipped due to an error, don't try to
|
|
||||||
// promote anything, since that can cause errors in a `const` if e.g. rvalue static
|
|
||||||
// promotion is attempted within a loop body.
|
|
||||||
let unleash_miri = self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
|
|
||||||
let promotion_candidates = if has_controlflow_error && !unleash_miri {
|
|
||||||
self.tcx.sess.delay_span_bug(
|
|
||||||
body.span,
|
|
||||||
"check_const: expected control-flow error(s)",
|
|
||||||
);
|
|
||||||
|
|
||||||
vec![]
|
|
||||||
} else {
|
|
||||||
promote_consts::validate_candidates(
|
|
||||||
self.tcx,
|
|
||||||
self.body,
|
|
||||||
self.def_id,
|
|
||||||
&self.temp_promotion_state,
|
|
||||||
&self.unchecked_promotion_candidates,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!("qualify_const: promotion_candidates={:?}", promotion_candidates);
|
|
||||||
for candidate in promotion_candidates {
|
|
||||||
match candidate {
|
|
||||||
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
|
|
||||||
if let StatementKind::Assign(box( _, Rvalue::Ref(_, _, place)))
|
|
||||||
= &self.body[bb].statements[stmt_idx].kind
|
|
||||||
{
|
|
||||||
if let PlaceBase::Local(local) = place.base {
|
|
||||||
promoted_temps.insert(local);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only rvalue-static promotion requires extending the lifetime of the promoted
|
|
||||||
// local.
|
|
||||||
Candidate::Argument { .. } | Candidate::Repeat(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let qualifs = self.qualifs_in_local(RETURN_PLACE);
|
|
||||||
(qualifs.encode_to_bits(), self.tcx.arena.alloc(promoted_temps))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1346,7 +1279,7 @@ pub fn provide(providers: &mut Providers<'_>) {
|
||||||
// in `promote_consts`, see the comment in `validate_operand`.
|
// in `promote_consts`, see the comment in `validate_operand`.
|
||||||
pub(super) const QUALIF_ERROR_BIT: u8 = 1 << 2;
|
pub(super) const QUALIF_ERROR_BIT: u8 = 1 << 2;
|
||||||
|
|
||||||
fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> (u8, &BitSet<Local>) {
|
fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> u8 {
|
||||||
// N.B., this `borrow()` is guaranteed to be valid (i.e., the value
|
// N.B., this `borrow()` is guaranteed to be valid (i.e., the value
|
||||||
// cannot yet be stolen), because `mir_validated()`, which steals
|
// cannot yet be stolen), because `mir_validated()`, which steals
|
||||||
// from `mir_const(), forces this query to execute before
|
// from `mir_const(), forces this query to execute before
|
||||||
|
@ -1355,7 +1288,7 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> (u8, &BitSet<Local>) {
|
||||||
|
|
||||||
if body.return_ty().references_error() {
|
if body.return_ty().references_error() {
|
||||||
tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
|
tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
|
||||||
return (QUALIF_ERROR_BIT, tcx.arena.alloc(BitSet::new_empty(0)));
|
return QUALIF_ERROR_BIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
Checker::new(tcx, def_id, body, Mode::Const).check_const()
|
Checker::new(tcx, def_id, body, Mode::Const).check_const()
|
||||||
|
@ -1391,56 +1324,34 @@ impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants<'tcx> {
|
||||||
let mode = determine_mode(tcx, hir_id, def_id);
|
let mode = determine_mode(tcx, hir_id, def_id);
|
||||||
|
|
||||||
debug!("run_pass: mode={:?}", mode);
|
debug!("run_pass: mode={:?}", mode);
|
||||||
if let Mode::NonConstFn | Mode::ConstFn = mode {
|
if let Mode::NonConstFn = mode {
|
||||||
|
// No need to const-check a non-const `fn` now that we don't do promotion here.
|
||||||
|
return;
|
||||||
|
} else if let Mode::ConstFn = mode {
|
||||||
let mut checker = Checker::new(tcx, def_id, body, mode);
|
let mut checker = Checker::new(tcx, def_id, body, mode);
|
||||||
if let Mode::ConstFn = mode {
|
let use_min_const_fn_checks =
|
||||||
let use_min_const_fn_checks =
|
!tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you &&
|
||||||
!tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you &&
|
tcx.is_min_const_fn(def_id);
|
||||||
tcx.is_min_const_fn(def_id);
|
if use_min_const_fn_checks {
|
||||||
if use_min_const_fn_checks {
|
// Enforce `min_const_fn` for stable `const fn`s.
|
||||||
// Enforce `min_const_fn` for stable `const fn`s.
|
use super::qualify_min_const_fn::is_min_const_fn;
|
||||||
use super::qualify_min_const_fn::is_min_const_fn;
|
if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
|
||||||
if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
|
error_min_const_fn_violation(tcx, span, err);
|
||||||
error_min_const_fn_violation(tcx, span, err);
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// `check_const` should not produce any errors, but better safe than sorry
|
|
||||||
// FIXME(#53819)
|
|
||||||
// NOTE(eddyb) `check_const` is actually needed for promotion inside
|
|
||||||
// `min_const_fn` functions.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enforce a constant-like CFG for `const fn`.
|
|
||||||
checker.check_const();
|
|
||||||
} else {
|
|
||||||
while let Some((bb, data)) = checker.rpo.next() {
|
|
||||||
checker.visit_basic_block_data(bb, data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Promote only the promotable candidates.
|
// `check_const` should not produce any errors, but better safe than sorry
|
||||||
let temps = checker.temp_promotion_state;
|
// FIXME(#53819)
|
||||||
let candidates = promote_consts::validate_candidates(
|
// Enforce a constant-like CFG for `const fn`.
|
||||||
tcx,
|
checker.check_const();
|
||||||
body,
|
|
||||||
def_id,
|
|
||||||
&temps,
|
|
||||||
&checker.unchecked_promotion_candidates,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Do the actual promotion, now that we know what's viable.
|
|
||||||
self.promoted.set(
|
|
||||||
promote_consts::promote_candidates(def_id, body, tcx, temps, candidates)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
check_short_circuiting_in_const_local(tcx, body, mode);
|
check_short_circuiting_in_const_local(tcx, body, mode);
|
||||||
|
|
||||||
let promoted_temps = match mode {
|
match mode {
|
||||||
Mode::Const => tcx.mir_const_qualif(def_id).1,
|
Mode::Const => tcx.mir_const_qualif(def_id),
|
||||||
_ => Checker::new(tcx, def_id, body, mode).check_const().1,
|
_ => Checker::new(tcx, def_id, body, mode).check_const(),
|
||||||
};
|
};
|
||||||
remove_drop_and_storage_dead_on_promoted_locals(body, promoted_temps);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if mode == Mode::Static && !tcx.has_attr(def_id, sym::thread_local) {
|
if mode == Mode::Static && !tcx.has_attr(def_id, sym::thread_local) {
|
||||||
|
@ -1501,40 +1412,6 @@ fn check_short_circuiting_in_const_local(tcx: TyCtxt<'_>, body: &mut Body<'tcx>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// In `const` and `static` everything without `StorageDead`
|
|
||||||
/// is `'static`, we don't have to create promoted MIR fragments,
|
|
||||||
/// just remove `Drop` and `StorageDead` on "promoted" locals.
|
|
||||||
fn remove_drop_and_storage_dead_on_promoted_locals(
|
|
||||||
body: &mut Body<'tcx>,
|
|
||||||
promoted_temps: &BitSet<Local>,
|
|
||||||
) {
|
|
||||||
debug!("run_pass: promoted_temps={:?}", promoted_temps);
|
|
||||||
|
|
||||||
for block in body.basic_blocks_mut() {
|
|
||||||
block.statements.retain(|statement| {
|
|
||||||
match statement.kind {
|
|
||||||
StatementKind::StorageDead(index) => !promoted_temps.contains(index),
|
|
||||||
_ => true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let terminator = block.terminator_mut();
|
|
||||||
match &terminator.kind {
|
|
||||||
TerminatorKind::Drop {
|
|
||||||
location,
|
|
||||||
target,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
if let Some(index) = location.as_local() {
|
|
||||||
if promoted_temps.contains(index) {
|
|
||||||
terminator.kind = TerminatorKind::Goto { target: *target };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_static_is_sync(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, hir_id: HirId) {
|
fn check_static_is_sync(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, hir_id: HirId) {
|
||||||
let ty = body.return_ty();
|
let ty = body.return_ty();
|
||||||
tcx.infer_ctxt().enter(|infcx| {
|
tcx.infer_ctxt().enter(|infcx| {
|
||||||
|
|
|
@ -554,7 +554,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
|
||||||
"arm" => arm::compute_abi_info(cx, self),
|
"arm" => arm::compute_abi_info(cx, self),
|
||||||
"mips" => mips::compute_abi_info(cx, self),
|
"mips" => mips::compute_abi_info(cx, self),
|
||||||
"mips64" => mips64::compute_abi_info(cx, self),
|
"mips64" => mips64::compute_abi_info(cx, self),
|
||||||
"powerpc" => powerpc::compute_abi_info(cx, self),
|
"powerpc" => powerpc::compute_abi_info(self),
|
||||||
"powerpc64" => powerpc64::compute_abi_info(cx, self),
|
"powerpc64" => powerpc64::compute_abi_info(cx, self),
|
||||||
"s390x" => s390x::compute_abi_info(cx, self),
|
"s390x" => s390x::compute_abi_info(cx, self),
|
||||||
"msp430" => msp430::compute_abi_info(self),
|
"msp430" => msp430::compute_abi_info(self),
|
||||||
|
|
|
@ -1,49 +1,28 @@
|
||||||
use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform};
|
use crate::abi::call::{ArgAbi, FnAbi};
|
||||||
use crate::abi::{HasDataLayout, LayoutOf, Size, TyLayoutMethods};
|
|
||||||
|
|
||||||
fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size)
|
fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
|
||||||
where Ty: TyLayoutMethods<'a, C>, C: LayoutOf<Ty = Ty> + HasDataLayout
|
if ret.layout.is_aggregate() {
|
||||||
{
|
|
||||||
if !ret.layout.is_aggregate() {
|
|
||||||
ret.extend_integer_width_to(32);
|
|
||||||
} else {
|
|
||||||
ret.make_indirect();
|
ret.make_indirect();
|
||||||
*offset += cx.data_layout().pointer_size;
|
} else {
|
||||||
|
ret.extend_integer_width_to(32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'_, Ty>, offset: &mut Size)
|
fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
|
||||||
where Ty: TyLayoutMethods<'a, C>, C: LayoutOf<Ty = Ty> + HasDataLayout
|
|
||||||
{
|
|
||||||
let dl = cx.data_layout();
|
|
||||||
let size = arg.layout.size;
|
|
||||||
let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align).abi;
|
|
||||||
|
|
||||||
if arg.layout.is_aggregate() {
|
if arg.layout.is_aggregate() {
|
||||||
arg.cast_to(Uniform {
|
arg.make_indirect();
|
||||||
unit: Reg::i32(),
|
|
||||||
total: size
|
|
||||||
});
|
|
||||||
if !offset.is_aligned(align) {
|
|
||||||
arg.pad_with(Reg::i32());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
arg.extend_integer_width_to(32);
|
arg.extend_integer_width_to(32);
|
||||||
}
|
}
|
||||||
|
|
||||||
*offset = offset.align_to(align) + size.align_to(align);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'_, Ty>)
|
pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
|
||||||
where Ty: TyLayoutMethods<'a, C>, C: LayoutOf<Ty = Ty> + HasDataLayout
|
|
||||||
{
|
|
||||||
let mut offset = Size::ZERO;
|
|
||||||
if !fn_abi.ret.is_ignore() {
|
if !fn_abi.ret.is_ignore() {
|
||||||
classify_ret(cx, &mut fn_abi.ret, &mut offset);
|
classify_ret(&mut fn_abi.ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
for arg in &mut fn_abi.args {
|
for arg in &mut fn_abi.args {
|
||||||
if arg.is_ignore() { continue; }
|
if arg.is_ignore() { continue; }
|
||||||
classify_arg(cx, arg, &mut offset);
|
classify_arg(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum ArgumentType {
|
enum ArgumentType {
|
||||||
Placeholder(String),
|
Placeholder(&'static str),
|
||||||
Count,
|
Count,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +244,57 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||||
parse::ArgumentNamed(s) => Named(s),
|
parse::ArgumentNamed(s) => Named(s),
|
||||||
};
|
};
|
||||||
|
|
||||||
let ty = Placeholder(arg.format.ty.to_string());
|
let ty = Placeholder(match &arg.format.ty[..] {
|
||||||
|
"" => "Display",
|
||||||
|
"?" => "Debug",
|
||||||
|
"e" => "LowerExp",
|
||||||
|
"E" => "UpperExp",
|
||||||
|
"o" => "Octal",
|
||||||
|
"p" => "Pointer",
|
||||||
|
"b" => "Binary",
|
||||||
|
"x" => "LowerHex",
|
||||||
|
"X" => "UpperHex",
|
||||||
|
_ => {
|
||||||
|
let fmtsp = self.fmtsp;
|
||||||
|
let sp = arg.format.ty_span.map(|sp| fmtsp.from_inner(sp));
|
||||||
|
let mut err = self.ecx.struct_span_err(
|
||||||
|
sp.unwrap_or(fmtsp),
|
||||||
|
&format!("unknown format trait `{}`", arg.format.ty),
|
||||||
|
);
|
||||||
|
err.note("the only appropriate formatting traits are:\n\
|
||||||
|
- ``, which uses the `Display` trait\n\
|
||||||
|
- `?`, which uses the `Debug` trait\n\
|
||||||
|
- `e`, which uses the `LowerExp` trait\n\
|
||||||
|
- `E`, which uses the `UpperExp` trait\n\
|
||||||
|
- `o`, which uses the `Octal` trait\n\
|
||||||
|
- `p`, which uses the `Pointer` trait\n\
|
||||||
|
- `b`, which uses the `Binary` trait\n\
|
||||||
|
- `x`, which uses the `LowerHex` trait\n\
|
||||||
|
- `X`, which uses the `UpperHex` trait");
|
||||||
|
if let Some(sp) = sp {
|
||||||
|
for (fmt, name) in &[
|
||||||
|
("", "Display"),
|
||||||
|
("?", "Debug"),
|
||||||
|
("e", "LowerExp"),
|
||||||
|
("E", "UpperExp"),
|
||||||
|
("o", "Octal"),
|
||||||
|
("p", "Pointer"),
|
||||||
|
("b", "Binary"),
|
||||||
|
("x", "LowerHex"),
|
||||||
|
("X", "UpperHex"),
|
||||||
|
] {
|
||||||
|
err.tool_only_span_suggestion(
|
||||||
|
sp,
|
||||||
|
&format!("use the `{}` trait", name),
|
||||||
|
fmt.to_string(),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err.emit();
|
||||||
|
"<invalid>"
|
||||||
|
}
|
||||||
|
});
|
||||||
self.verify_arg_type(pos, ty);
|
self.verify_arg_type(pos, ty);
|
||||||
self.curpiece += 1;
|
self.curpiece += 1;
|
||||||
}
|
}
|
||||||
|
@ -590,6 +640,7 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||||
width: parse::CountImplied,
|
width: parse::CountImplied,
|
||||||
width_span: None,
|
width_span: None,
|
||||||
ty: arg.format.ty,
|
ty: arg.format.ty,
|
||||||
|
ty_span: arg.format.ty_span,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -761,37 +812,8 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||||
sp = ecx.with_def_site_ctxt(sp);
|
sp = ecx.with_def_site_ctxt(sp);
|
||||||
let arg = ecx.expr_ident(sp, arg);
|
let arg = ecx.expr_ident(sp, arg);
|
||||||
let trait_ = match *ty {
|
let trait_ = match *ty {
|
||||||
Placeholder(ref tyname) => {
|
Placeholder(trait_) if trait_ == "<invalid>" => return DummyResult::raw_expr(sp, true),
|
||||||
match &tyname[..] {
|
Placeholder(trait_) => trait_,
|
||||||
"" => "Display",
|
|
||||||
"?" => "Debug",
|
|
||||||
"e" => "LowerExp",
|
|
||||||
"E" => "UpperExp",
|
|
||||||
"o" => "Octal",
|
|
||||||
"p" => "Pointer",
|
|
||||||
"b" => "Binary",
|
|
||||||
"x" => "LowerHex",
|
|
||||||
"X" => "UpperHex",
|
|
||||||
_ => {
|
|
||||||
let mut err = ecx.struct_span_err(
|
|
||||||
sp,
|
|
||||||
&format!("unknown format trait `{}`", *tyname),
|
|
||||||
);
|
|
||||||
err.note("the only appropriate formatting traits are:\n\
|
|
||||||
- ``, which uses the `Display` trait\n\
|
|
||||||
- `?`, which uses the `Debug` trait\n\
|
|
||||||
- `e`, which uses the `LowerExp` trait\n\
|
|
||||||
- `E`, which uses the `UpperExp` trait\n\
|
|
||||||
- `o`, which uses the `Octal` trait\n\
|
|
||||||
- `p`, which uses the `Pointer` trait\n\
|
|
||||||
- `b`, which uses the `Binary` trait\n\
|
|
||||||
- `x`, which uses the `LowerHex` trait\n\
|
|
||||||
- `X`, which uses the `UpperHex` trait");
|
|
||||||
err.emit();
|
|
||||||
return DummyResult::raw_expr(sp, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Count => {
|
Count => {
|
||||||
let path = ecx.std_path(&[sym::fmt, sym::ArgumentV1, sym::from_usize]);
|
let path = ecx.std_path(&[sym::fmt, sym::ArgumentV1, sym::from_usize]);
|
||||||
return ecx.expr_call_global(macsp, path, vec![arg]);
|
return ecx.expr_call_global(macsp, path, vec![arg]);
|
||||||
|
|
|
@ -39,7 +39,7 @@ fn main() {
|
||||||
|
|
||||||
// END RUST SOURCE
|
// END RUST SOURCE
|
||||||
//
|
//
|
||||||
// START rustc.full_tested_match.QualifyAndPromoteConstants.after.mir
|
// START rustc.full_tested_match.PromoteTemps.after.mir
|
||||||
// bb0: {
|
// bb0: {
|
||||||
// ...
|
// ...
|
||||||
// _2 = std::option::Option::<i32>::Some(const 42i32,);
|
// _2 = std::option::Option::<i32>::Some(const 42i32,);
|
||||||
|
@ -108,9 +108,9 @@ fn main() {
|
||||||
// _0 = ();
|
// _0 = ();
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
// END rustc.full_tested_match.QualifyAndPromoteConstants.after.mir
|
// END rustc.full_tested_match.PromoteTemps.after.mir
|
||||||
//
|
//
|
||||||
// START rustc.full_tested_match2.QualifyAndPromoteConstants.before.mir
|
// START rustc.full_tested_match2.PromoteTemps.before.mir
|
||||||
// bb0: {
|
// bb0: {
|
||||||
// ...
|
// ...
|
||||||
// _2 = std::option::Option::<i32>::Some(const 42i32,);
|
// _2 = std::option::Option::<i32>::Some(const 42i32,);
|
||||||
|
@ -179,9 +179,9 @@ fn main() {
|
||||||
// _0 = ();
|
// _0 = ();
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
// END rustc.full_tested_match2.QualifyAndPromoteConstants.before.mir
|
// END rustc.full_tested_match2.PromoteTemps.before.mir
|
||||||
//
|
//
|
||||||
// START rustc.main.QualifyAndPromoteConstants.before.mir
|
// START rustc.main.PromoteTemps.before.mir
|
||||||
// bb0: {
|
// bb0: {
|
||||||
// ...
|
// ...
|
||||||
// _2 = std::option::Option::<i32>::Some(const 1i32,);
|
// _2 = std::option::Option::<i32>::Some(const 1i32,);
|
||||||
|
@ -276,4 +276,4 @@ fn main() {
|
||||||
// _0 = ();
|
// _0 = ();
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
// END rustc.main.QualifyAndPromoteConstants.before.mir
|
// END rustc.main.PromoteTemps.before.mir
|
||||||
|
|
|
@ -8,4 +8,20 @@ impl Foo for () {
|
||||||
type Assoc = bool; //~ ERROR the trait bound `bool: Bar` is not satisfied
|
type Assoc = bool; //~ ERROR the trait bound `bool: Bar` is not satisfied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait Baz where Self::Assoc: Bar {
|
||||||
|
type Assoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Baz for () {
|
||||||
|
type Assoc = bool; //~ ERROR the trait bound `bool: Bar` is not satisfied
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Bat where <Self as Bat>::Assoc: Bar {
|
||||||
|
type Assoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bat for () {
|
||||||
|
type Assoc = bool; //~ ERROR the trait bound `bool: Bar` is not satisfied
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -9,6 +9,32 @@ LL | impl Foo for () {
|
||||||
LL | type Assoc = bool;
|
LL | type Assoc = bool;
|
||||||
| ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
|
| ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
|
||||||
|
|
||||||
error: aborting due to previous error
|
error[E0277]: the trait bound `bool: Bar` is not satisfied
|
||||||
|
--> $DIR/point-at-type-on-obligation-failure-2.rs:16:5
|
||||||
|
|
|
||||||
|
LL | trait Baz where Self::Assoc: Bar {
|
||||||
|
| ---------------- restricted in this bound
|
||||||
|
LL | type Assoc;
|
||||||
|
| ----- associated type defined here
|
||||||
|
...
|
||||||
|
LL | impl Baz for () {
|
||||||
|
| --------------- in this `impl` item
|
||||||
|
LL | type Assoc = bool;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `bool: Bar` is not satisfied
|
||||||
|
--> $DIR/point-at-type-on-obligation-failure-2.rs:24:5
|
||||||
|
|
|
||||||
|
LL | trait Bat where <Self as Bat>::Assoc: Bar {
|
||||||
|
| ------------------------- restricted in this bound
|
||||||
|
LL | type Assoc;
|
||||||
|
| ----- associated type defined here
|
||||||
|
...
|
||||||
|
LL | impl Bat for () {
|
||||||
|
| --------------- in this `impl` item
|
||||||
|
LL | type Assoc = bool;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0277`.
|
For more information about this error, try `rustc --explain E0277`.
|
||||||
|
|
|
@ -136,7 +136,6 @@ const fn no_rpit() -> impl std::fmt::Debug {} //~ ERROR `impl Trait` in const fn
|
||||||
const fn no_dyn_trait(_x: &dyn std::fmt::Debug) {} //~ ERROR trait bounds other than `Sized`
|
const fn no_dyn_trait(_x: &dyn std::fmt::Debug) {} //~ ERROR trait bounds other than `Sized`
|
||||||
const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
|
const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
|
||||||
//~^ ERROR trait bounds other than `Sized`
|
//~^ ERROR trait bounds other than `Sized`
|
||||||
//~| ERROR cannot return reference to temporary value
|
|
||||||
|
|
||||||
const fn no_unsafe() { unsafe {} }
|
const fn no_unsafe() { unsafe {} }
|
||||||
|
|
||||||
|
|
|
@ -286,17 +286,8 @@ LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
|
||||||
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
|
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
|
||||||
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
||||||
|
|
||||||
error[E0515]: cannot return reference to temporary value
|
|
||||||
--> $DIR/min_const_fn.rs:137:63
|
|
||||||
|
|
|
||||||
LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
|
|
||||||
| ^--
|
|
||||||
| ||
|
|
||||||
| |temporary value created here
|
|
||||||
| returns a reference to data owned by the current function
|
|
||||||
|
|
||||||
error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
|
error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
|
||||||
--> $DIR/min_const_fn.rs:143:41
|
--> $DIR/min_const_fn.rs:142:41
|
||||||
|
|
|
|
||||||
LL | const fn really_no_traits_i_mean_it() { (&() as &dyn std::fmt::Debug, ()).1 }
|
LL | const fn really_no_traits_i_mean_it() { (&() as &dyn std::fmt::Debug, ()).1 }
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -305,7 +296,7 @@ LL | const fn really_no_traits_i_mean_it() { (&() as &dyn std::fmt::Debug, ()).1
|
||||||
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
||||||
|
|
||||||
error[E0723]: function pointers in const fn are unstable
|
error[E0723]: function pointers in const fn are unstable
|
||||||
--> $DIR/min_const_fn.rs:146:21
|
--> $DIR/min_const_fn.rs:145:21
|
||||||
|
|
|
|
||||||
LL | const fn no_fn_ptrs(_x: fn()) {}
|
LL | const fn no_fn_ptrs(_x: fn()) {}
|
||||||
| ^^
|
| ^^
|
||||||
|
@ -314,7 +305,7 @@ LL | const fn no_fn_ptrs(_x: fn()) {}
|
||||||
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
||||||
|
|
||||||
error[E0723]: function pointers in const fn are unstable
|
error[E0723]: function pointers in const fn are unstable
|
||||||
--> $DIR/min_const_fn.rs:148:27
|
--> $DIR/min_const_fn.rs:147:27
|
||||||
|
|
|
|
||||||
LL | const fn no_fn_ptrs2() -> fn() { fn foo() {} foo }
|
LL | const fn no_fn_ptrs2() -> fn() { fn foo() {} foo }
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
@ -322,7 +313,7 @@ LL | const fn no_fn_ptrs2() -> fn() { fn foo() {} foo }
|
||||||
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
|
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
|
||||||
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
||||||
|
|
||||||
error: aborting due to 37 previous errors
|
error: aborting due to 36 previous errors
|
||||||
|
|
||||||
Some errors have detailed explanations: E0493, E0515, E0723.
|
Some errors have detailed explanations: E0493, E0723.
|
||||||
For more information about an error, try `rustc --explain E0493`.
|
For more information about an error, try `rustc --explain E0493`.
|
||||||
|
|
|
@ -11,6 +11,5 @@ const fn no_inner_dyn_trait2(x: Hide) {
|
||||||
}
|
}
|
||||||
const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
|
const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
|
||||||
//~^ ERROR trait bounds other than `Sized`
|
//~^ ERROR trait bounds other than `Sized`
|
||||||
//~| ERROR temporary value dropped while borrowed
|
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -16,16 +16,6 @@ LL | const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
|
||||||
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
|
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
|
||||||
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
||||||
|
|
||||||
error[E0716]: temporary value dropped while borrowed
|
error: aborting due to 2 previous errors
|
||||||
--> $DIR/min_const_fn_dyn.rs:12:67
|
|
||||||
|
|
|
||||||
LL | const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
|
|
||||||
| -^ - temporary value is freed at the end of this statement
|
|
||||||
| ||
|
|
||||||
| |creates a temporary which is freed while still in use
|
|
||||||
| cast requires that borrow lasts for `'static`
|
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
For more information about this error, try `rustc --explain E0723`.
|
||||||
|
|
||||||
Some errors have detailed explanations: E0716, E0723.
|
|
||||||
For more information about an error, try `rustc --explain E0716`.
|
|
||||||
|
|
|
@ -257,10 +257,10 @@ LL | println!("{} {:07$} {}", 1, 3.2, 4);
|
||||||
= note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
|
= note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
|
||||||
|
|
||||||
error: unknown format trait `foo`
|
error: unknown format trait `foo`
|
||||||
--> $DIR/ifmt-bad-arg.rs:86:24
|
--> $DIR/ifmt-bad-arg.rs:86:17
|
||||||
|
|
|
|
||||||
LL | println!("{:foo}", 1);
|
LL | println!("{:foo}", 1);
|
||||||
| ^
|
| ^^^
|
||||||
|
|
|
|
||||||
= note: the only appropriate formatting traits are:
|
= note: the only appropriate formatting traits are:
|
||||||
- ``, which uses the `Display` trait
|
- ``, which uses the `Display` trait
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
error: unknown format trait `notimplemented`
|
error: unknown format trait `notimplemented`
|
||||||
--> $DIR/ifmt-unknown-trait.rs:2:34
|
--> $DIR/ifmt-unknown-trait.rs:2:16
|
||||||
|
|
|
|
||||||
LL | format!("{:notimplemented}", "3");
|
LL | format!("{:notimplemented}", "3");
|
||||||
| ^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: the only appropriate formatting traits are:
|
= note: the only appropriate formatting traits are:
|
||||||
- ``, which uses the `Display` trait
|
- ``, which uses the `Display` trait
|
||||||
|
|
Loading…
Reference in New Issue