Merge from rustc
This commit is contained in:
commit
326d77516d
1459 changed files with 38634 additions and 44139 deletions
|
|
@ -4494,7 +4494,6 @@ dependencies = [
|
|||
"rustc_session",
|
||||
"rustc_span",
|
||||
"rustc_transmute",
|
||||
"rustc_type_ir",
|
||||
"smallvec",
|
||||
"thin-vec",
|
||||
"tracing",
|
||||
|
|
|
|||
|
|
@ -570,6 +570,12 @@
|
|||
# Defaults to rust.debug-assertions value
|
||||
#debug-assertions-std = rust.debug-assertions (boolean)
|
||||
|
||||
# Whether or not debug assertions are enabled for the tools built by bootstrap.
|
||||
# Overrides the `debug-assertions` option, if defined.
|
||||
#
|
||||
# Defaults to rust.debug-assertions value
|
||||
#debug-assertions-tools = rust.debug-assertions (boolean)
|
||||
|
||||
# Whether or not to leave debug! and trace! calls in the rust binary.
|
||||
#
|
||||
# Defaults to rust.debug-assertions value
|
||||
|
|
|
|||
|
|
@ -21,8 +21,10 @@
|
|||
#![feature(decl_macro)]
|
||||
#![feature(dropck_eyepatch)]
|
||||
#![feature(maybe_uninit_slice)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(unwrap_infallible)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use std::alloc::Layout;
|
||||
|
|
@ -200,6 +202,18 @@ impl<T> TypedArena<T> {
|
|||
/// storing the elements in the arena.
|
||||
#[inline]
|
||||
pub fn alloc_from_iter<I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
|
||||
self.try_alloc_from_iter(iter.into_iter().map(Ok::<T, !>)).into_ok()
|
||||
}
|
||||
|
||||
/// Allocates the elements of this iterator into a contiguous slice in the `TypedArena`.
|
||||
///
|
||||
/// Note: for reasons of reentrancy and panic safety we collect into a `SmallVec<[_; 8]>` before
|
||||
/// storing the elements in the arena.
|
||||
#[inline]
|
||||
pub fn try_alloc_from_iter<E>(
|
||||
&self,
|
||||
iter: impl IntoIterator<Item = Result<T, E>>,
|
||||
) -> Result<&mut [T], E> {
|
||||
// Despite the similarlty with `DroplessArena`, we cannot reuse their fast case. The reason
|
||||
// is subtle: these arenas are reentrant. In other words, `iter` may very well be holding a
|
||||
// reference to `self` and adding elements to the arena during iteration.
|
||||
|
|
@ -214,18 +228,19 @@ impl<T> TypedArena<T> {
|
|||
// doesn't need to be hyper-optimized.
|
||||
assert!(size_of::<T>() != 0);
|
||||
|
||||
let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect();
|
||||
let vec: Result<SmallVec<[T; 8]>, E> = iter.into_iter().collect();
|
||||
let mut vec = vec?;
|
||||
if vec.is_empty() {
|
||||
return &mut [];
|
||||
return Ok(&mut []);
|
||||
}
|
||||
// Move the content to the arena by copying and then forgetting it.
|
||||
let len = vec.len();
|
||||
let start_ptr = self.alloc_raw_slice(len);
|
||||
unsafe {
|
||||
Ok(unsafe {
|
||||
vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
|
||||
vec.set_len(0);
|
||||
slice::from_raw_parts_mut(start_ptr, len)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Grows the arena.
|
||||
|
|
@ -566,27 +581,34 @@ impl DroplessArena {
|
|||
// `drop`.
|
||||
unsafe { self.write_from_iter(iter, len, mem) }
|
||||
}
|
||||
(_, _) => {
|
||||
outline(move || -> &mut [T] {
|
||||
// Takes care of reentrancy.
|
||||
let mut vec: SmallVec<[_; 8]> = iter.collect();
|
||||
if vec.is_empty() {
|
||||
return &mut [];
|
||||
}
|
||||
// Move the content to the arena by copying it and then forgetting
|
||||
// the content of the SmallVec
|
||||
unsafe {
|
||||
let len = vec.len();
|
||||
let start_ptr =
|
||||
self.alloc_raw(Layout::for_value::<[T]>(vec.as_slice())) as *mut T;
|
||||
vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
|
||||
vec.set_len(0);
|
||||
slice::from_raw_parts_mut(start_ptr, len)
|
||||
}
|
||||
})
|
||||
}
|
||||
(_, _) => outline(move || self.try_alloc_from_iter(iter.map(Ok::<T, !>)).into_ok()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_alloc_from_iter<T, E>(
|
||||
&self,
|
||||
iter: impl IntoIterator<Item = Result<T, E>>,
|
||||
) -> Result<&mut [T], E> {
|
||||
// Despite the similarlty with `alloc_from_iter`, we cannot reuse their fast case, as we
|
||||
// cannot know the minimum length of the iterator in this case.
|
||||
assert!(size_of::<T>() != 0);
|
||||
|
||||
// Takes care of reentrancy.
|
||||
let vec: Result<SmallVec<[T; 8]>, E> = iter.into_iter().collect();
|
||||
let mut vec = vec?;
|
||||
if vec.is_empty() {
|
||||
return Ok(&mut []);
|
||||
}
|
||||
// Move the content to the arena by copying and then forgetting it.
|
||||
let len = vec.len();
|
||||
Ok(unsafe {
|
||||
let start_ptr = self.alloc_raw(Layout::for_value::<[T]>(vec.as_slice())) as *mut T;
|
||||
vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
|
||||
vec.set_len(0);
|
||||
slice::from_raw_parts_mut(start_ptr, len)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Declare an `Arena` containing one dropless arena and many typed arenas (the
|
||||
|
|
|
|||
|
|
@ -2469,6 +2469,8 @@ pub enum TyPatKind {
|
|||
/// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`).
|
||||
Range(Option<P<AnonConst>>, Option<P<AnonConst>>, Spanned<RangeEnd>),
|
||||
|
||||
Or(ThinVec<P<TyPat>>),
|
||||
|
||||
/// Placeholder for a pattern that wasn't syntactically well formed in some way.
|
||||
Err(ErrorGuaranteed),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
test(attr(deny(warnings)))
|
||||
)]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(array_windows)]
|
||||
#![feature(associated_type_defaults)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(if_let_guard)]
|
||||
|
|
@ -19,6 +20,7 @@
|
|||
#![feature(never_type)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
#![recursion_limit = "256"]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
pub mod util {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
use std::ops::DerefMut;
|
||||
use std::panic;
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
|
|
@ -20,7 +19,6 @@ use thin_vec::ThinVec;
|
|||
|
||||
use crate::ast::*;
|
||||
use crate::ptr::P;
|
||||
use crate::token::{self, Token};
|
||||
use crate::tokenstream::*;
|
||||
use crate::visit::{AssocCtxt, BoundKind, FnCtxt};
|
||||
|
||||
|
|
@ -48,11 +46,6 @@ pub trait WalkItemKind {
|
|||
}
|
||||
|
||||
pub trait MutVisitor: Sized {
|
||||
/// Mutable token visiting only exists for the `macro_rules` token marker and should not be
|
||||
/// used otherwise. Token visitor would be entirely separate from the regular visitor if
|
||||
/// the marker didn't have to visit AST fragments in nonterminal tokens.
|
||||
const VISIT_TOKENS: bool = false;
|
||||
|
||||
// Methods in this trait have one of three forms:
|
||||
//
|
||||
// fn visit_t(&mut self, t: &mut T); // common
|
||||
|
|
@ -360,6 +353,8 @@ pub trait MutVisitor: Sized {
|
|||
// Do nothing.
|
||||
}
|
||||
|
||||
// Span visiting is no longer used, but we keep it for now,
|
||||
// in case it's needed for something like #127241.
|
||||
fn visit_span(&mut self, _sp: &mut Span) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
|
@ -473,12 +468,8 @@ fn visit_attr_args<T: MutVisitor>(vis: &mut T, args: &mut AttrArgs) {
|
|||
|
||||
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
|
||||
fn visit_delim_args<T: MutVisitor>(vis: &mut T, args: &mut DelimArgs) {
|
||||
let DelimArgs { dspan, delim: _, tokens } = args;
|
||||
visit_tts(vis, tokens);
|
||||
visit_delim_span(vis, dspan);
|
||||
}
|
||||
|
||||
pub fn visit_delim_span<T: MutVisitor>(vis: &mut T, DelimSpan { open, close }: &mut DelimSpan) {
|
||||
let DelimArgs { dspan, delim: _, tokens: _ } = args;
|
||||
let DelimSpan { open, close } = dspan;
|
||||
vis.visit_span(open);
|
||||
vis.visit_span(close);
|
||||
}
|
||||
|
|
@ -552,7 +543,7 @@ fn walk_assoc_item_constraint<T: MutVisitor>(
|
|||
}
|
||||
|
||||
pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) {
|
||||
let Ty { id, kind, span, tokens } = ty.deref_mut();
|
||||
let Ty { id, kind, span, tokens: _ } = ty.deref_mut();
|
||||
vis.visit_id(id);
|
||||
match kind {
|
||||
TyKind::Err(_guar) => {}
|
||||
|
|
@ -600,21 +591,20 @@ pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) {
|
|||
}
|
||||
TyKind::MacCall(mac) => vis.visit_mac_call(mac),
|
||||
}
|
||||
visit_lazy_tts(vis, tokens);
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
pub fn walk_ty_pat<T: MutVisitor>(vis: &mut T, ty: &mut P<TyPat>) {
|
||||
let TyPat { id, kind, span, tokens } = ty.deref_mut();
|
||||
let TyPat { id, kind, span, tokens: _ } = ty.deref_mut();
|
||||
vis.visit_id(id);
|
||||
match kind {
|
||||
TyPatKind::Range(start, end, _include_end) => {
|
||||
visit_opt(start, |c| vis.visit_anon_const(c));
|
||||
visit_opt(end, |c| vis.visit_anon_const(c));
|
||||
}
|
||||
TyPatKind::Or(variants) => visit_thin_vec(variants, |p| vis.visit_ty_pat(p)),
|
||||
TyPatKind::Err(_) => {}
|
||||
}
|
||||
visit_lazy_tts(vis, tokens);
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
|
|
@ -654,11 +644,10 @@ fn walk_path_segment<T: MutVisitor>(vis: &mut T, segment: &mut PathSegment) {
|
|||
visit_opt(args, |args| vis.visit_generic_args(args));
|
||||
}
|
||||
|
||||
fn walk_path<T: MutVisitor>(vis: &mut T, Path { segments, span, tokens }: &mut Path) {
|
||||
fn walk_path<T: MutVisitor>(vis: &mut T, Path { segments, span, tokens: _ }: &mut Path) {
|
||||
for segment in segments {
|
||||
vis.visit_path_segment(segment);
|
||||
}
|
||||
visit_lazy_tts(vis, tokens);
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
|
|
@ -704,7 +693,7 @@ fn walk_parenthesized_parameter_data<T: MutVisitor>(vis: &mut T, args: &mut Pare
|
|||
}
|
||||
|
||||
fn walk_local<T: MutVisitor>(vis: &mut T, local: &mut P<Local>) {
|
||||
let Local { id, super_, pat, ty, kind, span, colon_sp, attrs, tokens } = local.deref_mut();
|
||||
let Local { id, super_, pat, ty, kind, span, colon_sp, attrs, tokens: _ } = local.deref_mut();
|
||||
visit_opt(super_, |sp| vis.visit_span(sp));
|
||||
vis.visit_id(id);
|
||||
visit_attrs(vis, attrs);
|
||||
|
|
@ -720,7 +709,6 @@ fn walk_local<T: MutVisitor>(vis: &mut T, local: &mut P<Local>) {
|
|||
vis.visit_block(els);
|
||||
}
|
||||
}
|
||||
visit_lazy_tts(vis, tokens);
|
||||
visit_opt(colon_sp, |sp| vis.visit_span(sp));
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
|
@ -729,14 +717,10 @@ fn walk_attribute<T: MutVisitor>(vis: &mut T, attr: &mut Attribute) {
|
|||
let Attribute { kind, id: _, style: _, span } = attr;
|
||||
match kind {
|
||||
AttrKind::Normal(normal) => {
|
||||
let NormalAttr {
|
||||
item: AttrItem { unsafety: _, path, args, tokens },
|
||||
tokens: attr_tokens,
|
||||
} = &mut **normal;
|
||||
let NormalAttr { item: AttrItem { unsafety: _, path, args, tokens: _ }, tokens: _ } =
|
||||
&mut **normal;
|
||||
vis.visit_path(path);
|
||||
visit_attr_args(vis, args);
|
||||
visit_lazy_tts(vis, tokens);
|
||||
visit_lazy_tts(vis, attr_tokens);
|
||||
}
|
||||
AttrKind::DocComment(_kind, _sym) => {}
|
||||
}
|
||||
|
|
@ -785,90 +769,6 @@ pub fn walk_flat_map_param<T: MutVisitor>(vis: &mut T, mut param: Param) -> Smal
|
|||
smallvec![param]
|
||||
}
|
||||
|
||||
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
|
||||
fn visit_attr_tt<T: MutVisitor>(vis: &mut T, tt: &mut AttrTokenTree) {
|
||||
match tt {
|
||||
AttrTokenTree::Token(token, _spacing) => {
|
||||
visit_token(vis, token);
|
||||
}
|
||||
AttrTokenTree::Delimited(dspan, _spacing, _delim, tts) => {
|
||||
visit_attr_tts(vis, tts);
|
||||
visit_delim_span(vis, dspan);
|
||||
}
|
||||
AttrTokenTree::AttrsTarget(AttrsTarget { attrs, tokens }) => {
|
||||
visit_attrs(vis, attrs);
|
||||
visit_lazy_tts_opt_mut(vis, Some(tokens));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
|
||||
fn visit_tt<T: MutVisitor>(vis: &mut T, tt: &mut TokenTree) {
|
||||
match tt {
|
||||
TokenTree::Token(token, _spacing) => {
|
||||
visit_token(vis, token);
|
||||
}
|
||||
TokenTree::Delimited(dspan, _spacing, _delim, tts) => {
|
||||
visit_tts(vis, tts);
|
||||
visit_delim_span(vis, dspan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
|
||||
fn visit_tts<T: MutVisitor>(vis: &mut T, TokenStream(tts): &mut TokenStream) {
|
||||
if T::VISIT_TOKENS && !tts.is_empty() {
|
||||
let tts = Arc::make_mut(tts);
|
||||
visit_vec(tts, |tree| visit_tt(vis, tree));
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_attr_tts<T: MutVisitor>(vis: &mut T, AttrTokenStream(tts): &mut AttrTokenStream) {
|
||||
if T::VISIT_TOKENS && !tts.is_empty() {
|
||||
let tts = Arc::make_mut(tts);
|
||||
visit_vec(tts, |tree| visit_attr_tt(vis, tree));
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_lazy_tts_opt_mut<T: MutVisitor>(vis: &mut T, lazy_tts: Option<&mut LazyAttrTokenStream>) {
|
||||
if T::VISIT_TOKENS {
|
||||
if let Some(lazy_tts) = lazy_tts {
|
||||
let mut tts = lazy_tts.to_attr_token_stream();
|
||||
visit_attr_tts(vis, &mut tts);
|
||||
*lazy_tts = LazyAttrTokenStream::new(tts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_lazy_tts<T: MutVisitor>(vis: &mut T, lazy_tts: &mut Option<LazyAttrTokenStream>) {
|
||||
visit_lazy_tts_opt_mut(vis, lazy_tts.as_mut());
|
||||
}
|
||||
|
||||
/// Applies ident visitor if it's an ident. In practice this is not actually
|
||||
/// used by specific visitors right now, but there's a test below checking that
|
||||
/// it works.
|
||||
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
|
||||
pub fn visit_token<T: MutVisitor>(vis: &mut T, t: &mut Token) {
|
||||
let Token { kind, span } = t;
|
||||
match kind {
|
||||
token::Ident(name, _is_raw) | token::Lifetime(name, _is_raw) => {
|
||||
let mut ident = Ident::new(*name, *span);
|
||||
vis.visit_ident(&mut ident);
|
||||
*name = ident.name;
|
||||
*span = ident.span;
|
||||
return; // Avoid visiting the span for the second time.
|
||||
}
|
||||
token::NtIdent(ident, _is_raw) => {
|
||||
vis.visit_ident(ident);
|
||||
}
|
||||
token::NtLifetime(ident, _is_raw) => {
|
||||
vis.visit_ident(ident);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
|
||||
fn visit_defaultness<T: MutVisitor>(vis: &mut T, defaultness: &mut Defaultness) {
|
||||
match defaultness {
|
||||
|
|
@ -1187,10 +1087,9 @@ fn walk_mt<T: MutVisitor>(vis: &mut T, MutTy { ty, mutbl: _ }: &mut MutTy) {
|
|||
}
|
||||
|
||||
pub fn walk_block<T: MutVisitor>(vis: &mut T, block: &mut P<Block>) {
|
||||
let Block { id, stmts, rules: _, span, tokens } = block.deref_mut();
|
||||
let Block { id, stmts, rules: _, span, tokens: _ } = block.deref_mut();
|
||||
vis.visit_id(id);
|
||||
stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt));
|
||||
visit_lazy_tts(vis, tokens);
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
|
|
@ -1471,12 +1370,11 @@ fn walk_item_ctxt<K: WalkItemKind>(
|
|||
item: &mut P<Item<K>>,
|
||||
ctxt: K::Ctxt,
|
||||
) {
|
||||
let Item { attrs, id, kind, vis, span, tokens } = item.deref_mut();
|
||||
let Item { attrs, id, kind, vis, span, tokens: _ } = item.deref_mut();
|
||||
visitor.visit_id(id);
|
||||
visit_attrs(visitor, attrs);
|
||||
visitor.visit_vis(vis);
|
||||
kind.walk(*span, *id, vis, ctxt, visitor);
|
||||
visit_lazy_tts(visitor, tokens);
|
||||
visitor.visit_span(span);
|
||||
}
|
||||
|
||||
|
|
@ -1550,7 +1448,7 @@ impl WalkItemKind for ForeignItemKind {
|
|||
}
|
||||
|
||||
pub fn walk_pat<T: MutVisitor>(vis: &mut T, pat: &mut P<Pat>) {
|
||||
let Pat { id, kind, span, tokens } = pat.deref_mut();
|
||||
let Pat { id, kind, span, tokens: _ } = pat.deref_mut();
|
||||
vis.visit_id(id);
|
||||
match kind {
|
||||
PatKind::Err(_guar) => {}
|
||||
|
|
@ -1592,7 +1490,6 @@ pub fn walk_pat<T: MutVisitor>(vis: &mut T, pat: &mut P<Pat>) {
|
|||
PatKind::Paren(inner) => vis.visit_pat(inner),
|
||||
PatKind::MacCall(mac) => vis.visit_mac_call(mac),
|
||||
}
|
||||
visit_lazy_tts(vis, tokens);
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
|
|
@ -1656,7 +1553,7 @@ fn walk_format_args<T: MutVisitor>(vis: &mut T, fmt: &mut FormatArgs) {
|
|||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, tokens }: &mut Expr) {
|
||||
pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, tokens: _ }: &mut Expr) {
|
||||
vis.visit_id(id);
|
||||
visit_attrs(vis, attrs);
|
||||
match kind {
|
||||
|
|
@ -1847,7 +1744,6 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
|
|||
ExprKind::Err(_guar) => {}
|
||||
ExprKind::Dummy => {}
|
||||
}
|
||||
visit_lazy_tts(vis, tokens);
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
|
|
@ -1889,17 +1785,16 @@ fn walk_flat_map_stmt_kind<T: MutVisitor>(vis: &mut T, kind: StmtKind) -> SmallV
|
|||
StmtKind::Semi(expr) => vis.filter_map_expr(expr).into_iter().map(StmtKind::Semi).collect(),
|
||||
StmtKind::Empty => smallvec![StmtKind::Empty],
|
||||
StmtKind::MacCall(mut mac) => {
|
||||
let MacCallStmt { mac: mac_, style: _, attrs, tokens } = mac.deref_mut();
|
||||
let MacCallStmt { mac: mac_, style: _, attrs, tokens: _ } = mac.deref_mut();
|
||||
visit_attrs(vis, attrs);
|
||||
vis.visit_mac_call(mac_);
|
||||
visit_lazy_tts(vis, tokens);
|
||||
smallvec![StmtKind::MacCall(mac)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_vis<T: MutVisitor>(vis: &mut T, visibility: &mut Visibility) {
|
||||
let Visibility { kind, span, tokens } = visibility;
|
||||
let Visibility { kind, span, tokens: _ } = visibility;
|
||||
match kind {
|
||||
VisibilityKind::Public | VisibilityKind::Inherited => {}
|
||||
VisibilityKind::Restricted { path, id, shorthand: _ } => {
|
||||
|
|
@ -1907,7 +1802,6 @@ fn walk_vis<T: MutVisitor>(vis: &mut T, visibility: &mut Visibility) {
|
|||
vis.visit_path(path);
|
||||
}
|
||||
}
|
||||
visit_lazy_tts(vis, tokens);
|
||||
vis.visit_span(span);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,14 +14,16 @@
|
|||
//! ownership of the original.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
use std::{cmp, fmt, iter};
|
||||
use std::{cmp, fmt, iter, mem};
|
||||
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::sync;
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
|
||||
use rustc_serialize::{Decodable, Encodable};
|
||||
use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::ast::AttrStyle;
|
||||
use crate::ast_traits::{HasAttrs, HasTokens};
|
||||
|
|
@ -106,25 +108,30 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub trait ToAttrTokenStream: sync::DynSend + sync::DynSync {
|
||||
fn to_attr_token_stream(&self) -> AttrTokenStream;
|
||||
}
|
||||
|
||||
impl ToAttrTokenStream for AttrTokenStream {
|
||||
fn to_attr_token_stream(&self) -> AttrTokenStream {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// A lazy version of [`TokenStream`], which defers creation
|
||||
/// of an actual `TokenStream` until it is needed.
|
||||
/// `Box` is here only to reduce the structure size.
|
||||
/// A lazy version of [`AttrTokenStream`], which defers creation of an actual
|
||||
/// `AttrTokenStream` until it is needed.
|
||||
#[derive(Clone)]
|
||||
pub struct LazyAttrTokenStream(Arc<Box<dyn ToAttrTokenStream>>);
|
||||
pub struct LazyAttrTokenStream(Arc<LazyAttrTokenStreamInner>);
|
||||
|
||||
impl LazyAttrTokenStream {
|
||||
pub fn new(inner: impl ToAttrTokenStream + 'static) -> LazyAttrTokenStream {
|
||||
LazyAttrTokenStream(Arc::new(Box::new(inner)))
|
||||
pub fn new_direct(stream: AttrTokenStream) -> LazyAttrTokenStream {
|
||||
LazyAttrTokenStream(Arc::new(LazyAttrTokenStreamInner::Direct(stream)))
|
||||
}
|
||||
|
||||
pub fn new_pending(
|
||||
start_token: (Token, Spacing),
|
||||
cursor_snapshot: TokenCursor,
|
||||
num_calls: u32,
|
||||
break_last_token: u32,
|
||||
node_replacements: ThinVec<NodeReplacement>,
|
||||
) -> LazyAttrTokenStream {
|
||||
LazyAttrTokenStream(Arc::new(LazyAttrTokenStreamInner::Pending {
|
||||
start_token,
|
||||
cursor_snapshot,
|
||||
num_calls,
|
||||
break_last_token,
|
||||
node_replacements,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn to_attr_token_stream(&self) -> AttrTokenStream {
|
||||
|
|
@ -156,6 +163,184 @@ impl<CTX> HashStable<CTX> for LazyAttrTokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
/// A token range within a `Parser`'s full token stream.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ParserRange(pub Range<u32>);
|
||||
|
||||
/// A token range within an individual AST node's (lazy) token stream, i.e.
|
||||
/// relative to that node's first token. Distinct from `ParserRange` so the two
|
||||
/// kinds of range can't be mixed up.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NodeRange(pub Range<u32>);
|
||||
|
||||
/// Indicates a range of tokens that should be replaced by an `AttrsTarget`
|
||||
/// (replacement) or be replaced by nothing (deletion). This is used in two
|
||||
/// places during token collection.
|
||||
///
|
||||
/// 1. Replacement. During the parsing of an AST node that may have a
|
||||
/// `#[derive]` attribute, when we parse a nested AST node that has `#[cfg]`
|
||||
/// or `#[cfg_attr]`, we replace the entire inner AST node with
|
||||
/// `FlatToken::AttrsTarget`. This lets us perform eager cfg-expansion on an
|
||||
/// `AttrTokenStream`.
|
||||
///
|
||||
/// 2. Deletion. We delete inner attributes from all collected token streams,
|
||||
/// and instead track them through the `attrs` field on the AST node. This
|
||||
/// lets us manipulate them similarly to outer attributes. When we create a
|
||||
/// `TokenStream`, the inner attributes are inserted into the proper place
|
||||
/// in the token stream.
|
||||
///
|
||||
/// Each replacement starts off in `ParserReplacement` form but is converted to
|
||||
/// `NodeReplacement` form when it is attached to a single AST node, via
|
||||
/// `LazyAttrTokenStreamImpl`.
|
||||
pub type ParserReplacement = (ParserRange, Option<AttrsTarget>);
|
||||
|
||||
/// See the comment on `ParserReplacement`.
|
||||
pub type NodeReplacement = (NodeRange, Option<AttrsTarget>);
|
||||
|
||||
impl NodeRange {
|
||||
// Converts a range within a parser's tokens to a range within a
|
||||
// node's tokens beginning at `start_pos`.
|
||||
//
|
||||
// For example, imagine a parser with 50 tokens in its token stream, a
|
||||
// function that spans `ParserRange(20..40)` and an inner attribute within
|
||||
// that function that spans `ParserRange(30..35)`. We would find the inner
|
||||
// attribute's range within the function's tokens by subtracting 20, which
|
||||
// is the position of the function's start token. This gives
|
||||
// `NodeRange(10..15)`.
|
||||
pub fn new(ParserRange(parser_range): ParserRange, start_pos: u32) -> NodeRange {
|
||||
assert!(!parser_range.is_empty());
|
||||
assert!(parser_range.start >= start_pos);
|
||||
NodeRange((parser_range.start - start_pos)..(parser_range.end - start_pos))
|
||||
}
|
||||
}
|
||||
|
||||
enum LazyAttrTokenStreamInner {
|
||||
// The token stream has already been produced.
|
||||
Direct(AttrTokenStream),
|
||||
|
||||
// From a value of this type we can reconstruct the `TokenStream` seen by
|
||||
// the `f` callback passed to a call to `Parser::collect_tokens`, by
|
||||
// replaying the getting of the tokens. This saves us producing a
|
||||
// `TokenStream` if it is never needed, e.g. a captured `macro_rules!`
|
||||
// argument that is never passed to a proc macro. In practice, token stream
|
||||
// creation happens rarely compared to calls to `collect_tokens` (see some
|
||||
// statistics in #78736) so we are doing as little up-front work as
|
||||
// possible.
|
||||
//
|
||||
// This also makes `Parser` very cheap to clone, since there is no
|
||||
// intermediate collection buffer to clone.
|
||||
Pending {
|
||||
start_token: (Token, Spacing),
|
||||
cursor_snapshot: TokenCursor,
|
||||
num_calls: u32,
|
||||
break_last_token: u32,
|
||||
node_replacements: ThinVec<NodeReplacement>,
|
||||
},
|
||||
}
|
||||
|
||||
impl LazyAttrTokenStreamInner {
|
||||
fn to_attr_token_stream(&self) -> AttrTokenStream {
|
||||
match self {
|
||||
LazyAttrTokenStreamInner::Direct(stream) => stream.clone(),
|
||||
LazyAttrTokenStreamInner::Pending {
|
||||
start_token,
|
||||
cursor_snapshot,
|
||||
num_calls,
|
||||
break_last_token,
|
||||
node_replacements,
|
||||
} => {
|
||||
// The token produced by the final call to `{,inlined_}next` was not
|
||||
// actually consumed by the callback. The combination of chaining the
|
||||
// initial token and using `take` produces the desired result - we
|
||||
// produce an empty `TokenStream` if no calls were made, and omit the
|
||||
// final token otherwise.
|
||||
let mut cursor_snapshot = cursor_snapshot.clone();
|
||||
let tokens = iter::once(FlatToken::Token(*start_token))
|
||||
.chain(iter::repeat_with(|| FlatToken::Token(cursor_snapshot.next())))
|
||||
.take(*num_calls as usize);
|
||||
|
||||
if node_replacements.is_empty() {
|
||||
make_attr_token_stream(tokens, *break_last_token)
|
||||
} else {
|
||||
let mut tokens: Vec<_> = tokens.collect();
|
||||
let mut node_replacements = node_replacements.to_vec();
|
||||
node_replacements.sort_by_key(|(range, _)| range.0.start);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
for [(node_range, tokens), (next_node_range, next_tokens)] in
|
||||
node_replacements.array_windows()
|
||||
{
|
||||
assert!(
|
||||
node_range.0.end <= next_node_range.0.start
|
||||
|| node_range.0.end >= next_node_range.0.end,
|
||||
"Node ranges should be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})",
|
||||
node_range,
|
||||
tokens,
|
||||
next_node_range,
|
||||
next_tokens,
|
||||
);
|
||||
}
|
||||
|
||||
// Process the replace ranges, starting from the highest start
|
||||
// position and working our way back. If have tokens like:
|
||||
//
|
||||
// `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }`
|
||||
//
|
||||
// Then we will generate replace ranges for both
|
||||
// the `#[cfg(FALSE)] field: bool` and the entire
|
||||
// `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }`
|
||||
//
|
||||
// By starting processing from the replace range with the greatest
|
||||
// start position, we ensure that any (outer) replace range which
|
||||
// encloses another (inner) replace range will fully overwrite the
|
||||
// inner range's replacement.
|
||||
for (node_range, target) in node_replacements.into_iter().rev() {
|
||||
assert!(
|
||||
!node_range.0.is_empty(),
|
||||
"Cannot replace an empty node range: {:?}",
|
||||
node_range.0
|
||||
);
|
||||
|
||||
// Replace the tokens in range with zero or one `FlatToken::AttrsTarget`s,
|
||||
// plus enough `FlatToken::Empty`s to fill up the rest of the range. This
|
||||
// keeps the total length of `tokens` constant throughout the replacement
|
||||
// process, allowing us to do all replacements without adjusting indices.
|
||||
let target_len = target.is_some() as usize;
|
||||
tokens.splice(
|
||||
(node_range.0.start as usize)..(node_range.0.end as usize),
|
||||
target.into_iter().map(|target| FlatToken::AttrsTarget(target)).chain(
|
||||
iter::repeat(FlatToken::Empty)
|
||||
.take(node_range.0.len() - target_len),
|
||||
),
|
||||
);
|
||||
}
|
||||
make_attr_token_stream(tokens.into_iter(), *break_last_token)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper struct used when building an `AttrTokenStream` from
|
||||
/// a `LazyAttrTokenStream`. Both delimiter and non-delimited tokens
|
||||
/// are stored as `FlatToken::Token`. A vector of `FlatToken`s
|
||||
/// is then 'parsed' to build up an `AttrTokenStream` with nested
|
||||
/// `AttrTokenTree::Delimited` tokens.
|
||||
#[derive(Debug, Clone)]
|
||||
enum FlatToken {
|
||||
/// A token - this holds both delimiter (e.g. '{' and '}')
|
||||
/// and non-delimiter tokens
|
||||
Token((Token, Spacing)),
|
||||
/// Holds the `AttrsTarget` for an AST node. The `AttrsTarget` is inserted
|
||||
/// directly into the constructed `AttrTokenStream` as an
|
||||
/// `AttrTokenTree::AttrsTarget`.
|
||||
AttrsTarget(AttrsTarget),
|
||||
/// A special 'empty' token that is ignored during the conversion
|
||||
/// to an `AttrTokenStream`. This is used to simplify the
|
||||
/// handling of replace ranges.
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// An `AttrTokenStream` is similar to a `TokenStream`, but with extra
|
||||
/// information about the tokens for attribute targets. This is used
|
||||
/// during expansion to perform early cfg-expansion, and to process attributes
|
||||
|
|
@ -163,6 +348,71 @@ impl<CTX> HashStable<CTX> for LazyAttrTokenStream {
|
|||
#[derive(Clone, Debug, Default, Encodable, Decodable)]
|
||||
pub struct AttrTokenStream(pub Arc<Vec<AttrTokenTree>>);
|
||||
|
||||
/// Converts a flattened iterator of tokens (including open and close delimiter tokens) into an
|
||||
/// `AttrTokenStream`, creating an `AttrTokenTree::Delimited` for each matching pair of open and
|
||||
/// close delims.
|
||||
fn make_attr_token_stream(
|
||||
iter: impl Iterator<Item = FlatToken>,
|
||||
break_last_token: u32,
|
||||
) -> AttrTokenStream {
|
||||
#[derive(Debug)]
|
||||
struct FrameData {
|
||||
// This is `None` for the first frame, `Some` for all others.
|
||||
open_delim_sp: Option<(Delimiter, Span, Spacing)>,
|
||||
inner: Vec<AttrTokenTree>,
|
||||
}
|
||||
// The stack always has at least one element. Storing it separately makes for shorter code.
|
||||
let mut stack_top = FrameData { open_delim_sp: None, inner: vec![] };
|
||||
let mut stack_rest = vec![];
|
||||
for flat_token in iter {
|
||||
match flat_token {
|
||||
FlatToken::Token((token @ Token { kind, span }, spacing)) => {
|
||||
if let Some(delim) = kind.open_delim() {
|
||||
stack_rest.push(mem::replace(
|
||||
&mut stack_top,
|
||||
FrameData { open_delim_sp: Some((delim, span, spacing)), inner: vec![] },
|
||||
));
|
||||
} else if let Some(delim) = kind.close_delim() {
|
||||
let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap());
|
||||
let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap();
|
||||
assert!(
|
||||
open_delim.eq_ignoring_invisible_origin(&delim),
|
||||
"Mismatched open/close delims: open={open_delim:?} close={span:?}"
|
||||
);
|
||||
let dspan = DelimSpan::from_pair(open_sp, span);
|
||||
let dspacing = DelimSpacing::new(open_spacing, spacing);
|
||||
let stream = AttrTokenStream::new(frame_data.inner);
|
||||
let delimited = AttrTokenTree::Delimited(dspan, dspacing, delim, stream);
|
||||
stack_top.inner.push(delimited);
|
||||
} else {
|
||||
stack_top.inner.push(AttrTokenTree::Token(token, spacing))
|
||||
}
|
||||
}
|
||||
FlatToken::AttrsTarget(target) => {
|
||||
stack_top.inner.push(AttrTokenTree::AttrsTarget(target))
|
||||
}
|
||||
FlatToken::Empty => {}
|
||||
}
|
||||
}
|
||||
|
||||
if break_last_token > 0 {
|
||||
let last_token = stack_top.inner.pop().unwrap();
|
||||
if let AttrTokenTree::Token(last_token, spacing) = last_token {
|
||||
let (unglued, _) = last_token.kind.break_two_token_op(break_last_token).unwrap();
|
||||
|
||||
// Tokens are always ASCII chars, so we can use byte arithmetic here.
|
||||
let mut first_span = last_token.span.shrink_to_lo();
|
||||
first_span =
|
||||
first_span.with_hi(first_span.lo() + rustc_span::BytePos(break_last_token));
|
||||
|
||||
stack_top.inner.push(AttrTokenTree::Token(Token::new(unglued, first_span), spacing));
|
||||
} else {
|
||||
panic!("Unexpected last token {last_token:?}")
|
||||
}
|
||||
}
|
||||
AttrTokenStream::new(stack_top.inner)
|
||||
}
|
||||
|
||||
/// Like `TokenTree`, but for `AttrTokenStream`.
|
||||
#[derive(Clone, Debug, Encodable, Decodable)]
|
||||
pub enum AttrTokenTree {
|
||||
|
|
@ -641,6 +891,104 @@ impl<'t> Iterator for TokenStreamIter<'t> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TokenTreeCursor {
|
||||
stream: TokenStream,
|
||||
/// Points to the current token tree in the stream. In `TokenCursor::curr`,
|
||||
/// this can be any token tree. In `TokenCursor::stack`, this is always a
|
||||
/// `TokenTree::Delimited`.
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl TokenTreeCursor {
|
||||
#[inline]
|
||||
pub fn new(stream: TokenStream) -> Self {
|
||||
TokenTreeCursor { stream, index: 0 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn curr(&self) -> Option<&TokenTree> {
|
||||
self.stream.get(self.index)
|
||||
}
|
||||
|
||||
pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
|
||||
self.stream.get(self.index + n)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bump(&mut self) {
|
||||
self.index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// A `TokenStream` cursor that produces `Token`s. It's a bit odd that
|
||||
/// we (a) lex tokens into a nice tree structure (`TokenStream`), and then (b)
|
||||
/// use this type to emit them as a linear sequence. But a linear sequence is
|
||||
/// what the parser expects, for the most part.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TokenCursor {
|
||||
// Cursor for the current (innermost) token stream. The index within the
|
||||
// cursor can point to any token tree in the stream (or one past the end).
|
||||
// The delimiters for this token stream are found in `self.stack.last()`;
|
||||
// if that is `None` we are in the outermost token stream which never has
|
||||
// delimiters.
|
||||
pub curr: TokenTreeCursor,
|
||||
|
||||
// Token streams surrounding the current one. The index within each cursor
|
||||
// always points to a `TokenTree::Delimited`.
|
||||
pub stack: Vec<TokenTreeCursor>,
|
||||
}
|
||||
|
||||
impl TokenCursor {
|
||||
pub fn next(&mut self) -> (Token, Spacing) {
|
||||
self.inlined_next()
|
||||
}
|
||||
|
||||
/// This always-inlined version should only be used on hot code paths.
|
||||
#[inline(always)]
|
||||
pub fn inlined_next(&mut self) -> (Token, Spacing) {
|
||||
loop {
|
||||
// FIXME: we currently don't return `Delimiter::Invisible` open/close delims. To fix
|
||||
// #67062 we will need to, whereupon the `delim != Delimiter::Invisible` conditions
|
||||
// below can be removed.
|
||||
if let Some(tree) = self.curr.curr() {
|
||||
match tree {
|
||||
&TokenTree::Token(token, spacing) => {
|
||||
debug_assert!(!token.kind.is_delim());
|
||||
let res = (token, spacing);
|
||||
self.curr.bump();
|
||||
return res;
|
||||
}
|
||||
&TokenTree::Delimited(sp, spacing, delim, ref tts) => {
|
||||
let trees = TokenTreeCursor::new(tts.clone());
|
||||
self.stack.push(mem::replace(&mut self.curr, trees));
|
||||
if !delim.skip() {
|
||||
return (Token::new(delim.as_open_token_kind(), sp.open), spacing.open);
|
||||
}
|
||||
// No open delimiter to return; continue on to the next iteration.
|
||||
}
|
||||
};
|
||||
} else if let Some(parent) = self.stack.pop() {
|
||||
// We have exhausted this token stream. Move back to its parent token stream.
|
||||
let Some(&TokenTree::Delimited(span, spacing, delim, _)) = parent.curr() else {
|
||||
panic!("parent should be Delimited")
|
||||
};
|
||||
self.curr = parent;
|
||||
self.curr.bump(); // move past the `Delimited`
|
||||
if !delim.skip() {
|
||||
return (Token::new(delim.as_close_token_kind(), span.close), spacing.close);
|
||||
}
|
||||
// No close delimiter to return; continue on to the next iteration.
|
||||
} else {
|
||||
// We have exhausted the outermost token stream. The use of
|
||||
// `Spacing::Alone` is arbitrary and immaterial, because the
|
||||
// `Eof` token's spacing is never used.
|
||||
return (Token::new(token::Eof, DUMMY_SP), Spacing::Alone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
|
||||
pub struct DelimSpan {
|
||||
pub open: Span,
|
||||
|
|
@ -687,6 +1035,7 @@ mod size_asserts {
|
|||
static_assert_size!(AttrTokenStream, 8);
|
||||
static_assert_size!(AttrTokenTree, 32);
|
||||
static_assert_size!(LazyAttrTokenStream, 8);
|
||||
static_assert_size!(LazyAttrTokenStreamInner, 88);
|
||||
static_assert_size!(Option<LazyAttrTokenStream>, 8); // must be small, used in many AST nodes
|
||||
static_assert_size!(TokenStream, 8);
|
||||
static_assert_size!(TokenTree, 32);
|
||||
|
|
|
|||
|
|
@ -608,6 +608,7 @@ pub fn walk_ty_pat<'a, V: Visitor<'a>>(visitor: &mut V, tp: &'a TyPat) -> V::Res
|
|||
visit_opt!(visitor, visit_anon_const, start);
|
||||
visit_opt!(visitor, visit_anon_const, end);
|
||||
}
|
||||
TyPatKind::Or(variants) => walk_list!(visitor, visit_ty_pat, variants),
|
||||
TyPatKind::Err(_) => {}
|
||||
}
|
||||
V::Result::output()
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ use rustc_attr_parsing::{AttributeParser, OmitDoc};
|
|||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::sync::spawn;
|
||||
use rustc_data_structures::tagged_ptr::TaggedRef;
|
||||
use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
|
||||
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
|
||||
|
|
@ -454,9 +455,14 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
|
|||
.lower_node(def_id);
|
||||
}
|
||||
|
||||
// Drop AST to free memory
|
||||
drop(ast_index);
|
||||
sess.time("drop_ast", || drop(krate));
|
||||
|
||||
// Drop AST to free memory. It can be expensive so try to drop it on a separate thread.
|
||||
let prof = sess.prof.clone();
|
||||
spawn(move || {
|
||||
let _timer = prof.verbose_generic_activity("drop_ast");
|
||||
drop(krate);
|
||||
});
|
||||
|
||||
// Don't hash unless necessary, because it's expensive.
|
||||
let opt_hir_hash =
|
||||
|
|
|
|||
|
|
@ -464,6 +464,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
)
|
||||
}),
|
||||
),
|
||||
TyPatKind::Or(variants) => {
|
||||
hir::TyPatKind::Or(self.arena.alloc_from_iter(
|
||||
variants.iter().map(|pat| self.lower_ty_pat_mut(pat, base_type)),
|
||||
))
|
||||
}
|
||||
TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -634,6 +634,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
false,
|
||||
None,
|
||||
*delim,
|
||||
None,
|
||||
tokens,
|
||||
true,
|
||||
span,
|
||||
|
|
@ -679,6 +680,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
false,
|
||||
None,
|
||||
*delim,
|
||||
Some(spacing.open),
|
||||
tts,
|
||||
convert_dollar_crate,
|
||||
dspan.entire(),
|
||||
|
|
@ -735,6 +737,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
has_bang: bool,
|
||||
ident: Option<Ident>,
|
||||
delim: Delimiter,
|
||||
open_spacing: Option<Spacing>,
|
||||
tts: &TokenStream,
|
||||
convert_dollar_crate: bool,
|
||||
span: Span,
|
||||
|
|
@ -758,16 +761,26 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
self.nbsp();
|
||||
}
|
||||
self.word("{");
|
||||
if !tts.is_empty() {
|
||||
|
||||
// Respect `Alone`, if provided, and print a space. Unless the list is empty.
|
||||
let open_space = (open_spacing == None || open_spacing == Some(Spacing::Alone))
|
||||
&& !tts.is_empty();
|
||||
if open_space {
|
||||
self.space();
|
||||
}
|
||||
let ib = self.ibox(0);
|
||||
self.print_tts(tts, convert_dollar_crate);
|
||||
self.end(ib);
|
||||
let empty = tts.is_empty();
|
||||
self.bclose(span, empty, cb.unwrap());
|
||||
|
||||
// Use `open_space` for the spacing *before* the closing delim.
|
||||
// Because spacing on delimiters is lost when going through
|
||||
// proc macros, and otherwise we can end up with ugly cases
|
||||
// like `{ x}`. Symmetry is better.
|
||||
self.bclose(span, !open_space, cb.unwrap());
|
||||
}
|
||||
delim => {
|
||||
// `open_spacing` is ignored. We never print spaces after
|
||||
// non-brace opening delims or before non-brace closing delims.
|
||||
let token_str = self.token_kind_to_string(&delim.as_open_token_kind());
|
||||
self.word(token_str);
|
||||
let ib = self.ibox(0);
|
||||
|
|
@ -797,6 +810,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
has_bang,
|
||||
Some(*ident),
|
||||
macro_def.body.delim,
|
||||
None,
|
||||
¯o_def.body.tokens,
|
||||
true,
|
||||
sp,
|
||||
|
|
@ -844,9 +858,9 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
self.end(ib);
|
||||
}
|
||||
|
||||
fn bclose_maybe_open(&mut self, span: rustc_span::Span, empty: bool, cb: Option<BoxMarker>) {
|
||||
fn bclose_maybe_open(&mut self, span: rustc_span::Span, no_space: bool, cb: Option<BoxMarker>) {
|
||||
let has_comment = self.maybe_print_comment(span.hi());
|
||||
if !empty || has_comment {
|
||||
if !no_space || has_comment {
|
||||
self.break_offset_if_not_bol(1, -INDENT_UNIT);
|
||||
}
|
||||
self.word("}");
|
||||
|
|
@ -855,9 +869,9 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
|||
}
|
||||
}
|
||||
|
||||
fn bclose(&mut self, span: rustc_span::Span, empty: bool, cb: BoxMarker) {
|
||||
fn bclose(&mut self, span: rustc_span::Span, no_space: bool, cb: BoxMarker) {
|
||||
let cb = Some(cb);
|
||||
self.bclose_maybe_open(span, empty, cb)
|
||||
self.bclose_maybe_open(span, no_space, cb)
|
||||
}
|
||||
|
||||
fn break_offset_if_not_bol(&mut self, n: usize, off: isize) {
|
||||
|
|
@ -1158,6 +1172,17 @@ impl<'a> State<'a> {
|
|||
self.print_expr_anon_const(end, &[]);
|
||||
}
|
||||
}
|
||||
rustc_ast::TyPatKind::Or(variants) => {
|
||||
let mut first = true;
|
||||
for pat in variants {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
self.word(" | ");
|
||||
}
|
||||
self.print_ty_pat(pat);
|
||||
}
|
||||
}
|
||||
rustc_ast::TyPatKind::Err(_) => {
|
||||
self.popen();
|
||||
self.word("/*ERROR*/");
|
||||
|
|
@ -1423,8 +1448,8 @@ impl<'a> State<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
let empty = !has_attrs && blk.stmts.is_empty();
|
||||
self.bclose_maybe_open(blk.span, empty, cb);
|
||||
let no_space = !has_attrs && blk.stmts.is_empty();
|
||||
self.bclose_maybe_open(blk.span, no_space, cb);
|
||||
self.ann.post(self, AnnNode::Block(blk))
|
||||
}
|
||||
|
||||
|
|
@ -1471,6 +1496,7 @@ impl<'a> State<'a> {
|
|||
true,
|
||||
None,
|
||||
m.args.delim,
|
||||
None,
|
||||
&m.args.tokens,
|
||||
true,
|
||||
m.span(),
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@
|
|||
#![cfg_attr(bootstrap, feature(let_chains))]
|
||||
#![doc(rust_logo)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![recursion_limit = "256"]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
#[macro_use]
|
||||
|
|
|
|||
|
|
@ -323,9 +323,9 @@ mod llvm_enzyme {
|
|||
Spacing::Joint,
|
||||
)];
|
||||
let never_arg = ast::DelimArgs {
|
||||
dspan: ast::tokenstream::DelimSpan::from_single(span),
|
||||
dspan: DelimSpan::from_single(span),
|
||||
delim: ast::token::Delimiter::Parenthesis,
|
||||
tokens: ast::tokenstream::TokenStream::from_iter(ts2),
|
||||
tokens: TokenStream::from_iter(ts2),
|
||||
};
|
||||
let inline_item = ast::AttrItem {
|
||||
unsafety: ast::Safety::Default,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#![feature(rustdoc_internals)]
|
||||
#![feature(string_from_utf8_lossy_owned)]
|
||||
#![feature(try_blocks)]
|
||||
#![recursion_limit = "256"]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
extern crate proc_macro;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::{AnonConst, DUMMY_NODE_ID, Ty, TyPat, TyPatKind, ast};
|
||||
use rustc_ast::{AnonConst, DUMMY_NODE_ID, Ty, TyPat, TyPatKind, ast, token};
|
||||
use rustc_errors::PResult;
|
||||
use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
|
||||
use rustc_parse::exp;
|
||||
use rustc_parse::parser::{CommaRecoveryMode, RecoverColon, RecoverComma};
|
||||
use rustc_span::Span;
|
||||
|
||||
pub(crate) fn expand<'cx>(
|
||||
|
|
@ -26,19 +27,42 @@ fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P
|
|||
|
||||
let ty = parser.parse_ty()?;
|
||||
parser.expect_keyword(exp!(Is))?;
|
||||
let pat = parser.parse_pat_no_top_alt(None, None)?.into_inner();
|
||||
|
||||
let pat = pat_to_ty_pat(
|
||||
cx,
|
||||
parser
|
||||
.parse_pat_no_top_guard(
|
||||
None,
|
||||
RecoverComma::No,
|
||||
RecoverColon::No,
|
||||
CommaRecoveryMode::EitherTupleOrPipe,
|
||||
)?
|
||||
.into_inner(),
|
||||
);
|
||||
|
||||
if parser.token != token::Eof {
|
||||
parser.unexpected()?;
|
||||
}
|
||||
|
||||
Ok((ty, pat))
|
||||
}
|
||||
|
||||
fn ty_pat(kind: TyPatKind, span: Span) -> P<TyPat> {
|
||||
P(TyPat { id: DUMMY_NODE_ID, kind, span, tokens: None })
|
||||
}
|
||||
|
||||
fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> P<TyPat> {
|
||||
let kind = match pat.kind {
|
||||
ast::PatKind::Range(start, end, include_end) => TyPatKind::Range(
|
||||
start.map(|value| P(AnonConst { id: DUMMY_NODE_ID, value })),
|
||||
end.map(|value| P(AnonConst { id: DUMMY_NODE_ID, value })),
|
||||
include_end,
|
||||
),
|
||||
ast::PatKind::Or(variants) => TyPatKind::Or(
|
||||
variants.into_iter().map(|pat| pat_to_ty_pat(cx, pat.into_inner())).collect(),
|
||||
),
|
||||
ast::PatKind::Err(guar) => TyPatKind::Err(guar),
|
||||
_ => TyPatKind::Err(cx.dcx().span_err(pat.span, "pattern not supported in pattern types")),
|
||||
};
|
||||
|
||||
let pat = P(TyPat { id: pat.id, kind, span: pat.span, tokens: pat.tokens });
|
||||
|
||||
Ok((ty, pat))
|
||||
ty_pat(kind, pat.span)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ use rustc_ast::InlineAsmOptions;
|
|||
use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization;
|
||||
use rustc_data_structures::profiling::SelfProfilerRef;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::InlineAsmMacro;
|
||||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv};
|
||||
|
|
@ -18,7 +16,6 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
|
|||
use crate::constant::ConstantCx;
|
||||
use crate::debuginfo::{FunctionDebugContext, TypeDebugContext};
|
||||
use crate::enable_verifier;
|
||||
use crate::inline_asm::codegen_naked_asm;
|
||||
use crate::prelude::*;
|
||||
use crate::pretty_clif::CommentWriter;
|
||||
|
||||
|
|
@ -37,7 +34,7 @@ pub(crate) fn codegen_fn<'tcx>(
|
|||
cached_func: Function,
|
||||
module: &mut dyn Module,
|
||||
instance: Instance<'tcx>,
|
||||
) -> Option<CodegenedFunction> {
|
||||
) -> CodegenedFunction {
|
||||
debug_assert!(!instance.args.has_infer());
|
||||
|
||||
let symbol_name = tcx.symbol_name(instance).name.to_string();
|
||||
|
|
@ -54,38 +51,6 @@ pub(crate) fn codegen_fn<'tcx>(
|
|||
String::from_utf8_lossy(&buf).into_owned()
|
||||
});
|
||||
|
||||
if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) {
|
||||
assert_eq!(mir.basic_blocks.len(), 1);
|
||||
assert!(mir.basic_blocks[START_BLOCK].statements.is_empty());
|
||||
|
||||
match &mir.basic_blocks[START_BLOCK].terminator().kind {
|
||||
TerminatorKind::InlineAsm {
|
||||
asm_macro: InlineAsmMacro::NakedAsm,
|
||||
template,
|
||||
operands,
|
||||
options,
|
||||
line_spans: _,
|
||||
targets: _,
|
||||
unwind: _,
|
||||
} => {
|
||||
codegen_naked_asm(
|
||||
tcx,
|
||||
cx,
|
||||
module,
|
||||
instance,
|
||||
mir.basic_blocks[START_BLOCK].terminator().source_info.span,
|
||||
&symbol_name,
|
||||
template,
|
||||
operands,
|
||||
*options,
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
// Declare function
|
||||
let sig = get_function_sig(tcx, module.target_config().default_call_conv, instance);
|
||||
let func_id = module.declare_function(&symbol_name, Linkage::Local, &sig).unwrap();
|
||||
|
|
@ -166,7 +131,7 @@ pub(crate) fn codegen_fn<'tcx>(
|
|||
// Verify function
|
||||
verify_func(tcx, &clif_comments, &func);
|
||||
|
||||
Some(CodegenedFunction { symbol_name, func_id, func, clif_comments, func_debug_cx })
|
||||
CodegenedFunction { symbol_name, func_id, func, clif_comments, func_debug_cx }
|
||||
}
|
||||
|
||||
pub(crate) fn compile_fn(
|
||||
|
|
|
|||
|
|
@ -22,7 +22,10 @@ use rustc_data_structures::sync::{IntoDynSyncSend, par_map};
|
|||
use rustc_metadata::EncodedMetadata;
|
||||
use rustc_metadata::fs::copy_to_stdout;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
use rustc_middle::mir::mono::{CodegenUnit, MonoItem};
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::mono::{
|
||||
CodegenUnit, Linkage as RLinkage, MonoItem, MonoItemData, Visibility,
|
||||
};
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::{DebugInfo, OutFileName, OutputFilenames, OutputType};
|
||||
|
||||
|
|
@ -30,7 +33,7 @@ use crate::CodegenCx;
|
|||
use crate::base::CodegenedFunction;
|
||||
use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken};
|
||||
use crate::debuginfo::TypeDebugContext;
|
||||
use crate::global_asm::GlobalAsmConfig;
|
||||
use crate::global_asm::{GlobalAsmConfig, GlobalAsmContext};
|
||||
use crate::prelude::*;
|
||||
use crate::unwind_module::UnwindModule;
|
||||
|
||||
|
|
@ -530,19 +533,35 @@ fn codegen_cgu_content(
|
|||
let mut type_dbg = TypeDebugContext::default();
|
||||
super::predefine_mono_items(tcx, module, &mono_items);
|
||||
let mut codegened_functions = vec![];
|
||||
for (mono_item, _) in mono_items {
|
||||
for (mono_item, item_data) in mono_items {
|
||||
match mono_item {
|
||||
MonoItem::Fn(inst) => {
|
||||
if let Some(codegened_function) = crate::base::codegen_fn(
|
||||
MonoItem::Fn(instance) => {
|
||||
if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED)
|
||||
{
|
||||
rustc_codegen_ssa::mir::naked_asm::codegen_naked_asm(
|
||||
&mut GlobalAsmContext { tcx, global_asm: &mut cx.global_asm },
|
||||
instance,
|
||||
MonoItemData {
|
||||
linkage: RLinkage::External,
|
||||
visibility: if item_data.linkage == RLinkage::Internal {
|
||||
Visibility::Hidden
|
||||
} else {
|
||||
item_data.visibility
|
||||
},
|
||||
..item_data
|
||||
},
|
||||
);
|
||||
continue;
|
||||
}
|
||||
let codegened_function = crate::base::codegen_fn(
|
||||
tcx,
|
||||
&mut cx,
|
||||
&mut type_dbg,
|
||||
Function::new(),
|
||||
module,
|
||||
inst,
|
||||
) {
|
||||
codegened_functions.push(codegened_function);
|
||||
}
|
||||
instance,
|
||||
);
|
||||
codegened_functions.push(codegened_function);
|
||||
}
|
||||
MonoItem::Static(def_id) => {
|
||||
let data_id = crate::constant::codegen_static(tcx, module, def_id);
|
||||
|
|
@ -551,7 +570,10 @@ fn codegen_cgu_content(
|
|||
}
|
||||
}
|
||||
MonoItem::GlobalAsm(item_id) => {
|
||||
crate::global_asm::codegen_global_asm_item(tcx, &mut cx.global_asm, item_id);
|
||||
rustc_codegen_ssa::base::codegen_global_asm(
|
||||
&mut GlobalAsmContext { tcx, global_asm: &mut cx.global_asm },
|
||||
item_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,6 +126,11 @@ pub(crate) fn codegen_and_compile_fn<'tcx>(
|
|||
module: &mut dyn Module,
|
||||
instance: Instance<'tcx>,
|
||||
) {
|
||||
if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) {
|
||||
tcx.dcx()
|
||||
.span_fatal(tcx.def_span(instance.def_id()), "Naked asm is not supported in JIT mode");
|
||||
}
|
||||
|
||||
cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler(
|
||||
tcx.prof.clone(),
|
||||
)));
|
||||
|
|
@ -135,16 +140,15 @@ pub(crate) fn codegen_and_compile_fn<'tcx>(
|
|||
crate::PrintOnPanic(|| format!("{:?} {}", instance, tcx.symbol_name(instance).name));
|
||||
|
||||
let cached_func = std::mem::replace(&mut cached_context.func, Function::new());
|
||||
if let Some(codegened_func) = crate::base::codegen_fn(
|
||||
let codegened_func = crate::base::codegen_fn(
|
||||
tcx,
|
||||
cx,
|
||||
&mut TypeDebugContext::default(),
|
||||
cached_func,
|
||||
module,
|
||||
instance,
|
||||
) {
|
||||
crate::base::compile_fn(cx, &tcx.prof, cached_context, module, codegened_func);
|
||||
}
|
||||
);
|
||||
crate::base::compile_fn(cx, &tcx.prof, cached_context, module, codegened_func);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,102 +7,139 @@ use std::process::{Command, Stdio};
|
|||
use std::sync::Arc;
|
||||
|
||||
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
||||
use rustc_hir::{InlineAsmOperand, ItemId};
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_codegen_ssa::traits::{AsmCodegenMethods, GlobalAsmOperandRef};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_middle::ty::layout::{
|
||||
FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers,
|
||||
};
|
||||
use rustc_session::config::{OutputFilenames, OutputType};
|
||||
use rustc_target::asm::InlineAsmArch;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String, item_id: ItemId) {
|
||||
let item = tcx.hir_item(item_id);
|
||||
if let rustc_hir::ItemKind::GlobalAsm { asm, .. } = item.kind {
|
||||
let is_x86 =
|
||||
matches!(tcx.sess.asm_arch.unwrap(), InlineAsmArch::X86 | InlineAsmArch::X86_64);
|
||||
pub(crate) struct GlobalAsmContext<'a, 'tcx> {
|
||||
pub tcx: TyCtxt<'tcx>,
|
||||
pub global_asm: &'a mut String,
|
||||
}
|
||||
|
||||
if is_x86 {
|
||||
if !asm.options.contains(InlineAsmOptions::ATT_SYNTAX) {
|
||||
global_asm.push_str("\n.intel_syntax noprefix\n");
|
||||
} else {
|
||||
global_asm.push_str("\n.att_syntax\n");
|
||||
}
|
||||
impl<'tcx> AsmCodegenMethods<'tcx> for GlobalAsmContext<'_, 'tcx> {
|
||||
fn codegen_global_asm(
|
||||
&mut self,
|
||||
template: &[InlineAsmTemplatePiece],
|
||||
operands: &[GlobalAsmOperandRef<'tcx>],
|
||||
options: InlineAsmOptions,
|
||||
_line_spans: &[Span],
|
||||
) {
|
||||
codegen_global_asm_inner(self.tcx, self.global_asm, template, operands, options);
|
||||
}
|
||||
|
||||
fn mangled_name(&self, instance: Instance<'tcx>) -> String {
|
||||
let symbol_name = self.tcx.symbol_name(instance).name.to_owned();
|
||||
if self.tcx.sess.target.is_like_darwin { format!("_{symbol_name}") } else { symbol_name }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LayoutOfHelpers<'tcx> for GlobalAsmContext<'_, 'tcx> {
|
||||
#[inline]
|
||||
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
|
||||
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
|
||||
self.tcx.sess.dcx().span_fatal(span, err.to_string())
|
||||
} else {
|
||||
self.tcx
|
||||
.sess
|
||||
.dcx()
|
||||
.span_fatal(span, format!("failed to get layout for `{}`: {}", ty, err))
|
||||
}
|
||||
for piece in asm.template {
|
||||
match *piece {
|
||||
InlineAsmTemplatePiece::String(ref s) => global_asm.push_str(s),
|
||||
InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: op_sp } => {
|
||||
match asm.operands[operand_idx].0 {
|
||||
InlineAsmOperand::Const { ref anon_const } => {
|
||||
match tcx.const_eval_poly(anon_const.def_id.to_def_id()) {
|
||||
Ok(const_value) => {
|
||||
let ty = tcx
|
||||
.typeck_body(anon_const.body)
|
||||
.node_type(anon_const.hir_id);
|
||||
let string = rustc_codegen_ssa::common::asm_const_to_str(
|
||||
tcx,
|
||||
op_sp,
|
||||
const_value,
|
||||
FullyMonomorphizedLayoutCx(tcx).layout_of(ty),
|
||||
);
|
||||
global_asm.push_str(&string);
|
||||
}
|
||||
Err(ErrorHandled::Reported { .. }) => {
|
||||
// An error has already been reported and compilation is
|
||||
// guaranteed to fail if execution hits this path.
|
||||
}
|
||||
Err(ErrorHandled::TooGeneric(_)) => {
|
||||
span_bug!(op_sp, "asm const cannot be resolved; too generic");
|
||||
}
|
||||
}
|
||||
}
|
||||
InlineAsmOperand::SymFn { expr } => {
|
||||
if cfg!(not(feature = "inline_asm_sym")) {
|
||||
tcx.dcx().span_err(
|
||||
item.span,
|
||||
"asm! and global_asm! sym operands are not yet supported",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ty = tcx.typeck(item_id.owner_id).expr_ty(expr);
|
||||
let instance = match ty.kind() {
|
||||
&ty::FnDef(def_id, args) => Instance::new(def_id, args),
|
||||
_ => span_bug!(op_sp, "asm sym is not a function"),
|
||||
};
|
||||
let symbol = tcx.symbol_name(instance);
|
||||
// FIXME handle the case where the function was made private to the
|
||||
// current codegen unit
|
||||
global_asm.push_str(symbol.name);
|
||||
}
|
||||
InlineAsmOperand::SymStatic { path: _, def_id } => {
|
||||
if cfg!(not(feature = "inline_asm_sym")) {
|
||||
tcx.dcx().span_err(
|
||||
item.span,
|
||||
"asm! and global_asm! sym operands are not yet supported",
|
||||
);
|
||||
}
|
||||
impl<'tcx> FnAbiOfHelpers<'tcx> for GlobalAsmContext<'_, 'tcx> {
|
||||
#[inline]
|
||||
fn handle_fn_abi_err(
|
||||
&self,
|
||||
err: FnAbiError<'tcx>,
|
||||
span: Span,
|
||||
fn_abi_request: FnAbiRequest<'tcx>,
|
||||
) -> ! {
|
||||
FullyMonomorphizedLayoutCx(self.tcx).handle_fn_abi_err(err, span, fn_abi_request)
|
||||
}
|
||||
}
|
||||
|
||||
let instance = Instance::mono(tcx, def_id);
|
||||
let symbol = tcx.symbol_name(instance);
|
||||
global_asm.push_str(symbol.name);
|
||||
impl<'tcx> HasTyCtxt<'tcx> for GlobalAsmContext<'_, 'tcx> {
|
||||
fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> rustc_abi::HasDataLayout for GlobalAsmContext<'_, 'tcx> {
|
||||
fn data_layout(&self) -> &rustc_abi::TargetDataLayout {
|
||||
&self.tcx.data_layout
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> HasTypingEnv<'tcx> for GlobalAsmContext<'_, 'tcx> {
|
||||
fn typing_env(&self) -> ty::TypingEnv<'tcx> {
|
||||
ty::TypingEnv::fully_monomorphized()
|
||||
}
|
||||
}
|
||||
|
||||
fn codegen_global_asm_inner<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
global_asm: &mut String,
|
||||
template: &[InlineAsmTemplatePiece],
|
||||
operands: &[GlobalAsmOperandRef<'tcx>],
|
||||
options: InlineAsmOptions,
|
||||
) {
|
||||
let is_x86 = matches!(tcx.sess.asm_arch.unwrap(), InlineAsmArch::X86 | InlineAsmArch::X86_64);
|
||||
|
||||
if is_x86 {
|
||||
if !options.contains(InlineAsmOptions::ATT_SYNTAX) {
|
||||
global_asm.push_str("\n.intel_syntax noprefix\n");
|
||||
} else {
|
||||
global_asm.push_str("\n.att_syntax\n");
|
||||
}
|
||||
}
|
||||
for piece in template {
|
||||
match *piece {
|
||||
InlineAsmTemplatePiece::String(ref s) => global_asm.push_str(s),
|
||||
InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span } => {
|
||||
match operands[operand_idx] {
|
||||
GlobalAsmOperandRef::Const { ref string } => {
|
||||
global_asm.push_str(string);
|
||||
}
|
||||
GlobalAsmOperandRef::SymFn { instance } => {
|
||||
if cfg!(not(feature = "inline_asm_sym")) {
|
||||
tcx.dcx().span_err(
|
||||
span,
|
||||
"asm! and global_asm! sym operands are not yet supported",
|
||||
);
|
||||
}
|
||||
InlineAsmOperand::In { .. }
|
||||
| InlineAsmOperand::Out { .. }
|
||||
| InlineAsmOperand::InOut { .. }
|
||||
| InlineAsmOperand::SplitInOut { .. }
|
||||
| InlineAsmOperand::Label { .. } => {
|
||||
span_bug!(op_sp, "invalid operand type for global_asm!")
|
||||
|
||||
let symbol = tcx.symbol_name(instance);
|
||||
// FIXME handle the case where the function was made private to the
|
||||
// current codegen unit
|
||||
global_asm.push_str(symbol.name);
|
||||
}
|
||||
GlobalAsmOperandRef::SymStatic { def_id } => {
|
||||
if cfg!(not(feature = "inline_asm_sym")) {
|
||||
tcx.dcx().span_err(
|
||||
span,
|
||||
"asm! and global_asm! sym operands are not yet supported",
|
||||
);
|
||||
}
|
||||
|
||||
let instance = Instance::mono(tcx, def_id);
|
||||
let symbol = tcx.symbol_name(instance);
|
||||
global_asm.push_str(symbol.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
global_asm.push('\n');
|
||||
if is_x86 {
|
||||
global_asm.push_str(".att_syntax\n\n");
|
||||
}
|
||||
} else {
|
||||
bug!("Expected GlobalAsm found {:?}", item);
|
||||
global_asm.push('\n');
|
||||
if is_x86 {
|
||||
global_asm.push_str(".att_syntax\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -161,7 +161,6 @@ pub(crate) fn codegen_inline_asm_inner<'tcx>(
|
|||
stack_slots_input: Vec::new(),
|
||||
stack_slots_output: Vec::new(),
|
||||
stack_slot_size: Size::from_bytes(0),
|
||||
is_naked: false,
|
||||
};
|
||||
asm_gen.allocate_registers();
|
||||
asm_gen.allocate_stack_slots();
|
||||
|
|
@ -201,114 +200,6 @@ pub(crate) fn codegen_inline_asm_inner<'tcx>(
|
|||
call_inline_asm(fx, &asm_name, asm_gen.stack_slot_size, inputs, outputs);
|
||||
}
|
||||
|
||||
pub(crate) fn codegen_naked_asm<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
cx: &mut crate::CodegenCx,
|
||||
module: &mut dyn Module,
|
||||
instance: Instance<'tcx>,
|
||||
span: Span,
|
||||
symbol_name: &str,
|
||||
template: &[InlineAsmTemplatePiece],
|
||||
operands: &[InlineAsmOperand<'tcx>],
|
||||
options: InlineAsmOptions,
|
||||
) {
|
||||
// FIXME add .eh_frame unwind info directives
|
||||
|
||||
let operands = operands
|
||||
.iter()
|
||||
.map(|operand| match *operand {
|
||||
InlineAsmOperand::In { .. }
|
||||
| InlineAsmOperand::Out { .. }
|
||||
| InlineAsmOperand::InOut { .. } => {
|
||||
span_bug!(span, "invalid operand type for naked asm")
|
||||
}
|
||||
InlineAsmOperand::Const { ref value } => {
|
||||
let cv = instance.instantiate_mir_and_normalize_erasing_regions(
|
||||
tcx,
|
||||
ty::TypingEnv::fully_monomorphized(),
|
||||
ty::EarlyBinder::bind(value.const_),
|
||||
);
|
||||
let const_value = cv
|
||||
.eval(tcx, ty::TypingEnv::fully_monomorphized(), value.span)
|
||||
.expect("erroneous constant missed by mono item collection");
|
||||
|
||||
let value = rustc_codegen_ssa::common::asm_const_to_str(
|
||||
tcx,
|
||||
span,
|
||||
const_value,
|
||||
FullyMonomorphizedLayoutCx(tcx).layout_of(cv.ty()),
|
||||
);
|
||||
CInlineAsmOperand::Const { value }
|
||||
}
|
||||
InlineAsmOperand::SymFn { ref value } => {
|
||||
if cfg!(not(feature = "inline_asm_sym")) {
|
||||
tcx.dcx()
|
||||
.span_err(span, "asm! and global_asm! sym operands are not yet supported");
|
||||
}
|
||||
|
||||
let const_ = instance.instantiate_mir_and_normalize_erasing_regions(
|
||||
tcx,
|
||||
ty::TypingEnv::fully_monomorphized(),
|
||||
ty::EarlyBinder::bind(value.const_),
|
||||
);
|
||||
if let ty::FnDef(def_id, args) = *const_.ty().kind() {
|
||||
let instance = ty::Instance::resolve_for_fn_ptr(
|
||||
tcx,
|
||||
ty::TypingEnv::fully_monomorphized(),
|
||||
def_id,
|
||||
args,
|
||||
)
|
||||
.unwrap();
|
||||
let symbol = tcx.symbol_name(instance);
|
||||
|
||||
// Pass a wrapper rather than the function itself as the function itself may not
|
||||
// be exported from the main codegen unit and may thus be unreachable from the
|
||||
// object file created by an external assembler.
|
||||
let wrapper_name = format!(
|
||||
"__inline_asm_{}_wrapper_n{}",
|
||||
cx.cgu_name.as_str().replace('.', "__").replace('-', "_"),
|
||||
cx.inline_asm_index
|
||||
);
|
||||
cx.inline_asm_index += 1;
|
||||
let sig =
|
||||
get_function_sig(tcx, module.target_config().default_call_conv, instance);
|
||||
create_wrapper_function(module, sig, &wrapper_name, symbol.name);
|
||||
|
||||
CInlineAsmOperand::Symbol { symbol: wrapper_name }
|
||||
} else {
|
||||
span_bug!(span, "invalid type for asm sym (fn)");
|
||||
}
|
||||
}
|
||||
InlineAsmOperand::SymStatic { def_id } => {
|
||||
assert!(tcx.is_static(def_id));
|
||||
let instance = Instance::mono(tcx, def_id);
|
||||
CInlineAsmOperand::Symbol { symbol: tcx.symbol_name(instance).name.to_owned() }
|
||||
}
|
||||
InlineAsmOperand::Label { .. } => {
|
||||
span_bug!(span, "asm! label operands are not yet supported");
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let asm_gen = InlineAssemblyGenerator {
|
||||
tcx,
|
||||
arch: tcx.sess.asm_arch.unwrap(),
|
||||
enclosing_def_id: instance.def_id(),
|
||||
template,
|
||||
operands: &operands,
|
||||
options,
|
||||
registers: Vec::new(),
|
||||
stack_slots_clobber: Vec::new(),
|
||||
stack_slots_input: Vec::new(),
|
||||
stack_slots_output: Vec::new(),
|
||||
stack_slot_size: Size::from_bytes(0),
|
||||
is_naked: true,
|
||||
};
|
||||
|
||||
let generated_asm = asm_gen.generate_asm_wrapper(symbol_name);
|
||||
cx.global_asm.push_str(&generated_asm);
|
||||
}
|
||||
|
||||
struct InlineAssemblyGenerator<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
arch: InlineAsmArch,
|
||||
|
|
@ -321,13 +212,10 @@ struct InlineAssemblyGenerator<'a, 'tcx> {
|
|||
stack_slots_input: Vec<Option<Size>>,
|
||||
stack_slots_output: Vec<Option<Size>>,
|
||||
stack_slot_size: Size,
|
||||
is_naked: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
|
||||
fn allocate_registers(&mut self) {
|
||||
assert!(!self.is_naked);
|
||||
|
||||
let sess = self.tcx.sess;
|
||||
let map = allocatable_registers(
|
||||
self.arch,
|
||||
|
|
@ -451,8 +339,6 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
|
|||
}
|
||||
|
||||
fn allocate_stack_slots(&mut self) {
|
||||
assert!(!self.is_naked);
|
||||
|
||||
let mut slot_size = Size::from_bytes(0);
|
||||
let mut slots_clobber = vec![None; self.operands.len()];
|
||||
let mut slots_input = vec![None; self.operands.len()];
|
||||
|
|
@ -582,32 +468,31 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
|
|||
if is_x86 {
|
||||
generated_asm.push_str(".intel_syntax noprefix\n");
|
||||
}
|
||||
if !self.is_naked {
|
||||
Self::prologue(&mut generated_asm, self.arch);
|
||||
|
||||
// Save clobbered registers
|
||||
if !self.options.contains(InlineAsmOptions::NORETURN) {
|
||||
for (reg, slot) in self
|
||||
.registers
|
||||
.iter()
|
||||
.zip(self.stack_slots_clobber.iter().copied())
|
||||
.filter_map(|(r, s)| r.zip(s))
|
||||
{
|
||||
Self::save_register(&mut generated_asm, self.arch, reg, slot);
|
||||
}
|
||||
}
|
||||
Self::prologue(&mut generated_asm, self.arch);
|
||||
|
||||
// Write input registers
|
||||
// Save clobbered registers
|
||||
if !self.options.contains(InlineAsmOptions::NORETURN) {
|
||||
for (reg, slot) in self
|
||||
.registers
|
||||
.iter()
|
||||
.zip(self.stack_slots_input.iter().copied())
|
||||
.zip(self.stack_slots_clobber.iter().copied())
|
||||
.filter_map(|(r, s)| r.zip(s))
|
||||
{
|
||||
Self::restore_register(&mut generated_asm, self.arch, reg, slot);
|
||||
Self::save_register(&mut generated_asm, self.arch, reg, slot);
|
||||
}
|
||||
}
|
||||
|
||||
// Write input registers
|
||||
for (reg, slot) in self
|
||||
.registers
|
||||
.iter()
|
||||
.zip(self.stack_slots_input.iter().copied())
|
||||
.filter_map(|(r, s)| r.zip(s))
|
||||
{
|
||||
Self::restore_register(&mut generated_asm, self.arch, reg, slot);
|
||||
}
|
||||
|
||||
if is_x86 && self.options.contains(InlineAsmOptions::ATT_SYNTAX) {
|
||||
generated_asm.push_str(".att_syntax\n");
|
||||
}
|
||||
|
|
@ -701,32 +586,30 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
|
|||
generated_asm.push_str(".intel_syntax noprefix\n");
|
||||
}
|
||||
|
||||
if !self.is_naked {
|
||||
if !self.options.contains(InlineAsmOptions::NORETURN) {
|
||||
// Read output registers
|
||||
for (reg, slot) in self
|
||||
.registers
|
||||
.iter()
|
||||
.zip(self.stack_slots_output.iter().copied())
|
||||
.filter_map(|(r, s)| r.zip(s))
|
||||
{
|
||||
Self::save_register(&mut generated_asm, self.arch, reg, slot);
|
||||
}
|
||||
|
||||
// Restore clobbered registers
|
||||
for (reg, slot) in self
|
||||
.registers
|
||||
.iter()
|
||||
.zip(self.stack_slots_clobber.iter().copied())
|
||||
.filter_map(|(r, s)| r.zip(s))
|
||||
{
|
||||
Self::restore_register(&mut generated_asm, self.arch, reg, slot);
|
||||
}
|
||||
|
||||
Self::epilogue(&mut generated_asm, self.arch);
|
||||
} else {
|
||||
Self::epilogue_noreturn(&mut generated_asm, self.arch);
|
||||
if !self.options.contains(InlineAsmOptions::NORETURN) {
|
||||
// Read output registers
|
||||
for (reg, slot) in self
|
||||
.registers
|
||||
.iter()
|
||||
.zip(self.stack_slots_output.iter().copied())
|
||||
.filter_map(|(r, s)| r.zip(s))
|
||||
{
|
||||
Self::save_register(&mut generated_asm, self.arch, reg, slot);
|
||||
}
|
||||
|
||||
// Restore clobbered registers
|
||||
for (reg, slot) in self
|
||||
.registers
|
||||
.iter()
|
||||
.zip(self.stack_slots_clobber.iter().copied())
|
||||
.filter_map(|(r, s)| r.zip(s))
|
||||
{
|
||||
Self::restore_register(&mut generated_asm, self.arch, reg, slot);
|
||||
}
|
||||
|
||||
Self::epilogue(&mut generated_asm, self.arch);
|
||||
} else {
|
||||
Self::epilogue_noreturn(&mut generated_asm, self.arch);
|
||||
}
|
||||
|
||||
if is_x86 {
|
||||
|
|
|
|||
|
|
@ -829,7 +829,7 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
|
|||
|
||||
impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||
fn codegen_global_asm(
|
||||
&self,
|
||||
&mut self,
|
||||
template: &[InlineAsmTemplatePiece],
|
||||
operands: &[GlobalAsmOperandRef<'tcx>],
|
||||
options: InlineAsmOptions,
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ pub fn compile_codegen_unit(
|
|||
let f128_type_supported = target_info.supports_target_dependent_type(CType::Float128);
|
||||
let u128_type_supported = target_info.supports_target_dependent_type(CType::UInt128t);
|
||||
// TODO: improve this to avoid passing that many arguments.
|
||||
let cx = CodegenCx::new(
|
||||
let mut cx = CodegenCx::new(
|
||||
&context,
|
||||
cgu,
|
||||
tcx,
|
||||
|
|
@ -223,8 +223,8 @@ pub fn compile_codegen_unit(
|
|||
}
|
||||
|
||||
// ... and now that we have everything pre-defined, fill out those definitions.
|
||||
for &(mono_item, _) in &mono_items {
|
||||
mono_item.define::<Builder<'_, '_, '_>>(&cx);
|
||||
for &(mono_item, item_data) in &mono_items {
|
||||
mono_item.define::<Builder<'_, '_, '_>>(&mut cx, item_data);
|
||||
}
|
||||
|
||||
// If this codegen unit contains the main function, also create the
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ enum ExtremumOperation {
|
|||
Min,
|
||||
}
|
||||
|
||||
pub struct Builder<'a: 'gcc, 'gcc, 'tcx> {
|
||||
pub struct Builder<'a, 'gcc, 'tcx> {
|
||||
pub cx: &'a CodegenCx<'gcc, 'tcx>,
|
||||
pub block: Block<'gcc>,
|
||||
pub location: Option<Location<'gcc>>,
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
|||
|
||||
impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
|
||||
fn codegen_global_asm(
|
||||
&self,
|
||||
&mut self,
|
||||
template: &[InlineAsmTemplatePiece],
|
||||
operands: &[GlobalAsmOperandRef<'tcx>],
|
||||
options: InlineAsmOptions,
|
||||
|
|
|
|||
|
|
@ -83,15 +83,15 @@ pub(crate) fn compile_codegen_unit(
|
|||
// Instantiate monomorphizations without filling out definitions yet...
|
||||
let llvm_module = ModuleLlvm::new(tcx, cgu_name.as_str());
|
||||
{
|
||||
let cx = CodegenCx::new(tcx, cgu, &llvm_module);
|
||||
let mut cx = CodegenCx::new(tcx, cgu, &llvm_module);
|
||||
let mono_items = cx.codegen_unit.items_in_deterministic_order(cx.tcx);
|
||||
for &(mono_item, data) in &mono_items {
|
||||
mono_item.predefine::<Builder<'_, '_, '_>>(&cx, data.linkage, data.visibility);
|
||||
}
|
||||
|
||||
// ... and now that we have everything pre-defined, fill out those definitions.
|
||||
for &(mono_item, _) in &mono_items {
|
||||
mono_item.define::<Builder<'_, '_, '_>>(&cx);
|
||||
for &(mono_item, item_data) in &mono_items {
|
||||
mono_item.define::<Builder<'_, '_, '_>>(&mut cx, item_data);
|
||||
}
|
||||
|
||||
// If this codegen unit contains the main function, also create the
|
||||
|
|
|
|||
|
|
@ -466,13 +466,9 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) {
|
|||
_ => true,
|
||||
};
|
||||
|
||||
cfg.has_reliable_f16_math = match (target_arch, target_os) {
|
||||
// x86 has a crash for `powi`: <https://github.com/llvm/llvm-project/issues/105747>
|
||||
("x86" | "x86_64", _) => false,
|
||||
// Assume that working `f16` means working `f16` math for most platforms, since
|
||||
// operations just go through `f32`.
|
||||
_ => true,
|
||||
} && cfg.has_reliable_f16;
|
||||
// Assume that working `f16` means working `f16` math for most platforms, since
|
||||
// operations just go through `f32`.
|
||||
cfg.has_reliable_f16_math = cfg.has_reliable_f16;
|
||||
|
||||
cfg.has_reliable_f128_math = match (target_arch, target_os) {
|
||||
// LLVM lowers `fp128` math to `long double` symbols even on platforms where
|
||||
|
|
|
|||
|
|
@ -12,19 +12,21 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
|
|||
use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
|
||||
use rustc_data_structures::sync::{IntoDynSyncSend, par_map};
|
||||
use rustc_data_structures::unord::UnordMap;
|
||||
use rustc_hir::ItemId;
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_metadata::EncodedMetadata;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
|
||||
use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType};
|
||||
use rustc_middle::middle::exported_symbols::SymbolExportKind;
|
||||
use rustc_middle::middle::{exported_symbols, lang_items};
|
||||
use rustc_middle::mir::BinOp;
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem, MonoItemPartitions};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::{self, CrateType, EntryFnType, OutputType};
|
||||
use rustc_span::{DUMMY_SP, Symbol, sym};
|
||||
|
|
@ -417,6 +419,69 @@ pub(crate) fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
mir::codegen_mir::<Bx>(cx, instance);
|
||||
}
|
||||
|
||||
pub fn codegen_global_asm<'tcx, Cx>(cx: &mut Cx, item_id: ItemId)
|
||||
where
|
||||
Cx: LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>> + AsmCodegenMethods<'tcx>,
|
||||
{
|
||||
let item = cx.tcx().hir_item(item_id);
|
||||
if let rustc_hir::ItemKind::GlobalAsm { asm, .. } = item.kind {
|
||||
let operands: Vec<_> = asm
|
||||
.operands
|
||||
.iter()
|
||||
.map(|(op, op_sp)| match *op {
|
||||
rustc_hir::InlineAsmOperand::Const { ref anon_const } => {
|
||||
match cx.tcx().const_eval_poly(anon_const.def_id.to_def_id()) {
|
||||
Ok(const_value) => {
|
||||
let ty =
|
||||
cx.tcx().typeck_body(anon_const.body).node_type(anon_const.hir_id);
|
||||
let string = common::asm_const_to_str(
|
||||
cx.tcx(),
|
||||
*op_sp,
|
||||
const_value,
|
||||
cx.layout_of(ty),
|
||||
);
|
||||
GlobalAsmOperandRef::Const { string }
|
||||
}
|
||||
Err(ErrorHandled::Reported { .. }) => {
|
||||
// An error has already been reported and
|
||||
// compilation is guaranteed to fail if execution
|
||||
// hits this path. So an empty string instead of
|
||||
// a stringified constant value will suffice.
|
||||
GlobalAsmOperandRef::Const { string: String::new() }
|
||||
}
|
||||
Err(ErrorHandled::TooGeneric(_)) => {
|
||||
span_bug!(*op_sp, "asm const cannot be resolved; too generic")
|
||||
}
|
||||
}
|
||||
}
|
||||
rustc_hir::InlineAsmOperand::SymFn { expr } => {
|
||||
let ty = cx.tcx().typeck(item_id.owner_id).expr_ty(expr);
|
||||
let instance = match ty.kind() {
|
||||
&ty::FnDef(def_id, args) => Instance::new(def_id, args),
|
||||
_ => span_bug!(*op_sp, "asm sym is not a function"),
|
||||
};
|
||||
|
||||
GlobalAsmOperandRef::SymFn { instance }
|
||||
}
|
||||
rustc_hir::InlineAsmOperand::SymStatic { path: _, def_id } => {
|
||||
GlobalAsmOperandRef::SymStatic { def_id }
|
||||
}
|
||||
rustc_hir::InlineAsmOperand::In { .. }
|
||||
| rustc_hir::InlineAsmOperand::Out { .. }
|
||||
| rustc_hir::InlineAsmOperand::InOut { .. }
|
||||
| rustc_hir::InlineAsmOperand::SplitInOut { .. }
|
||||
| rustc_hir::InlineAsmOperand::Label { .. } => {
|
||||
span_bug!(*op_sp, "invalid operand type for global_asm!")
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
cx.codegen_global_asm(asm.template, &operands, asm.options, asm.line_spans);
|
||||
} else {
|
||||
span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type")
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the `main` function which will initialize the rust runtime and call
|
||||
/// users main function.
|
||||
pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
let mut link_ordinal_span = None;
|
||||
let mut no_sanitize_span = None;
|
||||
let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default();
|
||||
let mut no_mangle_span = None;
|
||||
|
||||
for attr in attrs.iter() {
|
||||
// In some cases, attribute are only valid on functions, but it's the `check_attr`
|
||||
|
|
@ -139,6 +140,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
|
||||
sym::no_mangle => {
|
||||
no_mangle_span = Some(attr.span());
|
||||
if tcx.opt_item_name(did.to_def_id()).is_some() {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
|
||||
mixed_export_name_no_mangle_lint_state.track_no_mangle(
|
||||
|
|
@ -621,6 +623,34 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
|
||||
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
|
||||
&& codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
|
||||
{
|
||||
let lang_item =
|
||||
lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name));
|
||||
let mut err = tcx
|
||||
.dcx()
|
||||
.struct_span_err(
|
||||
no_mangle_span.unwrap_or_default(),
|
||||
"`#[no_mangle]` cannot be used on internal language items",
|
||||
)
|
||||
.with_note("Rustc requires this item to have a specific mangled name.")
|
||||
.with_span_label(tcx.def_span(did), "should be the internal language item");
|
||||
if let Some(lang_item) = lang_item {
|
||||
if let Some(link_name) = lang_item.link_name() {
|
||||
err = err
|
||||
.with_note("If you are trying to prevent mangling to ease debugging, many")
|
||||
.with_note(format!(
|
||||
"debuggers support a command such as `rbreak {link_name}` to"
|
||||
))
|
||||
.with_note(format!(
|
||||
"match `.*{link_name}.*` instead of `break {link_name}` on a specific name"
|
||||
))
|
||||
}
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
|
||||
// Any linkage to LLVM intrinsics for now forcibly marks them all as never
|
||||
// unwinds since LLVM sometimes can't handle codegen which `invoke`s
|
||||
// intrinsic functions.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#![feature(string_from_utf8_lossy_owned)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(try_blocks)]
|
||||
#![recursion_limit = "256"]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
//! This crate contains codegen code that is used by all codegen backends (LLVM and others).
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ mod coverageinfo;
|
|||
pub mod debuginfo;
|
||||
mod intrinsic;
|
||||
mod locals;
|
||||
mod naked_asm;
|
||||
pub mod naked_asm;
|
||||
pub mod operand;
|
||||
pub mod place;
|
||||
mod rvalue;
|
||||
|
|
@ -178,11 +178,6 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
|
||||
debug!("fn_abi: {:?}", fn_abi);
|
||||
|
||||
if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) {
|
||||
crate::mir::naked_asm::codegen_naked_asm::<Bx>(cx, &mir, instance);
|
||||
return;
|
||||
}
|
||||
|
||||
if tcx.features().ergonomic_clones() {
|
||||
let monomorphized_mir = instance.instantiate_mir_and_normalize_erasing_regions(
|
||||
tcx,
|
||||
|
|
|
|||
|
|
@ -1,23 +1,33 @@
|
|||
use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind};
|
||||
use rustc_attr_parsing::InstructionSetAttr;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility};
|
||||
use rustc_middle::mir::{Body, InlineAsmOperand};
|
||||
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf};
|
||||
use rustc_middle::ty::{Instance, Ty, TyCtxt};
|
||||
use rustc_middle::mir::mono::{Linkage, MonoItemData, Visibility};
|
||||
use rustc_middle::mir::{InlineAsmOperand, START_BLOCK};
|
||||
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{Instance, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::{bug, span_bug, ty};
|
||||
use rustc_span::sym;
|
||||
use rustc_target::callconv::{ArgAbi, FnAbi, PassMode};
|
||||
use rustc_target::spec::{BinaryFormat, WasmCAbi};
|
||||
|
||||
use crate::common;
|
||||
use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods};
|
||||
use crate::mir::AsmCodegenMethods;
|
||||
use crate::traits::GlobalAsmOperandRef;
|
||||
|
||||
pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
cx: &'a Bx::CodegenCx,
|
||||
mir: &Body<'tcx>,
|
||||
pub fn codegen_naked_asm<
|
||||
'a,
|
||||
'tcx,
|
||||
Cx: LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>>
|
||||
+ FnAbiOf<'tcx, FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>>
|
||||
+ AsmCodegenMethods<'tcx>,
|
||||
>(
|
||||
cx: &'a mut Cx,
|
||||
instance: Instance<'tcx>,
|
||||
item_data: MonoItemData,
|
||||
) {
|
||||
assert!(!instance.args.has_infer());
|
||||
let mir = cx.tcx().instance_mir(instance.def);
|
||||
|
||||
let rustc_middle::mir::TerminatorKind::InlineAsm {
|
||||
asm_macro: _,
|
||||
template,
|
||||
|
|
@ -26,15 +36,14 @@ pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
line_spans,
|
||||
targets: _,
|
||||
unwind: _,
|
||||
} = mir.basic_blocks.iter().next().unwrap().terminator().kind
|
||||
} = mir.basic_blocks[START_BLOCK].terminator().kind
|
||||
else {
|
||||
bug!("#[naked] functions should always terminate with an asm! block")
|
||||
};
|
||||
|
||||
let operands: Vec<_> =
|
||||
operands.iter().map(|op| inline_to_global_operand::<Bx>(cx, instance, op)).collect();
|
||||
operands.iter().map(|op| inline_to_global_operand::<Cx>(cx, instance, op)).collect();
|
||||
|
||||
let item_data = cx.codegen_unit().items().get(&MonoItem::Fn(instance)).unwrap();
|
||||
let name = cx.mangled_name(instance);
|
||||
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
|
||||
let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data, fn_abi);
|
||||
|
|
@ -47,8 +56,8 @@ pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||
cx.codegen_global_asm(&template_vec, &operands, options, line_spans);
|
||||
}
|
||||
|
||||
fn inline_to_global_operand<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
cx: &'a Bx::CodegenCx,
|
||||
fn inline_to_global_operand<'a, 'tcx, Cx: LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>>>(
|
||||
cx: &'a Cx,
|
||||
instance: Instance<'tcx>,
|
||||
op: &InlineAsmOperand<'tcx>,
|
||||
) -> GlobalAsmOperandRef<'tcx> {
|
||||
|
|
@ -108,7 +117,7 @@ fn prefix_and_suffix<'tcx>(
|
|||
tcx: TyCtxt<'tcx>,
|
||||
instance: Instance<'tcx>,
|
||||
asm_name: &str,
|
||||
item_data: &MonoItemData,
|
||||
item_data: MonoItemData,
|
||||
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
) -> (String, String) {
|
||||
use std::fmt::Write;
|
||||
|
|
@ -210,8 +219,10 @@ fn prefix_and_suffix<'tcx>(
|
|||
writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap();
|
||||
writeln!(begin, ".balign {align_bytes}").unwrap();
|
||||
write_linkage(&mut begin).unwrap();
|
||||
if let Visibility::Hidden = item_data.visibility {
|
||||
writeln!(begin, ".hidden {asm_name}").unwrap();
|
||||
match item_data.visibility {
|
||||
Visibility::Default => {}
|
||||
Visibility::Protected => writeln!(begin, ".protected {asm_name}").unwrap(),
|
||||
Visibility::Hidden => writeln!(begin, ".hidden {asm_name}").unwrap(),
|
||||
}
|
||||
writeln!(begin, ".type {asm_name}, {function}").unwrap();
|
||||
if !arch_prefix.is_empty() {
|
||||
|
|
@ -231,8 +242,9 @@ fn prefix_and_suffix<'tcx>(
|
|||
writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap();
|
||||
writeln!(begin, ".balign {align_bytes}").unwrap();
|
||||
write_linkage(&mut begin).unwrap();
|
||||
if let Visibility::Hidden = item_data.visibility {
|
||||
writeln!(begin, ".private_extern {asm_name}").unwrap();
|
||||
match item_data.visibility {
|
||||
Visibility::Default | Visibility::Protected => {}
|
||||
Visibility::Hidden => writeln!(begin, ".private_extern {asm_name}").unwrap(),
|
||||
}
|
||||
writeln!(begin, "{asm_name}:").unwrap();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
use rustc_hir as hir;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility};
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
||||
use rustc_middle::{span_bug, ty};
|
||||
use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility};
|
||||
use rustc_middle::ty::layout::HasTyCtxt;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::base;
|
||||
use crate::mir::naked_asm;
|
||||
use crate::traits::*;
|
||||
use crate::{base, common};
|
||||
|
||||
pub trait MonoItemExt<'a, 'tcx> {
|
||||
fn define<Bx: BuilderMethods<'a, 'tcx>>(&self, cx: &'a Bx::CodegenCx);
|
||||
fn define<Bx: BuilderMethods<'a, 'tcx>>(
|
||||
&self,
|
||||
cx: &'a mut Bx::CodegenCx,
|
||||
item_data: MonoItemData,
|
||||
);
|
||||
fn predefine<Bx: BuilderMethods<'a, 'tcx>>(
|
||||
&self,
|
||||
cx: &'a Bx::CodegenCx,
|
||||
|
|
@ -22,7 +23,11 @@ pub trait MonoItemExt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
|
||||
fn define<Bx: BuilderMethods<'a, 'tcx>>(&self, cx: &'a Bx::CodegenCx) {
|
||||
fn define<Bx: BuilderMethods<'a, 'tcx>>(
|
||||
&self,
|
||||
cx: &'a mut Bx::CodegenCx,
|
||||
item_data: MonoItemData,
|
||||
) {
|
||||
debug!(
|
||||
"BEGIN IMPLEMENTING '{} ({})' in cgu {}",
|
||||
self,
|
||||
|
|
@ -35,71 +40,19 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
|
|||
cx.codegen_static(def_id);
|
||||
}
|
||||
MonoItem::GlobalAsm(item_id) => {
|
||||
let item = cx.tcx().hir_item(item_id);
|
||||
if let hir::ItemKind::GlobalAsm { asm, .. } = item.kind {
|
||||
let operands: Vec<_> = asm
|
||||
.operands
|
||||
.iter()
|
||||
.map(|(op, op_sp)| match *op {
|
||||
hir::InlineAsmOperand::Const { ref anon_const } => {
|
||||
match cx.tcx().const_eval_poly(anon_const.def_id.to_def_id()) {
|
||||
Ok(const_value) => {
|
||||
let ty = cx
|
||||
.tcx()
|
||||
.typeck_body(anon_const.body)
|
||||
.node_type(anon_const.hir_id);
|
||||
let string = common::asm_const_to_str(
|
||||
cx.tcx(),
|
||||
*op_sp,
|
||||
const_value,
|
||||
cx.layout_of(ty),
|
||||
);
|
||||
GlobalAsmOperandRef::Const { string }
|
||||
}
|
||||
Err(ErrorHandled::Reported { .. }) => {
|
||||
// An error has already been reported and
|
||||
// compilation is guaranteed to fail if execution
|
||||
// hits this path. So an empty string instead of
|
||||
// a stringified constant value will suffice.
|
||||
GlobalAsmOperandRef::Const { string: String::new() }
|
||||
}
|
||||
Err(ErrorHandled::TooGeneric(_)) => {
|
||||
span_bug!(
|
||||
*op_sp,
|
||||
"asm const cannot be resolved; too generic"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::InlineAsmOperand::SymFn { expr } => {
|
||||
let ty = cx.tcx().typeck(item_id.owner_id).expr_ty(expr);
|
||||
let instance = match ty.kind() {
|
||||
&ty::FnDef(def_id, args) => Instance::new(def_id, args),
|
||||
_ => span_bug!(*op_sp, "asm sym is not a function"),
|
||||
};
|
||||
|
||||
GlobalAsmOperandRef::SymFn { instance }
|
||||
}
|
||||
hir::InlineAsmOperand::SymStatic { path: _, def_id } => {
|
||||
GlobalAsmOperandRef::SymStatic { def_id }
|
||||
}
|
||||
hir::InlineAsmOperand::In { .. }
|
||||
| hir::InlineAsmOperand::Out { .. }
|
||||
| hir::InlineAsmOperand::InOut { .. }
|
||||
| hir::InlineAsmOperand::SplitInOut { .. }
|
||||
| hir::InlineAsmOperand::Label { .. } => {
|
||||
span_bug!(*op_sp, "invalid operand type for global_asm!")
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
cx.codegen_global_asm(asm.template, &operands, asm.options, asm.line_spans);
|
||||
} else {
|
||||
span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type")
|
||||
}
|
||||
base::codegen_global_asm(cx, item_id);
|
||||
}
|
||||
MonoItem::Fn(instance) => {
|
||||
base::codegen_instance::<Bx>(cx, instance);
|
||||
if cx
|
||||
.tcx()
|
||||
.codegen_fn_attrs(instance.def_id())
|
||||
.flags
|
||||
.contains(CodegenFnAttrFlags::NAKED)
|
||||
{
|
||||
naked_asm::codegen_naked_asm::<Bx::CodegenCx>(cx, instance, item_data);
|
||||
} else {
|
||||
base::codegen_instance::<Bx>(cx, instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ pub trait AsmBuilderMethods<'tcx>: BackendTypes {
|
|||
|
||||
pub trait AsmCodegenMethods<'tcx> {
|
||||
fn codegen_global_asm(
|
||||
&self,
|
||||
&mut self,
|
||||
template: &[InlineAsmTemplatePiece],
|
||||
operands: &[GlobalAsmOperandRef<'tcx>],
|
||||
options: InlineAsmOptions,
|
||||
|
|
|
|||
|
|
@ -45,15 +45,14 @@ pub trait CodegenBackend {
|
|||
|
||||
fn print(&self, _req: &PrintRequest, _out: &mut String, _sess: &Session) {}
|
||||
|
||||
/// Returns two feature sets:
|
||||
/// - The first has the features that should be set in `cfg(target_features)`.
|
||||
/// - The second is like the first, but also includes unstable features.
|
||||
///
|
||||
/// RUSTC_SPECIFIC_FEATURES should be skipped here, those are handled outside codegen.
|
||||
/// Collect target-specific options that should be set in `cfg(...)`, including
|
||||
/// `target_feature` and support for unstable float types.
|
||||
fn target_config(&self, _sess: &Session) -> TargetConfig {
|
||||
TargetConfig {
|
||||
target_features: vec![],
|
||||
unstable_target_features: vec![],
|
||||
// `true` is used as a default so backends need to acknowledge when they do not
|
||||
// support the float types, rather than accidentally quietly skipping all tests.
|
||||
has_reliable_f16: true,
|
||||
has_reliable_f16_math: true,
|
||||
has_reliable_f128: true,
|
||||
|
|
|
|||
|
|
@ -61,16 +61,21 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
|
|||
ensure_monomorphic_enough(tcx, tp_ty)?;
|
||||
ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128())
|
||||
}
|
||||
sym::variant_count => match tp_ty.kind() {
|
||||
sym::variant_count => match match tp_ty.kind() {
|
||||
// Pattern types have the same number of variants as their base type.
|
||||
// Even if we restrict e.g. which variants are valid, the variants are essentially just uninhabited.
|
||||
// And `Result<(), !>` still has two variants according to `variant_count`.
|
||||
ty::Pat(base, _) => *base,
|
||||
_ => tp_ty,
|
||||
}
|
||||
.kind()
|
||||
{
|
||||
// Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough.
|
||||
ty::Adt(adt, _) => ConstValue::from_target_usize(adt.variants().len() as u64, &tcx),
|
||||
ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => {
|
||||
throw_inval!(TooGeneric)
|
||||
}
|
||||
ty::Pat(_, pat) => match **pat {
|
||||
ty::PatternKind::Range { .. } => ConstValue::from_target_usize(0u64, &tcx),
|
||||
// Future pattern kinds may have more variants
|
||||
},
|
||||
ty::Pat(..) => unreachable!(),
|
||||
ty::Bound(_, _) => bug!("bound ty during ctfe"),
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
|
|
@ -178,8 +183,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
|
||||
let res = self.binary_op(op, &a, &b)?;
|
||||
// `binary_op` already called `generate_nan` if needed.
|
||||
|
||||
// FIXME: Miri should add some non-determinism to the result here to catch any dependences on exact computations. This has previously been done, but the behaviour was removed as part of constification.
|
||||
let res = M::apply_float_nondet(self, res)?;
|
||||
self.write_immediate(*res, dest)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -276,6 +276,14 @@ pub trait Machine<'tcx>: Sized {
|
|||
F2::NAN
|
||||
}
|
||||
|
||||
/// Apply non-determinism to float operations that do not return a precise result.
|
||||
fn apply_float_nondet(
|
||||
_ecx: &mut InterpCx<'tcx, Self>,
|
||||
val: ImmTy<'tcx, Self::Provenance>,
|
||||
) -> InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>> {
|
||||
interp_ok(val)
|
||||
}
|
||||
|
||||
/// Determines the result of `min`/`max` on floats when the arguments are equal.
|
||||
fn equal_float_min_max<F: Float>(_ecx: &InterpCx<'tcx, Self>, a: F, _b: F) -> F {
|
||||
// By default, we pick the left argument.
|
||||
|
|
|
|||
|
|
@ -1248,6 +1248,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
|
|||
// Range patterns are precisely reflected into `valid_range` and thus
|
||||
// handled fully by `visit_scalar` (called below).
|
||||
ty::PatternKind::Range { .. } => {},
|
||||
|
||||
// FIXME(pattern_types): check that the value is covered by one of the variants.
|
||||
// For now, we rely on layout computation setting the scalar's `valid_range` to
|
||||
// match the pattern. However, this cannot always work; the layout may
|
||||
// pessimistically cover actually illegal ranges and Miri would miss that UB.
|
||||
// The consolation here is that codegen also will miss that UB, so at least
|
||||
// we won't see optimizations actually breaking such programs.
|
||||
ty::PatternKind::Or(_patterns) => {}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ pub use self::freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard};
|
|||
pub use self::lock::{Lock, LockGuard, Mode};
|
||||
pub use self::mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode};
|
||||
pub use self::parallel::{
|
||||
join, par_for_each_in, par_map, parallel_guard, scope, try_par_for_each_in,
|
||||
join, par_for_each_in, par_map, parallel_guard, scope, spawn, try_par_for_each_in,
|
||||
};
|
||||
pub use self::vec::{AppendOnlyIndexVec, AppendOnlyVec};
|
||||
pub use self::worker_local::{Registry, WorkerLocal};
|
||||
|
|
|
|||
|
|
@ -93,6 +93,17 @@ macro_rules! parallel {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn spawn(func: impl FnOnce() + DynSend + 'static) {
|
||||
if mode::is_dyn_thread_safe() {
|
||||
let func = FromDyn::from(func);
|
||||
rayon_core::spawn(|| {
|
||||
(func.into_inner())();
|
||||
});
|
||||
} else {
|
||||
func()
|
||||
}
|
||||
}
|
||||
|
||||
// This function only works when `mode::is_dyn_thread_safe()`.
|
||||
pub fn scope<'scope, OP, R>(op: OP) -> R
|
||||
where
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::Delimiter;
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::util::literal;
|
||||
use rustc_ast::{
|
||||
self as ast, AnonConst, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp,
|
||||
attr, token,
|
||||
attr, token, tokenstream,
|
||||
};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
|
||||
|
|
@ -55,13 +57,13 @@ impl<'a> ExtCtxt<'a> {
|
|||
&self,
|
||||
span: Span,
|
||||
path: ast::Path,
|
||||
delim: ast::token::Delimiter,
|
||||
tokens: ast::tokenstream::TokenStream,
|
||||
delim: Delimiter,
|
||||
tokens: TokenStream,
|
||||
) -> P<ast::MacCall> {
|
||||
P(ast::MacCall {
|
||||
path,
|
||||
args: P(ast::DelimArgs {
|
||||
dspan: ast::tokenstream::DelimSpan { open: span, close: span },
|
||||
dspan: tokenstream::DelimSpan { open: span, close: span },
|
||||
delim,
|
||||
tokens,
|
||||
}),
|
||||
|
|
@ -480,8 +482,8 @@ impl<'a> ExtCtxt<'a> {
|
|||
span,
|
||||
[sym::std, sym::unreachable].map(|s| Ident::new(s, span)).to_vec(),
|
||||
),
|
||||
ast::token::Delimiter::Parenthesis,
|
||||
ast::tokenstream::TokenStream::default(),
|
||||
Delimiter::Parenthesis,
|
||||
TokenStream::default(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ pub(crate) fn attr_into_trace(mut attr: Attribute, trace_name: Symbol) -> Attrib
|
|||
let NormalAttr { item, tokens } = &mut **normal;
|
||||
item.path.segments[0].ident.name = trace_name;
|
||||
// This makes the trace attributes unobservable to token-based proc macros.
|
||||
*tokens = Some(LazyAttrTokenStream::new(AttrTokenStream::default()));
|
||||
*tokens = Some(LazyAttrTokenStream::new_direct(AttrTokenStream::default()));
|
||||
}
|
||||
AttrKind::DocComment(..) => unreachable!(),
|
||||
}
|
||||
|
|
@ -192,7 +192,7 @@ impl<'a> StripUnconfigured<'a> {
|
|||
if self.config_tokens {
|
||||
if let Some(Some(tokens)) = node.tokens_mut() {
|
||||
let attr_stream = tokens.to_attr_token_stream();
|
||||
*tokens = LazyAttrTokenStream::new(self.configure_tokens(&attr_stream));
|
||||
*tokens = LazyAttrTokenStream::new_direct(self.configure_tokens(&attr_stream));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -223,7 +223,7 @@ impl<'a> StripUnconfigured<'a> {
|
|||
target.attrs.flat_map_in_place(|attr| self.process_cfg_attr(&attr));
|
||||
|
||||
if self.in_cfg(&target.attrs) {
|
||||
target.tokens = LazyAttrTokenStream::new(
|
||||
target.tokens = LazyAttrTokenStream::new_direct(
|
||||
self.configure_tokens(&target.tokens.to_attr_token_stream()),
|
||||
);
|
||||
Some(AttrTokenTree::AttrsTarget(target))
|
||||
|
|
@ -361,7 +361,7 @@ impl<'a> StripUnconfigured<'a> {
|
|||
.to_attr_token_stream(),
|
||||
));
|
||||
|
||||
let tokens = Some(LazyAttrTokenStream::new(AttrTokenStream::new(trees)));
|
||||
let tokens = Some(LazyAttrTokenStream::new_direct(AttrTokenStream::new(trees)));
|
||||
let attr = ast::attr::mk_attr_from_item(
|
||||
&self.sess.psess.attr_id_generator,
|
||||
item,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use std::mem;
|
||||
|
||||
use rustc_ast::mut_visit::{self, MutVisitor};
|
||||
use rustc_ast::token::{
|
||||
self, Delimiter, IdentIsRaw, InvisibleOrigin, Lit, LitKind, MetaVarKind, Token, TokenKind,
|
||||
};
|
||||
|
|
@ -29,10 +28,8 @@ use crate::mbe::{self, KleeneOp, MetaVarExpr};
|
|||
// A Marker adds the given mark to the syntax context.
|
||||
struct Marker(LocalExpnId, Transparency, FxHashMap<SyntaxContext, SyntaxContext>);
|
||||
|
||||
impl MutVisitor for Marker {
|
||||
const VISIT_TOKENS: bool = true;
|
||||
|
||||
fn visit_span(&mut self, span: &mut Span) {
|
||||
impl Marker {
|
||||
fn mark_span(&mut self, span: &mut Span) {
|
||||
// `apply_mark` is a relatively expensive operation, both due to taking hygiene lock, and
|
||||
// by itself. All tokens in a macro body typically have the same syntactic context, unless
|
||||
// it's some advanced case with macro-generated macros. So if we cache the marked version
|
||||
|
|
@ -292,7 +289,7 @@ pub(super) fn transcribe<'a>(
|
|||
|
||||
// Emit as a token stream within `Delimiter::Invisible` to maintain
|
||||
// parsing priorities.
|
||||
marker.visit_span(&mut sp);
|
||||
marker.mark_span(&mut sp);
|
||||
with_metavar_spans(|mspans| mspans.insert(mk_span, sp));
|
||||
// Both the open delim and close delim get the same span, which covers the
|
||||
// `$foo` in the decl macro RHS.
|
||||
|
|
@ -312,13 +309,13 @@ pub(super) fn transcribe<'a>(
|
|||
maybe_use_metavar_location(psess, &stack, sp, tt, &mut marker)
|
||||
}
|
||||
MatchedSingle(ParseNtResult::Ident(ident, is_raw)) => {
|
||||
marker.visit_span(&mut sp);
|
||||
marker.mark_span(&mut sp);
|
||||
with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
|
||||
let kind = token::NtIdent(*ident, *is_raw);
|
||||
TokenTree::token_alone(kind, sp)
|
||||
}
|
||||
MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => {
|
||||
marker.visit_span(&mut sp);
|
||||
marker.mark_span(&mut sp);
|
||||
with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
|
||||
let kind = token::NtLifetime(*ident, *is_raw);
|
||||
TokenTree::token_alone(kind, sp)
|
||||
|
|
@ -400,8 +397,8 @@ pub(super) fn transcribe<'a>(
|
|||
} else {
|
||||
// If we aren't able to match the meta-var, we push it back into the result but
|
||||
// with modified syntax context. (I believe this supports nested macros).
|
||||
marker.visit_span(&mut sp);
|
||||
marker.visit_ident(&mut original_ident);
|
||||
marker.mark_span(&mut sp);
|
||||
marker.mark_span(&mut original_ident.span);
|
||||
result.push(TokenTree::token_joint_hidden(token::Dollar, sp));
|
||||
result.push(TokenTree::Token(
|
||||
Token::from_ast_ident(original_ident),
|
||||
|
|
@ -430,16 +427,19 @@ pub(super) fn transcribe<'a>(
|
|||
// jump back out of the Delimited, pop the result_stack and add the new results back to
|
||||
// the previous results (from outside the Delimited).
|
||||
&mbe::TokenTree::Delimited(mut span, ref spacing, ref delimited) => {
|
||||
mut_visit::visit_delim_span(&mut marker, &mut span);
|
||||
marker.mark_span(&mut span.open);
|
||||
marker.mark_span(&mut span.close);
|
||||
stack.push(Frame::new_delimited(delimited, span, *spacing));
|
||||
result_stack.push(mem::take(&mut result));
|
||||
}
|
||||
|
||||
// Nothing much to do here. Just push the token to the result, being careful to
|
||||
// preserve syntax context.
|
||||
mbe::TokenTree::Token(token) => {
|
||||
let mut token = *token;
|
||||
mut_visit::visit_token(&mut marker, &mut token);
|
||||
&mbe::TokenTree::Token(mut token) => {
|
||||
marker.mark_span(&mut token.span);
|
||||
if let token::NtIdent(ident, _) | token::NtLifetime(ident, _) = &mut token.kind {
|
||||
marker.mark_span(&mut ident.span);
|
||||
}
|
||||
let tt = TokenTree::Token(token, Spacing::Alone);
|
||||
result.push(tt);
|
||||
}
|
||||
|
|
@ -504,7 +504,7 @@ fn maybe_use_metavar_location(
|
|||
return orig_tt.clone();
|
||||
}
|
||||
|
||||
marker.visit_span(&mut metavar_span);
|
||||
marker.mark_span(&mut metavar_span);
|
||||
let no_collision = match orig_tt {
|
||||
TokenTree::Token(token, ..) => {
|
||||
with_metavar_spans(|mspans| mspans.insert(token.span, metavar_span))
|
||||
|
|
@ -774,7 +774,7 @@ fn transcribe_metavar_expr<'a>(
|
|||
) -> PResult<'a, ()> {
|
||||
let mut visited_span = || {
|
||||
let mut span = sp.entire();
|
||||
marker.visit_span(&mut span);
|
||||
marker.mark_span(&mut span);
|
||||
span
|
||||
};
|
||||
match *expr {
|
||||
|
|
|
|||
|
|
@ -1813,6 +1813,9 @@ pub enum TyPatKind<'hir> {
|
|||
/// A range pattern (e.g., `1..=2` or `1..2`).
|
||||
Range(&'hir ConstArg<'hir>, &'hir ConstArg<'hir>),
|
||||
|
||||
/// A list of patterns where only one needs to be satisfied
|
||||
Or(&'hir [TyPat<'hir>]),
|
||||
|
||||
/// A placeholder for a pattern that wasn't well formed in some way.
|
||||
Err(ErrorGuaranteed),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -710,6 +710,7 @@ pub fn walk_ty_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v TyPat<'v>)
|
|||
try_visit!(visitor.visit_const_arg_unambig(lower_bound));
|
||||
try_visit!(visitor.visit_const_arg_unambig(upper_bound));
|
||||
}
|
||||
TyPatKind::Or(patterns) => walk_list!(visitor, visit_pattern_type_pattern, patterns),
|
||||
TyPatKind::Err(_) => (),
|
||||
}
|
||||
V::Result::output()
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#![feature(never_type)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(variant_count)]
|
||||
#![recursion_limit = "256"]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
extern crate self as rustc_hir;
|
||||
|
|
|
|||
|
|
@ -94,10 +94,12 @@ fn const_arg_anon_type_of<'tcx>(icx: &ItemCtxt<'tcx>, arg_hir_id: HirId, span: S
|
|||
}
|
||||
|
||||
Node::TyPat(pat) => {
|
||||
let hir::TyKind::Pat(ty, p) = tcx.parent_hir_node(pat.hir_id).expect_ty().kind else {
|
||||
bug!()
|
||||
let node = match tcx.parent_hir_node(pat.hir_id) {
|
||||
// Or patterns can be nested one level deep
|
||||
Node::TyPat(p) => tcx.parent_hir_node(p.hir_id),
|
||||
other => other,
|
||||
};
|
||||
assert_eq!(p.hir_id, pat.hir_id);
|
||||
let hir::TyKind::Pat(ty, _) = node.expect_ty().kind else { bug!() };
|
||||
icx.lower_ty(ty)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2715,30 +2715,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
hir::TyKind::Pat(ty, pat) => {
|
||||
let ty_span = ty.span;
|
||||
let ty = self.lower_ty(ty);
|
||||
let pat_ty = match pat.kind {
|
||||
hir::TyPatKind::Range(start, end) => {
|
||||
let (ty, start, end) = match ty.kind() {
|
||||
// Keep this list of types in sync with the list of types that
|
||||
// the `RangePattern` trait is implemented for.
|
||||
ty::Int(_) | ty::Uint(_) | ty::Char => {
|
||||
let start = self.lower_const_arg(start, FeedConstTy::No);
|
||||
let end = self.lower_const_arg(end, FeedConstTy::No);
|
||||
(ty, start, end)
|
||||
}
|
||||
_ => {
|
||||
let guar = self.dcx().span_delayed_bug(
|
||||
ty_span,
|
||||
"invalid base type for range pattern",
|
||||
);
|
||||
let errc = ty::Const::new_error(tcx, guar);
|
||||
(Ty::new_error(tcx, guar), errc, errc)
|
||||
}
|
||||
};
|
||||
|
||||
let pat = tcx.mk_pat(ty::PatternKind::Range { start, end });
|
||||
Ty::new_pat(tcx, ty, pat)
|
||||
}
|
||||
hir::TyPatKind::Err(e) => Ty::new_error(tcx, e),
|
||||
let pat_ty = match self.lower_pat_ty_pat(ty, ty_span, pat) {
|
||||
Ok(kind) => Ty::new_pat(tcx, ty, tcx.mk_pat(kind)),
|
||||
Err(guar) => Ty::new_error(tcx, guar),
|
||||
};
|
||||
self.record_ty(pat.hir_id, ty, pat.span);
|
||||
pat_ty
|
||||
|
|
@ -2750,6 +2729,39 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
result_ty
|
||||
}
|
||||
|
||||
fn lower_pat_ty_pat(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
ty_span: Span,
|
||||
pat: &hir::TyPat<'tcx>,
|
||||
) -> Result<ty::PatternKind<'tcx>, ErrorGuaranteed> {
|
||||
let tcx = self.tcx();
|
||||
match pat.kind {
|
||||
hir::TyPatKind::Range(start, end) => {
|
||||
match ty.kind() {
|
||||
// Keep this list of types in sync with the list of types that
|
||||
// the `RangePattern` trait is implemented for.
|
||||
ty::Int(_) | ty::Uint(_) | ty::Char => {
|
||||
let start = self.lower_const_arg(start, FeedConstTy::No);
|
||||
let end = self.lower_const_arg(end, FeedConstTy::No);
|
||||
Ok(ty::PatternKind::Range { start, end })
|
||||
}
|
||||
_ => Err(self
|
||||
.dcx()
|
||||
.span_delayed_bug(ty_span, "invalid base type for range pattern")),
|
||||
}
|
||||
}
|
||||
hir::TyPatKind::Or(patterns) => {
|
||||
self.tcx()
|
||||
.mk_patterns_from_iter(patterns.iter().map(|pat| {
|
||||
self.lower_pat_ty_pat(ty, ty_span, pat).map(|pat| tcx.mk_pat(pat))
|
||||
}))
|
||||
.map(ty::PatternKind::Or)
|
||||
}
|
||||
hir::TyPatKind::Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR.
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn lower_opaque_ty(&self, def_id: LocalDefId, in_trait: bool) -> Ty<'tcx> {
|
||||
|
|
|
|||
|
|
@ -251,12 +251,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
ty::Pat(typ, pat) => {
|
||||
match *pat {
|
||||
ty::PatternKind::Range { start, end } => {
|
||||
self.add_constraints_from_const(current, start, variance);
|
||||
self.add_constraints_from_const(current, end, variance);
|
||||
}
|
||||
}
|
||||
self.add_constraints_from_pat(current, variance, pat);
|
||||
self.add_constraints_from_ty(current, typ, variance);
|
||||
}
|
||||
|
||||
|
|
@ -334,6 +329,25 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn add_constraints_from_pat(
|
||||
&mut self,
|
||||
current: &CurrentItem,
|
||||
variance: VarianceTermPtr<'a>,
|
||||
pat: ty::Pattern<'tcx>,
|
||||
) {
|
||||
match *pat {
|
||||
ty::PatternKind::Range { start, end } => {
|
||||
self.add_constraints_from_const(current, start, variance);
|
||||
self.add_constraints_from_const(current, end, variance);
|
||||
}
|
||||
ty::PatternKind::Or(patterns) => {
|
||||
for pat in patterns {
|
||||
self.add_constraints_from_pat(current, variance, pat)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds constraints appropriate for a nominal type (enum, struct,
|
||||
/// object, etc) appearing in a context with ambient variance `variance`
|
||||
fn add_constraints_from_args(
|
||||
|
|
|
|||
|
|
@ -146,6 +146,7 @@ impl<'a> State<'a> {
|
|||
false,
|
||||
None,
|
||||
*delim,
|
||||
None,
|
||||
&tokens,
|
||||
true,
|
||||
span,
|
||||
|
|
@ -1882,6 +1883,19 @@ impl<'a> State<'a> {
|
|||
self.word("..=");
|
||||
self.print_const_arg(end);
|
||||
}
|
||||
TyPatKind::Or(patterns) => {
|
||||
self.popen();
|
||||
let mut first = true;
|
||||
for pat in patterns {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
self.word(" | ");
|
||||
}
|
||||
self.print_ty_pat(pat);
|
||||
}
|
||||
self.pclose();
|
||||
}
|
||||
TyPatKind::Err(_) => {
|
||||
self.popen();
|
||||
self.word("/*ERROR*/");
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use rustc_middle::ty::adjustment::{
|
|||
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{Ident, Span, sym};
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_trait_selection::error_reporting::traits::DefIdOrName;
|
||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
|
|
@ -87,14 +87,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
let output = match result {
|
||||
None => {
|
||||
// this will report an error since original_callee_ty is not a fn
|
||||
self.confirm_builtin_call(
|
||||
call_expr,
|
||||
callee_expr,
|
||||
original_callee_ty,
|
||||
arg_exprs,
|
||||
expected,
|
||||
)
|
||||
// Check all of the arg expressions, but with no expectations
|
||||
// since we don't have a signature to compare them to.
|
||||
for arg in arg_exprs {
|
||||
self.check_expr(arg);
|
||||
}
|
||||
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind
|
||||
&& let [segment] = path.segments
|
||||
{
|
||||
self.dcx().try_steal_modify_and_emit_err(
|
||||
segment.ident.span,
|
||||
StashKey::CallIntoMethod,
|
||||
|err| {
|
||||
// Try suggesting `foo(a)` -> `a.foo()` if possible.
|
||||
self.suggest_call_as_method(
|
||||
err, segment, arg_exprs, call_expr, expected,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let guar = self.report_invalid_callee(call_expr, callee_expr, expr_ty, arg_exprs);
|
||||
Ty::new_error(self.tcx, guar)
|
||||
}
|
||||
|
||||
Some(CallStep::Builtin(callee_ty)) => {
|
||||
|
|
@ -296,9 +311,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span)))
|
||||
});
|
||||
|
||||
if let Some(ok) = self.lookup_method_in_trait(
|
||||
if let Some(ok) = self.lookup_method_for_operator(
|
||||
self.misc(call_expr.span),
|
||||
Ident::with_dummy_span(method_name),
|
||||
method_name,
|
||||
trait_def_id,
|
||||
adjusted_ty,
|
||||
opt_input_type,
|
||||
|
|
@ -461,32 +476,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
(fn_sig, Some(def_id))
|
||||
}
|
||||
|
||||
// FIXME(const_trait_impl): these arms should error because we can't enforce them
|
||||
ty::FnPtr(sig_tys, hdr) => (sig_tys.with(hdr), None),
|
||||
_ => {
|
||||
for arg in arg_exprs {
|
||||
self.check_expr(arg);
|
||||
}
|
||||
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind
|
||||
&& let [segment] = path.segments
|
||||
{
|
||||
self.dcx().try_steal_modify_and_emit_err(
|
||||
segment.ident.span,
|
||||
StashKey::CallIntoMethod,
|
||||
|err| {
|
||||
// Try suggesting `foo(a)` -> `a.foo()` if possible.
|
||||
self.suggest_call_as_method(
|
||||
err, segment, arg_exprs, call_expr, expected,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let err = self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs);
|
||||
|
||||
return Ty::new_error(self.tcx, err);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// Replace any late-bound regions that appear in the function
|
||||
|
|
@ -908,19 +902,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
arg_exprs: &'tcx [hir::Expr<'tcx>],
|
||||
expected: Expectation<'tcx>,
|
||||
method_callee: MethodCallee<'tcx>,
|
||||
method: MethodCallee<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let output_type = self.check_method_argument_types(
|
||||
self.check_argument_types(
|
||||
call_expr.span,
|
||||
call_expr,
|
||||
Ok(method_callee),
|
||||
arg_exprs,
|
||||
TupleArgumentsFlag::TupleArguments,
|
||||
&method.sig.inputs()[1..],
|
||||
method.sig.output(),
|
||||
expected,
|
||||
arg_exprs,
|
||||
method.sig.c_variadic,
|
||||
TupleArgumentsFlag::TupleArguments,
|
||||
Some(method.def_id),
|
||||
);
|
||||
|
||||
self.write_method_call_and_enforce_effects(call_expr.hir_id, call_expr.span, method_callee);
|
||||
output_type
|
||||
self.write_method_call_and_enforce_effects(call_expr.hir_id, call_expr.span, method);
|
||||
|
||||
method.sig.output()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ use tracing::{debug, instrument, trace};
|
|||
use {rustc_ast as ast, rustc_hir as hir};
|
||||
|
||||
use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation};
|
||||
use crate::TupleArgumentsFlag::DontTupleArguments;
|
||||
use crate::coercion::{CoerceMany, DynamicCoerceMany};
|
||||
use crate::errors::{
|
||||
AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr,
|
||||
|
|
@ -51,8 +50,8 @@ use crate::errors::{
|
|||
YieldExprOutsideOfCoroutine,
|
||||
};
|
||||
use crate::{
|
||||
BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, Needs, cast, fatally_break_rust,
|
||||
report_unexpected_variant_res, type_error_struct,
|
||||
BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, Needs, TupleArgumentsFlag, cast,
|
||||
fatally_break_rust, report_unexpected_variant_res, type_error_struct,
|
||||
};
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
|
@ -1591,28 +1590,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// no need to check for bot/err -- callee does that
|
||||
let rcvr_t = self.structurally_resolve_type(rcvr.span, rcvr_t);
|
||||
|
||||
let method = match self.lookup_method(rcvr_t, segment, segment.ident.span, expr, rcvr, args)
|
||||
{
|
||||
match self.lookup_method(rcvr_t, segment, segment.ident.span, expr, rcvr, args) {
|
||||
Ok(method) => {
|
||||
// We could add a "consider `foo::<params>`" suggestion here, but I wasn't able to
|
||||
// trigger this codepath causing `structurally_resolve_type` to emit an error.
|
||||
self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method);
|
||||
Ok(method)
|
||||
|
||||
self.check_argument_types(
|
||||
segment.ident.span,
|
||||
expr,
|
||||
&method.sig.inputs()[1..],
|
||||
method.sig.output(),
|
||||
expected,
|
||||
args,
|
||||
method.sig.c_variadic,
|
||||
TupleArgumentsFlag::DontTupleArguments,
|
||||
Some(method.def_id),
|
||||
);
|
||||
|
||||
method.sig.output()
|
||||
}
|
||||
Err(error) => {
|
||||
Err(self.report_method_error(expr.hir_id, rcvr_t, error, expected, false))
|
||||
}
|
||||
};
|
||||
let guar = self.report_method_error(expr.hir_id, rcvr_t, error, expected, false);
|
||||
|
||||
// Call the generic checker.
|
||||
self.check_method_argument_types(
|
||||
segment.ident.span,
|
||||
expr,
|
||||
method,
|
||||
args,
|
||||
DontTupleArguments,
|
||||
expected,
|
||||
)
|
||||
let err_inputs = self.err_args(args.len(), guar);
|
||||
let err_output = Ty::new_error(self.tcx, guar);
|
||||
|
||||
self.check_argument_types(
|
||||
segment.ident.span,
|
||||
expr,
|
||||
&err_inputs,
|
||||
err_output,
|
||||
NoExpectation,
|
||||
args,
|
||||
false,
|
||||
TupleArgumentsFlag::DontTupleArguments,
|
||||
None,
|
||||
);
|
||||
|
||||
err_output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks use `x.use`.
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, P
|
|||
use crate::fn_ctxt::infer::FnCall;
|
||||
use crate::gather_locals::Declaration;
|
||||
use crate::inline_asm::InlineAsmCtxt;
|
||||
use crate::method::MethodCallee;
|
||||
use crate::method::probe::IsSuggestion;
|
||||
use crate::method::probe::Mode::MethodCall;
|
||||
use crate::method::probe::ProbeScope::TraitsInScope;
|
||||
|
|
@ -127,61 +126,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(in super::super) fn check_method_argument_types(
|
||||
&self,
|
||||
sp: Span,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
method: Result<MethodCallee<'tcx>, ErrorGuaranteed>,
|
||||
args_no_rcvr: &'tcx [hir::Expr<'tcx>],
|
||||
tuple_arguments: TupleArgumentsFlag,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let has_error = match method {
|
||||
Ok(method) => method.args.error_reported().and(method.sig.error_reported()),
|
||||
Err(guar) => Err(guar),
|
||||
};
|
||||
if let Err(guar) = has_error {
|
||||
let err_inputs = self.err_args(
|
||||
method.map_or(args_no_rcvr.len(), |method| method.sig.inputs().len() - 1),
|
||||
guar,
|
||||
);
|
||||
let err_output = Ty::new_error(self.tcx, guar);
|
||||
|
||||
let err_inputs = match tuple_arguments {
|
||||
DontTupleArguments => err_inputs,
|
||||
TupleArguments => vec![Ty::new_tup(self.tcx, &err_inputs)],
|
||||
};
|
||||
|
||||
self.check_argument_types(
|
||||
sp,
|
||||
expr,
|
||||
&err_inputs,
|
||||
err_output,
|
||||
NoExpectation,
|
||||
args_no_rcvr,
|
||||
false,
|
||||
tuple_arguments,
|
||||
method.ok().map(|method| method.def_id),
|
||||
);
|
||||
return err_output;
|
||||
}
|
||||
|
||||
let method = method.unwrap();
|
||||
self.check_argument_types(
|
||||
sp,
|
||||
expr,
|
||||
&method.sig.inputs()[1..],
|
||||
method.sig.output(),
|
||||
expected,
|
||||
args_no_rcvr,
|
||||
method.sig.c_variadic,
|
||||
tuple_arguments,
|
||||
Some(method.def_id),
|
||||
);
|
||||
|
||||
method.sig.output()
|
||||
}
|
||||
|
||||
/// Generic function that factors out common logic from function calls,
|
||||
/// method calls and overloaded operators.
|
||||
pub(in super::super) fn check_argument_types(
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use rustc_middle::ty::{
|
|||
self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TypeVisitableExt,
|
||||
};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span};
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::{self, NormalizeExt};
|
||||
use tracing::{debug, instrument};
|
||||
|
|
@ -329,10 +329,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
/// an obligation for a particular trait with the given self type and checks
|
||||
/// whether that trait is implemented.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(super) fn lookup_method_in_trait(
|
||||
pub(super) fn lookup_method_for_operator(
|
||||
&self,
|
||||
cause: ObligationCause<'tcx>,
|
||||
m_name: Ident,
|
||||
method_name: Symbol,
|
||||
trait_def_id: DefId,
|
||||
self_ty: Ty<'tcx>,
|
||||
opt_rhs_ty: Option<Ty<'tcx>>,
|
||||
|
|
@ -374,13 +374,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// Trait must have a method named `m_name` and it should not have
|
||||
// type parameters or early-bound regions.
|
||||
let tcx = self.tcx;
|
||||
let Some(method_item) = self.associated_value(trait_def_id, m_name) else {
|
||||
// We use `Ident::with_dummy_span` since no built-in operator methods have
|
||||
// any macro-specific hygeine, so the span's context doesn't really matter.
|
||||
let Some(method_item) =
|
||||
self.associated_value(trait_def_id, Ident::with_dummy_span(method_name))
|
||||
else {
|
||||
bug!("expected associated item for operator trait")
|
||||
};
|
||||
|
||||
let def_id = method_item.def_id;
|
||||
if !method_item.is_fn() {
|
||||
span_bug!(tcx.def_span(def_id), "expected `{m_name}` to be an associated function");
|
||||
span_bug!(
|
||||
tcx.def_span(def_id),
|
||||
"expected `{method_name}` to be an associated function"
|
||||
);
|
||||
}
|
||||
|
||||
debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
|
||||
|
|
|
|||
|
|
@ -1213,7 +1213,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
|||
debug!("pick_all_method: step={:?}", step);
|
||||
// skip types that are from a type error or that would require dereferencing
|
||||
// a raw pointer
|
||||
!step.self_ty.references_error() && !step.from_unsafe_deref
|
||||
!step.self_ty.value.references_error() && !step.from_unsafe_deref
|
||||
})
|
||||
.find_map(|step| {
|
||||
let InferOk { value: self_ty, obligations: _ } = self
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
|
|||
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_session::errors::ExprParenthesesNeeded;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{Ident, Span, Symbol, sym};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt};
|
||||
use tracing::debug;
|
||||
|
|
@ -975,7 +975,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
lhs_ty, opname, trait_did
|
||||
);
|
||||
|
||||
let opname = Ident::with_dummy_span(opname);
|
||||
let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip();
|
||||
let cause = self.cause(
|
||||
span,
|
||||
|
|
@ -990,7 +989,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
);
|
||||
|
||||
let method =
|
||||
self.lookup_method_in_trait(cause.clone(), opname, trait_did, lhs_ty, opt_rhs_ty);
|
||||
self.lookup_method_for_operator(cause.clone(), opname, trait_did, lhs_ty, opt_rhs_ty);
|
||||
match method {
|
||||
Some(ok) => {
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use rustc_middle::ty::adjustment::{
|
|||
PointerCoercion,
|
||||
};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::{Ident, Span, sym};
|
||||
use rustc_span::{Span, sym};
|
||||
use tracing::debug;
|
||||
use {rustc_ast as ast, rustc_hir as hir};
|
||||
|
||||
|
|
@ -211,13 +211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return None;
|
||||
};
|
||||
|
||||
self.lookup_method_in_trait(
|
||||
self.misc(span),
|
||||
Ident::with_dummy_span(imm_op),
|
||||
imm_tr,
|
||||
base_ty,
|
||||
opt_rhs_ty,
|
||||
)
|
||||
self.lookup_method_for_operator(self.misc(span), imm_op, imm_tr, base_ty, opt_rhs_ty)
|
||||
}
|
||||
|
||||
fn try_mutable_overloaded_place_op(
|
||||
|
|
@ -237,13 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return None;
|
||||
};
|
||||
|
||||
self.lookup_method_in_trait(
|
||||
self.misc(span),
|
||||
Ident::with_dummy_span(mut_op),
|
||||
mut_tr,
|
||||
base_ty,
|
||||
opt_rhs_ty,
|
||||
)
|
||||
self.lookup_method_for_operator(self.misc(span), mut_op, mut_tr, base_ty, opt_rhs_ty)
|
||||
}
|
||||
|
||||
/// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
// Type resolution: the phase that finds all the types in the AST with
|
||||
// unresolved type variables and replaces "ty_var" types with their
|
||||
// generic parameters.
|
||||
//! During type inference, partially inferred terms are
|
||||
//! represented using inference variables (ty::Infer). These don't appear in
|
||||
//! the final [`ty::TypeckResults`] since all of the types should have been
|
||||
//! inferred once typeck is done.
|
||||
//!
|
||||
//! When type inference is running however, having to update the typeck results
|
||||
//! every time a new type is inferred would be unreasonably slow, so instead all
|
||||
//! of the replacement happens at the end in [`FnCtxt::resolve_type_vars_in_body`],
|
||||
//! which creates a new `TypeckResults` which doesn't contain any inference variables.
|
||||
|
||||
use std::mem;
|
||||
|
||||
|
|
@ -9,7 +15,6 @@ use rustc_errors::ErrorGuaranteed;
|
|||
use rustc_hir::intravisit::{self, InferKind, Visitor};
|
||||
use rustc_hir::{self as hir, AmbigArg, HirId};
|
||||
use rustc_infer::traits::solve::Goal;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion};
|
||||
use rustc_middle::ty::{
|
||||
|
|
@ -27,15 +32,6 @@ use crate::FnCtxt;
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
// Entry point
|
||||
|
||||
// During type inference, partially inferred types are
|
||||
// represented using Type variables (ty::Infer). These don't appear in
|
||||
// the final TypeckResults since all of the types should have been
|
||||
// inferred once typeck is done.
|
||||
// When type inference is running however, having to update the typeck
|
||||
// typeck results every time a new type is inferred would be unreasonably slow,
|
||||
// so instead all of the replacement happens at the end in
|
||||
// resolve_type_vars_in_body, which creates a new TypeTables which
|
||||
// doesn't contain any inference types.
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub(crate) fn resolve_type_vars_in_body(
|
||||
&self,
|
||||
|
|
@ -90,14 +86,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// The Writeback context. This visitor walks the HIR, checking the
|
||||
// fn-specific typeck results to find references to types or regions. It
|
||||
// resolves those regions to remove inference variables and writes the
|
||||
// final result back into the master typeck results in the tcx. Here and
|
||||
// there, it applies a few ad-hoc checks that were not convenient to
|
||||
// do elsewhere.
|
||||
|
||||
/// The Writeback context. This visitor walks the HIR, checking the
|
||||
/// fn-specific typeck results to find inference variables. It resolves
|
||||
/// those inference variables and writes the final result into the
|
||||
/// `TypeckResults`. It also applies a few ad-hoc checks that were not
|
||||
/// convenient to do elsewhere.
|
||||
struct WritebackCx<'cx, 'tcx> {
|
||||
fcx: &'cx FnCtxt<'cx, 'tcx>,
|
||||
|
||||
|
|
@ -513,15 +506,6 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
|||
self.typeck_results.user_provided_types_mut().extend(
|
||||
fcx_typeck_results.user_provided_types().items().map(|(local_id, c_ty)| {
|
||||
let hir_id = HirId { owner: common_hir_owner, local_id };
|
||||
|
||||
if cfg!(debug_assertions) && c_ty.has_infer() {
|
||||
span_bug!(
|
||||
hir_id.to_span(self.fcx.tcx),
|
||||
"writeback: `{:?}` has inference variables",
|
||||
c_ty
|
||||
);
|
||||
};
|
||||
|
||||
(hir_id, *c_ty)
|
||||
}),
|
||||
);
|
||||
|
|
@ -532,17 +516,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
|||
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
|
||||
|
||||
self.typeck_results.user_provided_sigs.extend_unord(
|
||||
fcx_typeck_results.user_provided_sigs.items().map(|(&def_id, c_sig)| {
|
||||
if cfg!(debug_assertions) && c_sig.has_infer() {
|
||||
span_bug!(
|
||||
self.fcx.tcx.def_span(def_id),
|
||||
"writeback: `{:?}` has inference variables",
|
||||
c_sig
|
||||
);
|
||||
};
|
||||
|
||||
(def_id, *c_sig)
|
||||
}),
|
||||
fcx_typeck_results.user_provided_sigs.items().map(|(def_id, c_sig)| (*def_id, *c_sig)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -897,7 +871,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
|
|||
let cause = ObligationCause::misc(self.span.to_span(tcx), body_id);
|
||||
let at = self.fcx.at(&cause, self.fcx.param_env);
|
||||
let universes = vec![None; outer_exclusive_binder(value).as_usize()];
|
||||
match solve::deeply_normalize_with_skipped_universes_and_ambiguous_goals(
|
||||
match solve::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
|
||||
at, value, universes,
|
||||
) {
|
||||
Ok((value, goals)) => {
|
||||
|
|
|
|||
|
|
@ -900,6 +900,12 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
|
|||
// is not defined. So we need to cfg it out.
|
||||
#[cfg(all(not(doc), debug_assertions))]
|
||||
rustc_passes::hir_id_validator::check_crate(tcx);
|
||||
|
||||
// Prefetch this to prevent multiple threads from blocking on it later.
|
||||
// This is needed since the `hir_id_validator::check_crate` call above is not guaranteed
|
||||
// to use `hir_crate`.
|
||||
tcx.ensure_done().hir_crate(());
|
||||
|
||||
let sess = tcx.sess;
|
||||
sess.time("misc_checking_1", || {
|
||||
parallel!(
|
||||
|
|
|
|||
|
|
@ -422,6 +422,16 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_, hir::AmbigArg>) {
|
||||
if let hir::TyKind::BareFn(hir::BareFnTy { param_idents, .. }) = &ty.kind {
|
||||
for param_ident in *param_idents {
|
||||
if let Some(param_ident) = param_ident {
|
||||
self.check_snake_case(cx, "variable", param_ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &hir::TraitItem<'_>) {
|
||||
if let hir::TraitItemKind::Fn(_, hir::TraitFn::Required(param_idents)) = item.kind {
|
||||
self.check_snake_case(cx, "trait method", &item.ident);
|
||||
|
|
|
|||
|
|
@ -755,10 +755,10 @@ declare_lint! {
|
|||
/// *subsequent* fields of the associated structs to use an alignment value
|
||||
/// where the floating-point type is aligned on a 4-byte boundary.
|
||||
///
|
||||
/// The power alignment rule for structs needed for C compatibility is
|
||||
/// unimplementable within `repr(C)` in the compiler without building in
|
||||
/// handling of references to packed fields and infectious nested layouts,
|
||||
/// so a warning is produced in these situations.
|
||||
/// Effectively, subsequent floating-point fields act as-if they are `repr(packed(4))`. This
|
||||
/// would be unsound to do in a `repr(C)` type without all the restrictions that come with
|
||||
/// `repr(packed)`. Rust instead chooses a layout that maintains soundness of Rust code, at the
|
||||
/// expense of incompatibility with C code.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
|
|
@ -790,8 +790,10 @@ declare_lint! {
|
|||
/// - offset_of!(Floats, a) == 0
|
||||
/// - offset_of!(Floats, b) == 8
|
||||
/// - offset_of!(Floats, c) == 12
|
||||
/// However, rust currently aligns `c` at offset_of!(Floats, c) == 16.
|
||||
/// Thus, a warning should be produced for the above struct in this case.
|
||||
///
|
||||
/// However, Rust currently aligns `c` at `offset_of!(Floats, c) == 16`.
|
||||
/// Using offset 12 would be unsound since `f64` generally must be 8-aligned on this target.
|
||||
/// Thus, a warning is produced for the above struct.
|
||||
USES_POWER_ALIGNMENT,
|
||||
Warn,
|
||||
"Structs do not follow the power alignment rule under repr(C)"
|
||||
|
|
@ -878,25 +880,36 @@ fn ty_is_known_nonnull<'tcx>(
|
|||
}
|
||||
ty::Pat(base, pat) => {
|
||||
ty_is_known_nonnull(tcx, typing_env, *base, mode)
|
||||
|| Option::unwrap_or_default(
|
||||
try {
|
||||
match **pat {
|
||||
ty::PatternKind::Range { start, end } => {
|
||||
let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?;
|
||||
let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?;
|
||||
|
||||
// This also works for negative numbers, as we just need
|
||||
// to ensure we aren't wrapping over zero.
|
||||
start > 0 && end >= start
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|| pat_ty_is_known_nonnull(tcx, typing_env, *pat)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn pat_ty_is_known_nonnull<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
pat: ty::Pattern<'tcx>,
|
||||
) -> bool {
|
||||
Option::unwrap_or_default(
|
||||
try {
|
||||
match *pat {
|
||||
ty::PatternKind::Range { start, end } => {
|
||||
let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?;
|
||||
let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?;
|
||||
|
||||
// This also works for negative numbers, as we just need
|
||||
// to ensure we aren't wrapping over zero.
|
||||
start > 0 && end >= start
|
||||
}
|
||||
ty::PatternKind::Or(patterns) => {
|
||||
patterns.iter().all(|pat| pat_ty_is_known_nonnull(tcx, typing_env, pat))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type.
|
||||
/// If the type passed in was not scalar, returns None.
|
||||
fn get_nullable_type<'tcx>(
|
||||
|
|
@ -1038,13 +1051,29 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
|
|||
}
|
||||
None
|
||||
}
|
||||
ty::Pat(base, pat) => match **pat {
|
||||
ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, *base),
|
||||
},
|
||||
ty::Pat(base, pat) => get_nullable_type_from_pat(tcx, typing_env, *base, *pat),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_nullable_type_from_pat<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
base: Ty<'tcx>,
|
||||
pat: ty::Pattern<'tcx>,
|
||||
) -> Option<Ty<'tcx>> {
|
||||
match *pat {
|
||||
ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, base),
|
||||
ty::PatternKind::Or(patterns) => {
|
||||
let first = get_nullable_type_from_pat(tcx, typing_env, base, patterns[0])?;
|
||||
for &pat in &patterns[1..] {
|
||||
assert_eq!(first, get_nullable_type_from_pat(tcx, typing_env, base, pat)?);
|
||||
}
|
||||
Some(first)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
||||
/// Check if the type is array and emit an unsafe type lint.
|
||||
fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
|
||||
|
|
@ -1628,15 +1657,13 @@ impl ImproperCTypesDefinitions {
|
|||
cx: &LateContext<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
assert!(cx.tcx.sess.target.os == "aix");
|
||||
// Structs (under repr(C)) follow the power alignment rule if:
|
||||
// - the first field of the struct is a floating-point type that
|
||||
// is greater than 4-bytes, or
|
||||
// - the first field of the struct is an aggregate whose
|
||||
// recursively first field is a floating-point type greater than
|
||||
// 4 bytes.
|
||||
if cx.tcx.sess.target.os != "aix" {
|
||||
return false;
|
||||
}
|
||||
if ty.is_floating_point() && ty.primitive_size(cx.tcx).bytes() > 4 {
|
||||
return true;
|
||||
} else if let Adt(adt_def, _) = ty.kind()
|
||||
|
|
@ -1674,21 +1701,14 @@ impl ImproperCTypesDefinitions {
|
|||
&& !adt_def.all_fields().next().is_none()
|
||||
{
|
||||
let struct_variant_data = item.expect_struct().1;
|
||||
for (index, ..) in struct_variant_data.fields().iter().enumerate() {
|
||||
for field_def in struct_variant_data.fields().iter().skip(1) {
|
||||
// Struct fields (after the first field) are checked for the
|
||||
// power alignment rule, as fields after the first are likely
|
||||
// to be the fields that are misaligned.
|
||||
if index != 0 {
|
||||
let first_field_def = struct_variant_data.fields()[index];
|
||||
let def_id = first_field_def.def_id;
|
||||
let ty = cx.tcx.type_of(def_id).instantiate_identity();
|
||||
if self.check_arg_for_power_alignment(cx, ty) {
|
||||
cx.emit_span_lint(
|
||||
USES_POWER_ALIGNMENT,
|
||||
first_field_def.span,
|
||||
UsesPowerAlignment,
|
||||
);
|
||||
}
|
||||
let def_id = field_def.def_id;
|
||||
let ty = cx.tcx.type_of(def_id).instantiate_identity();
|
||||
if self.check_arg_for_power_alignment(cx, ty) {
|
||||
cx.emit_span_lint(USES_POWER_ALIGNMENT, field_def.span, UsesPowerAlignment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1365,7 +1365,12 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, size_t num_modules,
|
|||
// Convert the preserved symbols set from string to GUID, this is then needed
|
||||
// for internalization.
|
||||
for (size_t i = 0; i < num_symbols; i++) {
|
||||
#if LLVM_VERSION_GE(21, 0)
|
||||
auto GUID =
|
||||
GlobalValue::getGUIDAssumingExternalLinkage(preserved_symbols[i]);
|
||||
#else
|
||||
auto GUID = GlobalValue::getGUID(preserved_symbols[i]);
|
||||
#endif
|
||||
Ret->GUIDPreservedSymbols.insert(GUID);
|
||||
}
|
||||
|
||||
|
|
@ -1685,11 +1690,11 @@ extern "C" void LLVMRustComputeLTOCacheKey(RustStringRef KeyOut,
|
|||
// Based on the 'InProcessThinBackend' constructor in LLVM
|
||||
#if LLVM_VERSION_GE(21, 0)
|
||||
for (auto &Name : Data->Index.cfiFunctionDefs().symbols())
|
||||
CfiFunctionDefs.insert(
|
||||
GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name)));
|
||||
CfiFunctionDefs.insert(GlobalValue::getGUIDAssumingExternalLinkage(
|
||||
GlobalValue::dropLLVMManglingEscape(Name)));
|
||||
for (auto &Name : Data->Index.cfiFunctionDecls().symbols())
|
||||
CfiFunctionDecls.insert(
|
||||
GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name)));
|
||||
CfiFunctionDecls.insert(GlobalValue::getGUIDAssumingExternalLinkage(
|
||||
GlobalValue::dropLLVMManglingEscape(Name)));
|
||||
#else
|
||||
for (auto &Name : Data->Index.cfiFunctionDefs())
|
||||
CfiFunctionDefs.insert(
|
||||
|
|
|
|||
|
|
@ -407,11 +407,23 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
|
||||
TokenStream::from(quote! {
|
||||
/// Higher-order macro that invokes the specified macro with a prepared
|
||||
/// list of all query signatures (including modifiers).
|
||||
///
|
||||
/// This allows multiple simpler macros to each have access to the list
|
||||
/// of queries.
|
||||
#[macro_export]
|
||||
macro_rules! rustc_query_append {
|
||||
($macro:ident! $( [$($other:tt)*] )?) => {
|
||||
macro_rules! rustc_with_all_queries {
|
||||
(
|
||||
// The macro to invoke once, on all queries (plus extras).
|
||||
$macro:ident!
|
||||
|
||||
// Within [], an optional list of extra "query" signatures to
|
||||
// pass to the given macro, in addition to the actual queries.
|
||||
$( [$($extra_fake_queries:tt)*] )?
|
||||
) => {
|
||||
$macro! {
|
||||
$( $($other)* )?
|
||||
$( $($extra_fake_queries)* )?
|
||||
#query_stream
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,11 @@ use crate::ty::TyCtxt;
|
|||
|
||||
macro_rules! define_dep_nodes {
|
||||
(
|
||||
$($(#[$attr:meta])*
|
||||
[$($modifiers:tt)*] fn $variant:ident($($K:tt)*) -> $V:ty,)*) => {
|
||||
$(
|
||||
$(#[$attr:meta])*
|
||||
[$($modifiers:tt)*] fn $variant:ident($($K:tt)*) -> $V:ty,
|
||||
)*
|
||||
) => {
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! make_dep_kind_array {
|
||||
|
|
@ -83,7 +86,9 @@ macro_rules! define_dep_nodes {
|
|||
};
|
||||
}
|
||||
|
||||
rustc_query_append!(define_dep_nodes![
|
||||
// Create various data structures for each query, and also for a few things
|
||||
// that aren't queries.
|
||||
rustc_with_all_queries!(define_dep_nodes![
|
||||
/// We use this for most things when incr. comp. is turned off.
|
||||
[] fn Null() -> (),
|
||||
/// We use this to create a forever-red node.
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@
|
|||
#![feature(try_trait_v2_yeet)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(yeet_expr)]
|
||||
#![recursion_limit = "256"]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -115,15 +115,16 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
// @lcnr believes that successfully evaluating even though there are
|
||||
// used generic parameters is a bug of evaluation, so checking for it
|
||||
// here does feel somewhat sensible.
|
||||
if !self.features().generic_const_exprs() && ct.args.has_non_region_param() {
|
||||
let def_kind = self.def_kind(instance.def_id());
|
||||
assert!(
|
||||
matches!(
|
||||
def_kind,
|
||||
DefKind::InlineConst | DefKind::AnonConst | DefKind::AssocConst
|
||||
),
|
||||
"{cid:?} is {def_kind:?}",
|
||||
);
|
||||
if !self.features().generic_const_exprs()
|
||||
&& ct.args.has_non_region_param()
|
||||
// We only FCW for anon consts as repeat expr counts with anon consts are the only place
|
||||
// that we have a back compat hack for. We don't need to check this is a const argument
|
||||
// as only anon consts as const args should get evaluated "for the type system".
|
||||
//
|
||||
// If we don't *only* FCW anon consts we can wind up incorrectly FCW'ing uses of assoc
|
||||
// consts in pattern positions. #140447
|
||||
&& self.def_kind(instance.def_id()) == DefKind::AnonConst
|
||||
{
|
||||
let mir_body = self.mir_for_ctfe(instance.def_id());
|
||||
if mir_body.is_polymorphic {
|
||||
let Some(local_def_id) = ct.def.as_local() else { return };
|
||||
|
|
|
|||
|
|
@ -2578,5 +2578,5 @@ rustc_queries! {
|
|||
}
|
||||
}
|
||||
|
||||
rustc_query_append! { define_callbacks! }
|
||||
rustc_with_all_queries! { define_callbacks! }
|
||||
rustc_feedable_queries! { define_feedable! }
|
||||
|
|
|
|||
|
|
@ -313,8 +313,11 @@ macro_rules! separate_provide_extern_default {
|
|||
|
||||
macro_rules! define_callbacks {
|
||||
(
|
||||
$($(#[$attr:meta])*
|
||||
[$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => {
|
||||
$(
|
||||
$(#[$attr:meta])*
|
||||
[$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,
|
||||
)*
|
||||
) => {
|
||||
|
||||
#[allow(unused_lifetimes)]
|
||||
pub mod queries {
|
||||
|
|
|
|||
|
|
@ -442,6 +442,15 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List<ty::BoundVaria
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List<ty::Pattern<'tcx>> {
|
||||
fn decode(decoder: &mut D) -> &'tcx Self {
|
||||
let len = decoder.read_usize();
|
||||
decoder.interner().mk_patterns_from_iter(
|
||||
(0..len).map::<ty::Pattern<'tcx>, _>(|_| Decodable::decode(decoder)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List<ty::Const<'tcx>> {
|
||||
fn decode(decoder: &mut D) -> &'tcx Self {
|
||||
let len = decoder.read_usize();
|
||||
|
|
@ -503,6 +512,7 @@ impl_decodable_via_ref! {
|
|||
&'tcx mir::Body<'tcx>,
|
||||
&'tcx mir::ConcreteOpaqueTypes<'tcx>,
|
||||
&'tcx ty::List<ty::BoundVariableKind>,
|
||||
&'tcx ty::List<ty::Pattern<'tcx>>,
|
||||
&'tcx ty::ListWithCachedTypeInfo<ty::Clause<'tcx>>,
|
||||
&'tcx ty::List<FieldIdx>,
|
||||
&'tcx ty::List<(VariantIdx, FieldIdx)>,
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||
|
||||
type AllocId = crate::mir::interpret::AllocId;
|
||||
type Pat = Pattern<'tcx>;
|
||||
type PatList = &'tcx List<Pattern<'tcx>>;
|
||||
type Safety = hir::Safety;
|
||||
type Abi = ExternAbi;
|
||||
type Const = ty::Const<'tcx>;
|
||||
|
|
@ -842,6 +843,7 @@ pub struct CtxtInterners<'tcx> {
|
|||
captures: InternedSet<'tcx, List<&'tcx ty::CapturedPlace<'tcx>>>,
|
||||
offset_of: InternedSet<'tcx, List<(VariantIdx, FieldIdx)>>,
|
||||
valtree: InternedSet<'tcx, ty::ValTreeKind<'tcx>>,
|
||||
patterns: InternedSet<'tcx, List<ty::Pattern<'tcx>>>,
|
||||
}
|
||||
|
||||
impl<'tcx> CtxtInterners<'tcx> {
|
||||
|
|
@ -878,6 +880,7 @@ impl<'tcx> CtxtInterners<'tcx> {
|
|||
captures: InternedSet::with_capacity(N),
|
||||
offset_of: InternedSet::with_capacity(N),
|
||||
valtree: InternedSet::with_capacity(N),
|
||||
patterns: InternedSet::with_capacity(N),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2662,6 +2665,7 @@ slice_interners!(
|
|||
local_def_ids: intern_local_def_ids(LocalDefId),
|
||||
captures: intern_captures(&'tcx ty::CapturedPlace<'tcx>),
|
||||
offset_of: pub mk_offset_of((VariantIdx, FieldIdx)),
|
||||
patterns: pub mk_patterns(Pattern<'tcx>),
|
||||
);
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
|
|
@ -2935,6 +2939,14 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
self.intern_local_def_ids(def_ids)
|
||||
}
|
||||
|
||||
pub fn mk_patterns_from_iter<I, T>(self, iter: I) -> T::Output
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: CollectAndApply<ty::Pattern<'tcx>, &'tcx List<ty::Pattern<'tcx>>>,
|
||||
{
|
||||
T::collect_and_apply(iter, |xs| self.mk_patterns(xs))
|
||||
}
|
||||
|
||||
pub fn mk_local_def_ids_from_iter<I, T>(self, iter: I) -> T::Output
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ use rustc_session::lint::LintBuffer;
|
|||
pub use rustc_session::lint::RegisteredTools;
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
use rustc_span::{DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol, kw, sym};
|
||||
pub use rustc_type_ir::data_structures::DelayedSet;
|
||||
pub use rustc_type_ir::relate::VarianceDiagInfo;
|
||||
pub use rustc_type_ir::*;
|
||||
use tracing::{debug, instrument};
|
||||
|
|
|
|||
|
|
@ -23,6 +23,13 @@ impl<'tcx> Flags for Pattern<'tcx> {
|
|||
FlagComputation::for_const_kind(&start.kind()).flags
|
||||
| FlagComputation::for_const_kind(&end.kind()).flags
|
||||
}
|
||||
ty::PatternKind::Or(pats) => {
|
||||
let mut flags = pats[0].flags();
|
||||
for pat in pats[1..].iter() {
|
||||
flags |= pat.flags();
|
||||
}
|
||||
flags
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -31,6 +38,13 @@ impl<'tcx> Flags for Pattern<'tcx> {
|
|||
ty::PatternKind::Range { start, end } => {
|
||||
start.outer_exclusive_binder().max(end.outer_exclusive_binder())
|
||||
}
|
||||
ty::PatternKind::Or(pats) => {
|
||||
let mut idx = pats[0].outer_exclusive_binder();
|
||||
for pat in pats[1..].iter() {
|
||||
idx = idx.max(pat.outer_exclusive_binder());
|
||||
}
|
||||
idx
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -77,6 +91,19 @@ impl<'tcx> IrPrint<PatternKind<'tcx>> for TyCtxt<'tcx> {
|
|||
|
||||
write!(f, "..={end}")
|
||||
}
|
||||
PatternKind::Or(patterns) => {
|
||||
write!(f, "(")?;
|
||||
let mut first = true;
|
||||
for pat in patterns {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
write!(f, " | ")?;
|
||||
}
|
||||
write!(f, "{pat:?}")?;
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Pattern<'tcx> {
|
|||
a: Self,
|
||||
b: Self,
|
||||
) -> RelateResult<'tcx, Self> {
|
||||
let tcx = relation.cx();
|
||||
match (&*a, &*b) {
|
||||
(
|
||||
&ty::PatternKind::Range { start: start_a, end: end_a },
|
||||
|
|
@ -56,8 +57,17 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Pattern<'tcx> {
|
|||
) => {
|
||||
let start = relation.relate(start_a, start_b)?;
|
||||
let end = relation.relate(end_a, end_b)?;
|
||||
Ok(relation.cx().mk_pat(ty::PatternKind::Range { start, end }))
|
||||
Ok(tcx.mk_pat(ty::PatternKind::Range { start, end }))
|
||||
}
|
||||
(&ty::PatternKind::Or(a), &ty::PatternKind::Or(b)) => {
|
||||
if a.len() != b.len() {
|
||||
return Err(TypeError::Mismatch);
|
||||
}
|
||||
let v = iter::zip(a, b).map(|(a, b)| relation.relate(a, b));
|
||||
let patterns = tcx.mk_patterns_from_iter(v)?;
|
||||
Ok(tcx.mk_pat(ty::PatternKind::Or(patterns)))
|
||||
}
|
||||
(ty::PatternKind::Range { .. } | ty::PatternKind::Or(_), _) => Err(TypeError::Mismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ use rustc_type_ir::{ConstKind, TypeFolder, VisitorResult, try_visit};
|
|||
|
||||
use super::print::PrettyPrinter;
|
||||
use super::{GenericArg, GenericArgKind, Pattern, Region};
|
||||
use crate::infer::canonical::CanonicalVarInfos;
|
||||
use crate::mir::PlaceElem;
|
||||
use crate::ty::print::{FmtPrinter, Printer, with_no_trimmed_paths};
|
||||
use crate::ty::{
|
||||
|
|
@ -779,5 +778,5 @@ list_fold! {
|
|||
ty::Clauses<'tcx> : mk_clauses,
|
||||
&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> : mk_poly_existential_predicates,
|
||||
&'tcx ty::List<PlaceElem<'tcx>> : mk_place_elems,
|
||||
CanonicalVarInfos<'tcx> : mk_canonical_var_infos,
|
||||
&'tcx ty::List<ty::Pattern<'tcx>> : mk_patterns,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -716,6 +716,8 @@ pub type CanonicalUserTypeAnnotations<'tcx> =
|
|||
|
||||
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct CanonicalUserTypeAnnotation<'tcx> {
|
||||
#[type_foldable(identity)]
|
||||
#[type_visitable(ignore)]
|
||||
pub user_ty: Box<CanonicalUserType<'tcx>>,
|
||||
pub span: Span,
|
||||
pub inferred_ty: Ty<'tcx>,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_middle::mir::*;
|
|||
use rustc_middle::ty::{Ty, TyCtxt};
|
||||
use rustc_session::Session;
|
||||
|
||||
use crate::check_pointers::{BorrowCheckMode, PointerCheck, check_pointers};
|
||||
use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_pointers};
|
||||
|
||||
pub(super) struct CheckAlignment;
|
||||
|
||||
|
|
@ -19,15 +19,15 @@ impl<'tcx> crate::MirPass<'tcx> for CheckAlignment {
|
|||
// Skip trivially aligned place types.
|
||||
let excluded_pointees = [tcx.types.bool, tcx.types.i8, tcx.types.u8];
|
||||
|
||||
// We have to exclude borrows here: in `&x.field`, the exact
|
||||
// requirement is that the final reference must be aligned, but
|
||||
// `check_pointers` would check that `x` is aligned, which would be wrong.
|
||||
// When checking the alignment of references to field projections (`&(*ptr).a`),
|
||||
// we need to make sure that the reference is aligned according to the field type
|
||||
// and not to the pointer type.
|
||||
check_pointers(
|
||||
tcx,
|
||||
body,
|
||||
&excluded_pointees,
|
||||
insert_alignment_check,
|
||||
BorrowCheckMode::ExcludeBorrows,
|
||||
BorrowedFieldProjectionMode::FollowProjections,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use rustc_middle::mir::*;
|
|||
use rustc_middle::ty::{Ty, TyCtxt};
|
||||
use rustc_session::Session;
|
||||
|
||||
use crate::check_pointers::{BorrowCheckMode, PointerCheck, check_pointers};
|
||||
use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_pointers};
|
||||
|
||||
pub(super) struct CheckNull;
|
||||
|
||||
|
|
@ -14,7 +14,13 @@ impl<'tcx> crate::MirPass<'tcx> for CheckNull {
|
|||
}
|
||||
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
check_pointers(tcx, body, &[], insert_null_check, BorrowCheckMode::IncludeBorrows);
|
||||
check_pointers(
|
||||
tcx,
|
||||
body,
|
||||
&[],
|
||||
insert_null_check,
|
||||
BorrowedFieldProjectionMode::NoFollowProjections,
|
||||
);
|
||||
}
|
||||
|
||||
fn is_required(&self) -> bool {
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@ pub(crate) struct PointerCheck<'tcx> {
|
|||
pub(crate) assert_kind: Box<AssertKind<Operand<'tcx>>>,
|
||||
}
|
||||
|
||||
/// Indicates whether we insert the checks for borrow places of a raw pointer.
|
||||
/// Concretely places with [MutatingUseContext::Borrow] or
|
||||
/// [NonMutatingUseContext::SharedBorrow].
|
||||
/// When checking for borrows of field projections (`&(*ptr).a`), we might want
|
||||
/// to check for the field type (type of `.a` in the example). This enum defines
|
||||
/// the variations (pass the pointer [Ty] or the field [Ty]).
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum BorrowCheckMode {
|
||||
IncludeBorrows,
|
||||
ExcludeBorrows,
|
||||
pub(crate) enum BorrowedFieldProjectionMode {
|
||||
FollowProjections,
|
||||
NoFollowProjections,
|
||||
}
|
||||
|
||||
/// Utility for adding a check for read/write on every sized, raw pointer.
|
||||
|
|
@ -27,8 +27,8 @@ pub(crate) enum BorrowCheckMode {
|
|||
/// new basic block directly before the pointer access. (Read/write accesses
|
||||
/// are determined by the `PlaceContext` of the MIR visitor.) Then calls
|
||||
/// `on_finding` to insert the actual logic for a pointer check (e.g. check for
|
||||
/// alignment). A check can choose to be inserted for (mutable) borrows of
|
||||
/// raw pointers via the `borrow_check_mode` parameter.
|
||||
/// alignment). A check can choose to follow borrows of field projections via
|
||||
/// the `field_projection_mode` parameter.
|
||||
///
|
||||
/// This utility takes care of the right order of blocks, the only thing a
|
||||
/// caller must do in `on_finding` is:
|
||||
|
|
@ -45,7 +45,7 @@ pub(crate) fn check_pointers<'tcx, F>(
|
|||
body: &mut Body<'tcx>,
|
||||
excluded_pointees: &[Ty<'tcx>],
|
||||
on_finding: F,
|
||||
borrow_check_mode: BorrowCheckMode,
|
||||
field_projection_mode: BorrowedFieldProjectionMode,
|
||||
) where
|
||||
F: Fn(
|
||||
/* tcx: */ TyCtxt<'tcx>,
|
||||
|
|
@ -82,7 +82,7 @@ pub(crate) fn check_pointers<'tcx, F>(
|
|||
local_decls,
|
||||
typing_env,
|
||||
excluded_pointees,
|
||||
borrow_check_mode,
|
||||
field_projection_mode,
|
||||
);
|
||||
finder.visit_statement(statement, location);
|
||||
|
||||
|
|
@ -128,7 +128,7 @@ struct PointerFinder<'a, 'tcx> {
|
|||
typing_env: ty::TypingEnv<'tcx>,
|
||||
pointers: Vec<(Place<'tcx>, Ty<'tcx>, PlaceContext)>,
|
||||
excluded_pointees: &'a [Ty<'tcx>],
|
||||
borrow_check_mode: BorrowCheckMode,
|
||||
field_projection_mode: BorrowedFieldProjectionMode,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
|
||||
|
|
@ -137,7 +137,7 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
|
|||
local_decls: &'a mut LocalDecls<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
excluded_pointees: &'a [Ty<'tcx>],
|
||||
borrow_check_mode: BorrowCheckMode,
|
||||
field_projection_mode: BorrowedFieldProjectionMode,
|
||||
) -> Self {
|
||||
PointerFinder {
|
||||
tcx,
|
||||
|
|
@ -145,7 +145,7 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
|
|||
typing_env,
|
||||
excluded_pointees,
|
||||
pointers: Vec::new(),
|
||||
borrow_check_mode,
|
||||
field_projection_mode,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -163,15 +163,14 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
|
|||
MutatingUseContext::Store
|
||||
| MutatingUseContext::Call
|
||||
| MutatingUseContext::Yield
|
||||
| MutatingUseContext::Drop,
|
||||
| MutatingUseContext::Drop
|
||||
| MutatingUseContext::Borrow,
|
||||
) => true,
|
||||
PlaceContext::NonMutatingUse(
|
||||
NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
|
||||
NonMutatingUseContext::Copy
|
||||
| NonMutatingUseContext::Move
|
||||
| NonMutatingUseContext::SharedBorrow,
|
||||
) => true,
|
||||
PlaceContext::MutatingUse(MutatingUseContext::Borrow)
|
||||
| PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) => {
|
||||
matches!(self.borrow_check_mode, BorrowCheckMode::IncludeBorrows)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -183,19 +182,29 @@ impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
// Since Deref projections must come first and only once, the pointer for an indirect place
|
||||
// is the Local that the Place is based on.
|
||||
// Get the place and type we visit.
|
||||
let pointer = Place::from(place.local);
|
||||
let pointer_ty = self.local_decls[place.local].ty;
|
||||
let pointer_ty = pointer.ty(self.local_decls, self.tcx).ty;
|
||||
|
||||
// We only want to check places based on raw pointers
|
||||
if !pointer_ty.is_raw_ptr() {
|
||||
let &ty::RawPtr(mut pointee_ty, _) = pointer_ty.kind() else {
|
||||
trace!("Indirect, but not based on an raw ptr, not checking {:?}", place);
|
||||
return;
|
||||
};
|
||||
|
||||
// If we see a borrow of a field projection, we want to pass the field type to the
|
||||
// check and not the pointee type.
|
||||
if matches!(self.field_projection_mode, BorrowedFieldProjectionMode::FollowProjections)
|
||||
&& matches!(
|
||||
context,
|
||||
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
|
||||
| PlaceContext::MutatingUse(MutatingUseContext::Borrow)
|
||||
)
|
||||
{
|
||||
// Naturally, the field type is type of the initial place we look at.
|
||||
pointee_ty = place.ty(self.local_decls, self.tcx).ty;
|
||||
}
|
||||
|
||||
let pointee_ty =
|
||||
pointer_ty.builtin_deref(true).expect("no builtin_deref for an raw pointer");
|
||||
// Ideally we'd support this in the future, but for now we are limited to sized types.
|
||||
if !pointee_ty.is_sized(self.tcx, self.typing_env) {
|
||||
trace!("Raw pointer, but pointee is not known to be sized: {:?}", pointer_ty);
|
||||
|
|
@ -207,6 +216,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> {
|
|||
ty::Array(ty, _) => *ty,
|
||||
_ => pointee_ty,
|
||||
};
|
||||
// Check if we excluded this pointee type from the check.
|
||||
if self.excluded_pointees.contains(&element_ty) {
|
||||
trace!("Skipping pointer for type: {:?}", pointee_ty);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ where
|
|||
if self.tcx().features().async_drop()
|
||||
&& self.elaborator.body().coroutine.is_some()
|
||||
&& self.elaborator.allow_async_drops()
|
||||
&& !self.elaborator.body()[bb].is_cleanup
|
||||
&& !self.elaborator.patch_ref().block(self.elaborator.body(), bb).is_cleanup
|
||||
&& drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env())
|
||||
{
|
||||
self.build_async_drop(
|
||||
|
|
|
|||
|
|
@ -177,16 +177,8 @@ impl<'a> ConditionSet<'a> {
|
|||
arena: &'a DroplessArena,
|
||||
f: impl Fn(Condition) -> Option<Condition>,
|
||||
) -> Option<ConditionSet<'a>> {
|
||||
let mut all_ok = true;
|
||||
let set = arena.alloc_from_iter(self.iter().map_while(|c| {
|
||||
if let Some(c) = f(c) {
|
||||
Some(c)
|
||||
} else {
|
||||
all_ok = false;
|
||||
None
|
||||
}
|
||||
}));
|
||||
all_ok.then_some(ConditionSet(set))
|
||||
let set = arena.try_alloc_from_iter(self.iter().map(|c| f(c).ok_or(()))).ok()?;
|
||||
Some(ConditionSet(set))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -148,11 +148,20 @@ impl<'tcx> MirPatch<'tcx> {
|
|||
self.term_patch_map[bb].is_some()
|
||||
}
|
||||
|
||||
/// Universal getter for block data, either it is in 'old' blocks or in patched ones
|
||||
pub(crate) fn block<'a>(
|
||||
&'a self,
|
||||
body: &'a Body<'tcx>,
|
||||
bb: BasicBlock,
|
||||
) -> &'a BasicBlockData<'tcx> {
|
||||
match bb.index().checked_sub(body.basic_blocks.len()) {
|
||||
Some(new) => &self.new_blocks[new],
|
||||
None => &body[bb],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn terminator_loc(&self, body: &Body<'tcx>, bb: BasicBlock) -> Location {
|
||||
let offset = match bb.index().checked_sub(body.basic_blocks.len()) {
|
||||
Some(index) => self.new_blocks[index].statements.len(),
|
||||
None => body[bb].statements.len(),
|
||||
};
|
||||
let offset = self.block(body, bb).statements.len();
|
||||
Location { block: bb, statement_index: offset }
|
||||
}
|
||||
|
||||
|
|
@ -284,10 +293,7 @@ impl<'tcx> MirPatch<'tcx> {
|
|||
}
|
||||
|
||||
pub(crate) fn source_info_for_location(&self, body: &Body<'tcx>, loc: Location) -> SourceInfo {
|
||||
let data = match loc.block.index().checked_sub(body.basic_blocks.len()) {
|
||||
Some(new) => &self.new_blocks[new],
|
||||
None => &body[loc.block],
|
||||
};
|
||||
let data = self.block(body, loc.block);
|
||||
Self::source_info_for_index(data, loc)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -724,6 +724,9 @@ pub(in crate::solve) fn const_conditions_for_destruct<I: Interner>(
|
|||
let destruct_def_id = cx.require_lang_item(TraitSolverLangItem::Destruct);
|
||||
|
||||
match self_ty.kind() {
|
||||
// `ManuallyDrop` is trivially `~const Destruct` as we do not run any drop glue on it.
|
||||
ty::Adt(adt_def, _) if adt_def.is_manually_drop() => Ok(vec![]),
|
||||
|
||||
// An ADT is `~const Destruct` only if all of the fields are,
|
||||
// *and* if there is a `Drop` impl, that `Drop` impl is also `~const`.
|
||||
ty::Adt(adt_def, args) => {
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@
|
|||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![cfg_attr(bootstrap, feature(let_chains))]
|
||||
#![feature(array_windows)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(debug_closure_helpers)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(string_from_utf8_lossy_owned)]
|
||||
#![recursion_limit = "256"]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use rustc_ast as ast;
|
||||
use rustc_ast::token::{self, MetaVarKind};
|
||||
use rustc_ast::tokenstream::ParserRange;
|
||||
use rustc_ast::{Attribute, attr};
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{Diag, PResult};
|
||||
|
|
@ -8,8 +9,7 @@ use thin_vec::ThinVec;
|
|||
use tracing::debug;
|
||||
|
||||
use super::{
|
||||
AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, ParserRange, PathStyle, Trailing,
|
||||
UsePreAttrPos,
|
||||
AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle, Trailing, UsePreAttrPos,
|
||||
};
|
||||
use crate::{errors, exp, fluent_generated as fluent};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,18 @@
|
|||
use std::borrow::Cow;
|
||||
use std::{iter, mem};
|
||||
use std::mem;
|
||||
|
||||
use rustc_ast::token::{Delimiter, Token};
|
||||
use rustc_ast::token::Token;
|
||||
use rustc_ast::tokenstream::{
|
||||
AttrTokenStream, AttrTokenTree, AttrsTarget, DelimSpacing, DelimSpan, LazyAttrTokenStream,
|
||||
Spacing, ToAttrTokenStream,
|
||||
AttrsTarget, LazyAttrTokenStream, NodeRange, ParserRange, Spacing, TokenCursor,
|
||||
};
|
||||
use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, HasTokens};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::PResult;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::{DUMMY_SP, Span, sym};
|
||||
use rustc_span::{DUMMY_SP, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use super::{
|
||||
Capturing, FlatToken, ForceCollect, NodeRange, NodeReplacement, Parser, ParserRange,
|
||||
TokenCursor, Trailing,
|
||||
};
|
||||
use super::{Capturing, ForceCollect, Parser, Trailing};
|
||||
|
||||
// When collecting tokens, this fully captures the start point. Usually its
|
||||
// just after outer attributes, but occasionally it's before.
|
||||
|
|
@ -94,95 +91,6 @@ fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool {
|
|||
})
|
||||
}
|
||||
|
||||
// From a value of this type we can reconstruct the `TokenStream` seen by the
|
||||
// `f` callback passed to a call to `Parser::collect_tokens`, by
|
||||
// replaying the getting of the tokens. This saves us producing a `TokenStream`
|
||||
// if it is never needed, e.g. a captured `macro_rules!` argument that is never
|
||||
// passed to a proc macro. In practice, token stream creation happens rarely
|
||||
// compared to calls to `collect_tokens` (see some statistics in #78736) so we
|
||||
// are doing as little up-front work as possible.
|
||||
//
|
||||
// This also makes `Parser` very cheap to clone, since
|
||||
// there is no intermediate collection buffer to clone.
|
||||
struct LazyAttrTokenStreamImpl {
|
||||
start_token: (Token, Spacing),
|
||||
cursor_snapshot: TokenCursor,
|
||||
num_calls: u32,
|
||||
break_last_token: u32,
|
||||
node_replacements: Box<[NodeReplacement]>,
|
||||
}
|
||||
|
||||
impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
|
||||
fn to_attr_token_stream(&self) -> AttrTokenStream {
|
||||
// The token produced by the final call to `{,inlined_}next` was not
|
||||
// actually consumed by the callback. The combination of chaining the
|
||||
// initial token and using `take` produces the desired result - we
|
||||
// produce an empty `TokenStream` if no calls were made, and omit the
|
||||
// final token otherwise.
|
||||
let mut cursor_snapshot = self.cursor_snapshot.clone();
|
||||
let tokens = iter::once(FlatToken::Token(self.start_token))
|
||||
.chain(iter::repeat_with(|| FlatToken::Token(cursor_snapshot.next())))
|
||||
.take(self.num_calls as usize);
|
||||
|
||||
if self.node_replacements.is_empty() {
|
||||
make_attr_token_stream(tokens, self.break_last_token)
|
||||
} else {
|
||||
let mut tokens: Vec<_> = tokens.collect();
|
||||
let mut node_replacements = self.node_replacements.to_vec();
|
||||
node_replacements.sort_by_key(|(range, _)| range.0.start);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
for [(node_range, tokens), (next_node_range, next_tokens)] in
|
||||
node_replacements.array_windows()
|
||||
{
|
||||
assert!(
|
||||
node_range.0.end <= next_node_range.0.start
|
||||
|| node_range.0.end >= next_node_range.0.end,
|
||||
"Node ranges should be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})",
|
||||
node_range,
|
||||
tokens,
|
||||
next_node_range,
|
||||
next_tokens,
|
||||
);
|
||||
}
|
||||
|
||||
// Process the replace ranges, starting from the highest start
|
||||
// position and working our way back. If have tokens like:
|
||||
//
|
||||
// `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }`
|
||||
//
|
||||
// Then we will generate replace ranges for both
|
||||
// the `#[cfg(FALSE)] field: bool` and the entire
|
||||
// `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }`
|
||||
//
|
||||
// By starting processing from the replace range with the greatest
|
||||
// start position, we ensure that any (outer) replace range which
|
||||
// encloses another (inner) replace range will fully overwrite the
|
||||
// inner range's replacement.
|
||||
for (node_range, target) in node_replacements.into_iter().rev() {
|
||||
assert!(
|
||||
!node_range.0.is_empty(),
|
||||
"Cannot replace an empty node range: {:?}",
|
||||
node_range.0
|
||||
);
|
||||
|
||||
// Replace the tokens in range with zero or one `FlatToken::AttrsTarget`s, plus
|
||||
// enough `FlatToken::Empty`s to fill up the rest of the range. This keeps the
|
||||
// total length of `tokens` constant throughout the replacement process, allowing
|
||||
// us to do all replacements without adjusting indices.
|
||||
let target_len = target.is_some() as usize;
|
||||
tokens.splice(
|
||||
(node_range.0.start as usize)..(node_range.0.end as usize),
|
||||
target.into_iter().map(|target| FlatToken::AttrsTarget(target)).chain(
|
||||
iter::repeat(FlatToken::Empty).take(node_range.0.len() - target_len),
|
||||
),
|
||||
);
|
||||
}
|
||||
make_attr_token_stream(tokens.into_iter(), self.break_last_token)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
pub(super) fn collect_pos(&self) -> CollectPos {
|
||||
CollectPos {
|
||||
|
|
@ -387,10 +295,10 @@ impl<'a> Parser<'a> {
|
|||
|
||||
// This is hot enough for `deep-vector` that checking the conditions for an empty iterator
|
||||
// is measurably faster than actually executing the iterator.
|
||||
let node_replacements: Box<[_]> = if parser_replacements_start == parser_replacements_end
|
||||
let node_replacements = if parser_replacements_start == parser_replacements_end
|
||||
&& inner_attr_parser_replacements.is_empty()
|
||||
{
|
||||
Box::new([])
|
||||
ThinVec::new()
|
||||
} else {
|
||||
// Grab any replace ranges that occur *inside* the current AST node. Convert them
|
||||
// from `ParserRange` form to `NodeRange` form. We will perform the actual
|
||||
|
|
@ -429,13 +337,13 @@ impl<'a> Parser<'a> {
|
|||
// - `attrs`: includes the outer and the inner attr.
|
||||
// - `tokens`: lazy tokens for `g` (with its inner attr deleted).
|
||||
|
||||
let tokens = LazyAttrTokenStream::new(LazyAttrTokenStreamImpl {
|
||||
start_token: collect_pos.start_token,
|
||||
cursor_snapshot: collect_pos.cursor_snapshot,
|
||||
let tokens = LazyAttrTokenStream::new_pending(
|
||||
collect_pos.start_token,
|
||||
collect_pos.cursor_snapshot,
|
||||
num_calls,
|
||||
break_last_token: self.break_last_token,
|
||||
self.break_last_token,
|
||||
node_replacements,
|
||||
});
|
||||
);
|
||||
let mut tokens_used = false;
|
||||
|
||||
// If in "definite capture mode" we need to register a replace range
|
||||
|
|
@ -483,71 +391,6 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts a flattened iterator of tokens (including open and close delimiter tokens) into an
|
||||
/// `AttrTokenStream`, creating an `AttrTokenTree::Delimited` for each matching pair of open and
|
||||
/// close delims.
|
||||
fn make_attr_token_stream(
|
||||
iter: impl Iterator<Item = FlatToken>,
|
||||
break_last_token: u32,
|
||||
) -> AttrTokenStream {
|
||||
#[derive(Debug)]
|
||||
struct FrameData {
|
||||
// This is `None` for the first frame, `Some` for all others.
|
||||
open_delim_sp: Option<(Delimiter, Span, Spacing)>,
|
||||
inner: Vec<AttrTokenTree>,
|
||||
}
|
||||
// The stack always has at least one element. Storing it separately makes for shorter code.
|
||||
let mut stack_top = FrameData { open_delim_sp: None, inner: vec![] };
|
||||
let mut stack_rest = vec![];
|
||||
for flat_token in iter {
|
||||
match flat_token {
|
||||
FlatToken::Token((token @ Token { kind, span }, spacing)) => {
|
||||
if let Some(delim) = kind.open_delim() {
|
||||
stack_rest.push(mem::replace(
|
||||
&mut stack_top,
|
||||
FrameData { open_delim_sp: Some((delim, span, spacing)), inner: vec![] },
|
||||
));
|
||||
} else if let Some(delim) = kind.close_delim() {
|
||||
let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap());
|
||||
let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap();
|
||||
assert!(
|
||||
open_delim.eq_ignoring_invisible_origin(&delim),
|
||||
"Mismatched open/close delims: open={open_delim:?} close={span:?}"
|
||||
);
|
||||
let dspan = DelimSpan::from_pair(open_sp, span);
|
||||
let dspacing = DelimSpacing::new(open_spacing, spacing);
|
||||
let stream = AttrTokenStream::new(frame_data.inner);
|
||||
let delimited = AttrTokenTree::Delimited(dspan, dspacing, delim, stream);
|
||||
stack_top.inner.push(delimited);
|
||||
} else {
|
||||
stack_top.inner.push(AttrTokenTree::Token(token, spacing))
|
||||
}
|
||||
}
|
||||
FlatToken::AttrsTarget(target) => {
|
||||
stack_top.inner.push(AttrTokenTree::AttrsTarget(target))
|
||||
}
|
||||
FlatToken::Empty => {}
|
||||
}
|
||||
}
|
||||
|
||||
if break_last_token > 0 {
|
||||
let last_token = stack_top.inner.pop().unwrap();
|
||||
if let AttrTokenTree::Token(last_token, spacing) = last_token {
|
||||
let (unglued, _) = last_token.kind.break_two_token_op(break_last_token).unwrap();
|
||||
|
||||
// Tokens are always ASCII chars, so we can use byte arithmetic here.
|
||||
let mut first_span = last_token.span.shrink_to_lo();
|
||||
first_span =
|
||||
first_span.with_hi(first_span.lo() + rustc_span::BytePos(break_last_token));
|
||||
|
||||
stack_top.inner.push(AttrTokenTree::Token(Token::new(unglued, first_span), spacing));
|
||||
} else {
|
||||
panic!("Unexpected last token {last_token:?}")
|
||||
}
|
||||
}
|
||||
AttrTokenStream::new(stack_top.inner)
|
||||
}
|
||||
|
||||
/// Tokens are needed if:
|
||||
/// - any non-single-segment attributes (other than doc comments) are present,
|
||||
/// e.g. `rustfmt::skip`; or
|
||||
|
|
@ -562,14 +405,3 @@ fn needs_tokens(attrs: &[ast::Attribute]) -> bool {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Some types are used a lot. Make sure they don't unintentionally get bigger.
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
mod size_asserts {
|
||||
use rustc_data_structures::static_assert_size;
|
||||
|
||||
use super::*;
|
||||
// tidy-alphabetical-start
|
||||
static_assert_size!(LazyAttrTokenStreamImpl, 96);
|
||||
// tidy-alphabetical-end
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ pub mod token_type;
|
|||
mod ty;
|
||||
|
||||
use std::assert_matches::debug_assert_matches;
|
||||
use std::ops::Range;
|
||||
use std::{fmt, mem, slice};
|
||||
|
||||
use attr_wrapper::{AttrWrapper, UsePreAttrPos};
|
||||
|
|
@ -25,7 +24,9 @@ use rustc_ast::ptr::P;
|
|||
use rustc_ast::token::{
|
||||
self, IdentIsRaw, InvisibleOrigin, MetaVarKind, NtExprKind, NtPatKind, Token, TokenKind,
|
||||
};
|
||||
use rustc_ast::tokenstream::{AttrsTarget, Spacing, TokenStream, TokenTree};
|
||||
use rustc_ast::tokenstream::{
|
||||
ParserRange, ParserReplacement, Spacing, TokenCursor, TokenStream, TokenTree, TokenTreeCursor,
|
||||
};
|
||||
use rustc_ast::util::case::Case;
|
||||
use rustc_ast::{
|
||||
self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID,
|
||||
|
|
@ -37,7 +38,7 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use rustc_errors::{Applicability, Diag, FatalError, MultiSpan, PResult};
|
||||
use rustc_index::interval::IntervalSet;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
|
||||
use rustc_span::{Ident, Span, Symbol, kw, sym};
|
||||
use thin_vec::ThinVec;
|
||||
use token_type::TokenTypeSet;
|
||||
pub use token_type::{ExpKeywordPair, ExpTokenPair, TokenType};
|
||||
|
|
@ -55,19 +56,64 @@ mod tests;
|
|||
mod tokenstream {
|
||||
mod tests;
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod mut_visit {
|
||||
mod tests;
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// Restrictions applied while parsing.
|
||||
///
|
||||
/// The parser maintains a bitset of restrictions it will honor while
|
||||
/// parsing. This is essentially used as a way of tracking state of what
|
||||
/// is being parsed and to change behavior based on that.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Restrictions: u8 {
|
||||
/// Restricts expressions for use in statement position.
|
||||
///
|
||||
/// When expressions are used in various places, like statements or
|
||||
/// match arms, this is used to stop parsing once certain tokens are
|
||||
/// reached.
|
||||
///
|
||||
/// For example, `if true {} & 1` with `STMT_EXPR` in effect is parsed
|
||||
/// as two separate expression statements (`if` and a reference to 1).
|
||||
/// Otherwise it is parsed as a bitwise AND where `if` is on the left
|
||||
/// and 1 is on the right.
|
||||
const STMT_EXPR = 1 << 0;
|
||||
/// Do not allow struct literals.
|
||||
///
|
||||
/// There are several places in the grammar where we don't want to
|
||||
/// allow struct literals because they can require lookahead, or
|
||||
/// otherwise could be ambiguous or cause confusion. For example,
|
||||
/// `if Foo {} {}` isn't clear if it is `Foo{}` struct literal, or
|
||||
/// just `Foo` is the condition, followed by a consequent block,
|
||||
/// followed by an empty block.
|
||||
///
|
||||
/// See [RFC 92](https://rust-lang.github.io/rfcs/0092-struct-grammar.html).
|
||||
const NO_STRUCT_LITERAL = 1 << 1;
|
||||
/// Used to provide better error messages for const generic arguments.
|
||||
///
|
||||
/// An un-braced const generic argument is limited to a very small
|
||||
/// subset of expressions. This is used to detect the situation where
|
||||
/// an expression outside of that subset is used, and to suggest to
|
||||
/// wrap the expression in braces.
|
||||
const CONST_EXPR = 1 << 2;
|
||||
/// Allows `let` expressions.
|
||||
///
|
||||
/// `let pattern = scrutinee` is parsed as an expression, but it is
|
||||
/// only allowed in let chains (`if` and `while` conditions).
|
||||
/// Otherwise it is not an expression (note that `let` in statement
|
||||
/// positions is treated as a `StmtKind::Let` statement, which has a
|
||||
/// slightly different grammar).
|
||||
const ALLOW_LET = 1 << 3;
|
||||
/// Used to detect a missing `=>` in a match guard.
|
||||
///
|
||||
/// This is used for error handling in a match guard to give a better
|
||||
/// error message if the `=>` is missing. It is set when parsing the
|
||||
/// guard expression.
|
||||
const IN_IF_GUARD = 1 << 4;
|
||||
/// Used to detect the incorrect use of expressions in patterns.
|
||||
///
|
||||
/// This is used for error handling while parsing a pattern. During
|
||||
/// error recovery, this will be set to try to parse the pattern as an
|
||||
/// expression, but halts parsing the expression when reaching certain
|
||||
/// tokens like `=`.
|
||||
const IS_PAT = 1 << 5;
|
||||
}
|
||||
}
|
||||
|
|
@ -187,57 +233,6 @@ struct ClosureSpans {
|
|||
body: Span,
|
||||
}
|
||||
|
||||
/// A token range within a `Parser`'s full token stream.
|
||||
#[derive(Clone, Debug)]
|
||||
struct ParserRange(Range<u32>);
|
||||
|
||||
/// A token range within an individual AST node's (lazy) token stream, i.e.
|
||||
/// relative to that node's first token. Distinct from `ParserRange` so the two
|
||||
/// kinds of range can't be mixed up.
|
||||
#[derive(Clone, Debug)]
|
||||
struct NodeRange(Range<u32>);
|
||||
|
||||
/// Indicates a range of tokens that should be replaced by an `AttrsTarget`
|
||||
/// (replacement) or be replaced by nothing (deletion). This is used in two
|
||||
/// places during token collection.
|
||||
///
|
||||
/// 1. Replacement. During the parsing of an AST node that may have a
|
||||
/// `#[derive]` attribute, when we parse a nested AST node that has `#[cfg]`
|
||||
/// or `#[cfg_attr]`, we replace the entire inner AST node with
|
||||
/// `FlatToken::AttrsTarget`. This lets us perform eager cfg-expansion on an
|
||||
/// `AttrTokenStream`.
|
||||
///
|
||||
/// 2. Deletion. We delete inner attributes from all collected token streams,
|
||||
/// and instead track them through the `attrs` field on the AST node. This
|
||||
/// lets us manipulate them similarly to outer attributes. When we create a
|
||||
/// `TokenStream`, the inner attributes are inserted into the proper place
|
||||
/// in the token stream.
|
||||
///
|
||||
/// Each replacement starts off in `ParserReplacement` form but is converted to
|
||||
/// `NodeReplacement` form when it is attached to a single AST node, via
|
||||
/// `LazyAttrTokenStreamImpl`.
|
||||
type ParserReplacement = (ParserRange, Option<AttrsTarget>);
|
||||
|
||||
/// See the comment on `ParserReplacement`.
|
||||
type NodeReplacement = (NodeRange, Option<AttrsTarget>);
|
||||
|
||||
impl NodeRange {
|
||||
// Converts a range within a parser's tokens to a range within a
|
||||
// node's tokens beginning at `start_pos`.
|
||||
//
|
||||
// For example, imagine a parser with 50 tokens in its token stream, a
|
||||
// function that spans `ParserRange(20..40)` and an inner attribute within
|
||||
// that function that spans `ParserRange(30..35)`. We would find the inner
|
||||
// attribute's range within the function's tokens by subtracting 20, which
|
||||
// is the position of the function's start token. This gives
|
||||
// `NodeRange(10..15)`.
|
||||
fn new(ParserRange(parser_range): ParserRange, start_pos: u32) -> NodeRange {
|
||||
assert!(!parser_range.is_empty());
|
||||
assert!(parser_range.start >= start_pos);
|
||||
NodeRange((parser_range.start - start_pos)..(parser_range.end - start_pos))
|
||||
}
|
||||
}
|
||||
|
||||
/// Controls how we capture tokens. Capturing can be expensive,
|
||||
/// so we try to avoid performing capturing in cases where
|
||||
/// we will never need an `AttrTokenStream`.
|
||||
|
|
@ -260,104 +255,6 @@ struct CaptureState {
|
|||
seen_attrs: IntervalSet<AttrId>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct TokenTreeCursor {
|
||||
stream: TokenStream,
|
||||
/// Points to the current token tree in the stream. In `TokenCursor::curr`,
|
||||
/// this can be any token tree. In `TokenCursor::stack`, this is always a
|
||||
/// `TokenTree::Delimited`.
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl TokenTreeCursor {
|
||||
#[inline]
|
||||
fn new(stream: TokenStream) -> Self {
|
||||
TokenTreeCursor { stream, index: 0 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn curr(&self) -> Option<&TokenTree> {
|
||||
self.stream.get(self.index)
|
||||
}
|
||||
|
||||
fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
|
||||
self.stream.get(self.index + n)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bump(&mut self) {
|
||||
self.index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// A `TokenStream` cursor that produces `Token`s. It's a bit odd that
|
||||
/// we (a) lex tokens into a nice tree structure (`TokenStream`), and then (b)
|
||||
/// use this type to emit them as a linear sequence. But a linear sequence is
|
||||
/// what the parser expects, for the most part.
|
||||
#[derive(Clone, Debug)]
|
||||
struct TokenCursor {
|
||||
// Cursor for the current (innermost) token stream. The index within the
|
||||
// cursor can point to any token tree in the stream (or one past the end).
|
||||
// The delimiters for this token stream are found in `self.stack.last()`;
|
||||
// if that is `None` we are in the outermost token stream which never has
|
||||
// delimiters.
|
||||
curr: TokenTreeCursor,
|
||||
|
||||
// Token streams surrounding the current one. The index within each cursor
|
||||
// always points to a `TokenTree::Delimited`.
|
||||
stack: Vec<TokenTreeCursor>,
|
||||
}
|
||||
|
||||
impl TokenCursor {
|
||||
fn next(&mut self) -> (Token, Spacing) {
|
||||
self.inlined_next()
|
||||
}
|
||||
|
||||
/// This always-inlined version should only be used on hot code paths.
|
||||
#[inline(always)]
|
||||
fn inlined_next(&mut self) -> (Token, Spacing) {
|
||||
loop {
|
||||
// FIXME: we currently don't return `Delimiter::Invisible` open/close delims. To fix
|
||||
// #67062 we will need to, whereupon the `delim != Delimiter::Invisible` conditions
|
||||
// below can be removed.
|
||||
if let Some(tree) = self.curr.curr() {
|
||||
match tree {
|
||||
&TokenTree::Token(token, spacing) => {
|
||||
debug_assert!(!token.kind.is_delim());
|
||||
let res = (token, spacing);
|
||||
self.curr.bump();
|
||||
return res;
|
||||
}
|
||||
&TokenTree::Delimited(sp, spacing, delim, ref tts) => {
|
||||
let trees = TokenTreeCursor::new(tts.clone());
|
||||
self.stack.push(mem::replace(&mut self.curr, trees));
|
||||
if !delim.skip() {
|
||||
return (Token::new(delim.as_open_token_kind(), sp.open), spacing.open);
|
||||
}
|
||||
// No open delimiter to return; continue on to the next iteration.
|
||||
}
|
||||
};
|
||||
} else if let Some(parent) = self.stack.pop() {
|
||||
// We have exhausted this token stream. Move back to its parent token stream.
|
||||
let Some(&TokenTree::Delimited(span, spacing, delim, _)) = parent.curr() else {
|
||||
panic!("parent should be Delimited")
|
||||
};
|
||||
self.curr = parent;
|
||||
self.curr.bump(); // move past the `Delimited`
|
||||
if !delim.skip() {
|
||||
return (Token::new(delim.as_close_token_kind(), span.close), spacing.close);
|
||||
}
|
||||
// No close delimiter to return; continue on to the next iteration.
|
||||
} else {
|
||||
// We have exhausted the outermost token stream. The use of
|
||||
// `Spacing::Alone` is arbitrary and immaterial, because the
|
||||
// `Eof` token's spacing is never used.
|
||||
return (Token::new(token::Eof, DUMMY_SP), Spacing::Alone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A sequence separator.
|
||||
#[derive(Debug)]
|
||||
struct SeqSep<'a> {
|
||||
|
|
@ -1742,26 +1639,6 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A helper struct used when building an `AttrTokenStream` from
|
||||
/// a `LazyAttrTokenStream`. Both delimiter and non-delimited tokens
|
||||
/// are stored as `FlatToken::Token`. A vector of `FlatToken`s
|
||||
/// is then 'parsed' to build up an `AttrTokenStream` with nested
|
||||
/// `AttrTokenTree::Delimited` tokens.
|
||||
#[derive(Debug, Clone)]
|
||||
enum FlatToken {
|
||||
/// A token - this holds both delimiter (e.g. '{' and '}')
|
||||
/// and non-delimiter tokens
|
||||
Token((Token, Spacing)),
|
||||
/// Holds the `AttrsTarget` for an AST node. The `AttrsTarget` is inserted
|
||||
/// directly into the constructed `AttrTokenStream` as an
|
||||
/// `AttrTokenTree::AttrsTarget`.
|
||||
AttrsTarget(AttrsTarget),
|
||||
/// A special 'empty' token that is ignored during the conversion
|
||||
/// to an `AttrTokenStream`. This is used to simplify the
|
||||
/// handling of replace ranges.
|
||||
Empty,
|
||||
}
|
||||
|
||||
// Metavar captures of various kinds.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ParseNtResult {
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
use rustc_ast as ast;
|
||||
use rustc_ast::mut_visit::MutVisitor;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_span::{Ident, create_default_session_globals_then};
|
||||
|
||||
use crate::parser::tests::{matches_codepattern, string_to_crate};
|
||||
|
||||
// This version doesn't care about getting comments or doc-strings in.
|
||||
fn print_crate_items(krate: &ast::Crate) -> String {
|
||||
krate.items.iter().map(|i| pprust::item_to_string(i)).collect::<Vec<_>>().join(" ")
|
||||
}
|
||||
|
||||
// Change every identifier to "zz".
|
||||
struct ToZzIdentMutVisitor;
|
||||
|
||||
impl MutVisitor for ToZzIdentMutVisitor {
|
||||
const VISIT_TOKENS: bool = true;
|
||||
|
||||
fn visit_ident(&mut self, ident: &mut Ident) {
|
||||
*ident = Ident::from_str("zz");
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! assert_matches_codepattern {
|
||||
($a:expr , $b:expr) => {{
|
||||
let a_val = $a;
|
||||
let b_val = $b;
|
||||
if !matches_codepattern(&a_val, &b_val) {
|
||||
panic!("expected args satisfying `matches_codepattern`, got {} and {}", a_val, b_val);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
// Make sure idents get transformed everywhere.
|
||||
#[test]
|
||||
fn ident_transformation() {
|
||||
create_default_session_globals_then(|| {
|
||||
let mut zz_visitor = ToZzIdentMutVisitor;
|
||||
let mut krate =
|
||||
string_to_crate("#[a] mod b {fn c (d : e, f : g) {h!(i,j,k);l;m}}".to_string());
|
||||
zz_visitor.visit_crate(&mut krate);
|
||||
assert_matches_codepattern!(
|
||||
print_crate_items(&krate),
|
||||
"#[zz]mod zz{fn zz(zz:zz,zz:zz){zz!(zz,zz,zz);zz;zz}}".to_string()
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
// Make sure idents get transformed even inside macro defs.
|
||||
#[test]
|
||||
fn ident_transformation_in_defs() {
|
||||
create_default_session_globals_then(|| {
|
||||
let mut zz_visitor = ToZzIdentMutVisitor;
|
||||
let mut krate = string_to_crate(
|
||||
"macro_rules! a {(b $c:expr $(d $e:token)f+ => \
|
||||
(g $(d $d $e)+))} "
|
||||
.to_string(),
|
||||
);
|
||||
zz_visitor.visit_crate(&mut krate);
|
||||
assert_matches_codepattern!(
|
||||
print_crate_items(&krate),
|
||||
"macro_rules! zz{(zz$zz:zz$(zz $zz:zz)zz+=>(zz$(zz$zz$zz)+))}".to_string()
|
||||
);
|
||||
})
|
||||
}
|
||||
|
|
@ -95,12 +95,6 @@ pub(crate) fn string_to_stream(source_str: String) -> TokenStream {
|
|||
))
|
||||
}
|
||||
|
||||
/// Parses a string, returns a crate.
|
||||
pub(crate) fn string_to_crate(source_str: String) -> ast::Crate {
|
||||
let psess = psess();
|
||||
with_error_checking_parse(source_str, &psess, |p| p.parse_crate_mod())
|
||||
}
|
||||
|
||||
/// Does the given string match the pattern? whitespace in the first string
|
||||
/// may be deleted or replaced with other whitespace to match the pattern.
|
||||
/// This function is relatively Unicode-ignorant; fortunately, the careful design
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ pub fn query_system<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
rustc_middle::rustc_query_append! { define_queries! }
|
||||
rustc_middle::rustc_with_all_queries! { define_queries! }
|
||||
|
||||
pub fn provide(providers: &mut rustc_middle::util::Providers) {
|
||||
providers.hooks.alloc_self_profile_query_strings = alloc_self_profile_query_strings;
|
||||
|
|
|
|||
|
|
@ -575,11 +575,14 @@ where
|
|||
}
|
||||
|
||||
// NOTE: `$V` isn't used here, but we still need to match on it so it can be passed to other macros
|
||||
// invoked by `rustc_query_append`.
|
||||
// invoked by `rustc_with_all_queries`.
|
||||
macro_rules! define_queries {
|
||||
(
|
||||
$($(#[$attr:meta])*
|
||||
[$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => {
|
||||
$(
|
||||
$(#[$attr:meta])*
|
||||
[$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,
|
||||
)*
|
||||
) => {
|
||||
|
||||
pub(crate) mod query_impl { $(pub(crate) mod $name {
|
||||
use super::super::*;
|
||||
|
|
|
|||
|
|
@ -958,6 +958,11 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
|
|||
self.resolve_anon_const(end, AnonConstKind::ConstArg(IsRepeatExpr::No));
|
||||
}
|
||||
}
|
||||
TyPatKind::Or(patterns) => {
|
||||
for pat in patterns {
|
||||
self.visit_ty_pat(pat)
|
||||
}
|
||||
}
|
||||
TyPatKind::Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#![feature(iter_intersperse)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![recursion_limit = "256"]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
|
|
|||
|
|
@ -412,6 +412,7 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> {
|
|||
end: Some(end.stable(tables)),
|
||||
include_end: true,
|
||||
},
|
||||
ty::PatternKind::Or(_) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,6 +253,22 @@ impl<'tcx> SymbolMangler<'tcx> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_pat(&mut self, pat: ty::Pattern<'tcx>) -> Result<(), std::fmt::Error> {
|
||||
Ok(match *pat {
|
||||
ty::PatternKind::Range { start, end } => {
|
||||
let consts = [start, end];
|
||||
for ct in consts {
|
||||
Ty::new_array_with_const_len(self.tcx, self.tcx.types.unit, ct).print(self)?;
|
||||
}
|
||||
}
|
||||
ty::PatternKind::Or(patterns) => {
|
||||
for pat in patterns {
|
||||
self.print_pat(pat)?;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
|
||||
|
|
@ -469,20 +485,14 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
|
|||
ty.print(self)?;
|
||||
}
|
||||
|
||||
ty::Pat(ty, pat) => match *pat {
|
||||
ty::PatternKind::Range { start, end } => {
|
||||
let consts = [start, end];
|
||||
// HACK: Represent as tuple until we have something better.
|
||||
// HACK: constants are used in arrays, even if the types don't match.
|
||||
self.push("T");
|
||||
ty.print(self)?;
|
||||
for ct in consts {
|
||||
Ty::new_array_with_const_len(self.tcx, self.tcx.types.unit, ct)
|
||||
.print(self)?;
|
||||
}
|
||||
self.push("E");
|
||||
}
|
||||
},
|
||||
ty::Pat(ty, pat) => {
|
||||
// HACK: Represent as tuple until we have something better.
|
||||
// HACK: constants are used in arrays, even if the types don't match.
|
||||
self.push("T");
|
||||
ty.print(self)?;
|
||||
self.print_pat(pat)?;
|
||||
self.push("E");
|
||||
}
|
||||
|
||||
ty::Array(ty, len) => {
|
||||
self.push("A");
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ pub(crate) fn target() -> Target {
|
|||
max_atomic_width: Some(32),
|
||||
mcount: "\u{1}__gnu_mcount_nc".into(),
|
||||
has_thumb_interworking: true,
|
||||
llvm_mcount_intrinsic: Some("llvm.arm.gnu.eabi.mcount".into()),
|
||||
..base::linux_gnu::opts()
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ pub(crate) fn target() -> Target {
|
|||
features: "+v7,+vfp3,-d32,+thumb2,-neon".into(),
|
||||
max_atomic_width: Some(64),
|
||||
mcount: "\u{1}__gnu_mcount_nc".into(),
|
||||
llvm_mcount_intrinsic: Some("llvm.arm.gnu.eabi.mcount".into()),
|
||||
..base::linux_gnu::opts()
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ rustc_parse_format = { path = "../rustc_parse_format" }
|
|||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] }
|
||||
rustc_type_ir = { path = "../rustc_type_ir" }
|
||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
||||
thin-vec = "0.2"
|
||||
tracing = "0.1"
|
||||
|
|
|
|||
|
|
@ -15,10 +15,9 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
|
|||
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer};
|
||||
use rustc_middle::ty::{
|
||||
self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Term, TermKind,
|
||||
Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults,
|
||||
Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, TypeckResults,
|
||||
};
|
||||
use rustc_span::{BytePos, DUMMY_SP, FileName, Ident, Span, sym};
|
||||
use rustc_type_ir::TypeVisitableExt;
|
||||
use tracing::{debug, instrument, warn};
|
||||
|
||||
use super::nice_region_error::placeholder_error::Highlighted;
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@ use rustc_hir::{self as hir, ParamName};
|
|||
use rustc_middle::bug;
|
||||
use rustc_middle::traits::ObligationCauseCode;
|
||||
use rustc_middle::ty::error::TypeError;
|
||||
use rustc_middle::ty::{self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _};
|
||||
use rustc_middle::ty::{
|
||||
self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _, Upcast as _,
|
||||
};
|
||||
use rustc_span::{BytePos, ErrorGuaranteed, Span, Symbol, kw};
|
||||
use rustc_type_ir::Upcast as _;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use super::ObligationCauseAsDiagArg;
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@ use rustc_hir::def::Namespace;
|
|||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_infer::traits::{Obligation, PredicateObligation};
|
||||
use rustc_middle::ty::print::{FmtPrinter, Print};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_middle::ty::{self, TyCtxt, Upcast};
|
||||
use rustc_session::Limit;
|
||||
use rustc_span::Span;
|
||||
use rustc_type_ir::Upcast;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::error_reporting::TypeErrCtxt;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,6 @@ pub use fulfill::{FulfillmentCtxt, NextSolverError};
|
|||
pub(crate) use normalize::deeply_normalize_for_diagnostics;
|
||||
pub use normalize::{
|
||||
deeply_normalize, deeply_normalize_with_skipped_universes,
|
||||
deeply_normalize_with_skipped_universes_and_ambiguous_goals,
|
||||
deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals,
|
||||
};
|
||||
pub use select::InferCtxtSelectExt;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue