Support the new lowering of format_args!()
This commit is contained in:
parent
fd8f92d94a
commit
c0bf494d24
4 changed files with 523 additions and 105 deletions
|
|
@ -432,6 +432,8 @@ pub struct ExprCollector<'db> {
|
|||
|
||||
awaitable_context: Option<Awaitable>,
|
||||
krate: base_db::Crate,
|
||||
|
||||
name_generator_index: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -537,9 +539,16 @@ impl<'db> ExprCollector<'db> {
|
|||
current_block_legacy_macro_defs_count: FxHashMap::default(),
|
||||
outer_impl_trait: false,
|
||||
krate,
|
||||
name_generator_index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_new_name(&mut self) -> Name {
|
||||
let index = self.name_generator_index;
|
||||
self.name_generator_index += 1;
|
||||
Name::generate_new_name(index)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn lang_items(&self) -> &'db LangItems {
|
||||
self.lang_items.get_or_init(|| crate::lang_item::lang_items(self.db, self.def_map.krate()))
|
||||
|
|
@ -1638,9 +1647,8 @@ impl<'db> ExprCollector<'db> {
|
|||
/// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
|
||||
fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId {
|
||||
let try_from_output = self.lang_path(self.lang_items().TryTraitFromOutput);
|
||||
let label = self.alloc_label_desugared(Label {
|
||||
name: Name::generate_new_name(self.store.labels.len()),
|
||||
});
|
||||
let label = self.generate_new_name();
|
||||
let label = self.alloc_label_desugared(Label { name: label });
|
||||
let old_label = self.current_try_block_label.replace(label);
|
||||
|
||||
let ptr = AstPtr::new(&e).upcast();
|
||||
|
|
@ -1768,7 +1776,7 @@ impl<'db> ExprCollector<'db> {
|
|||
this.collect_expr_opt(e.loop_body().map(|it| it.into()))
|
||||
}),
|
||||
};
|
||||
let iter_name = Name::generate_new_name(self.store.exprs.len());
|
||||
let iter_name = self.generate_new_name();
|
||||
let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr);
|
||||
let iter_expr_mut = self.alloc_expr(
|
||||
Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
|
||||
|
|
@ -1829,7 +1837,7 @@ impl<'db> ExprCollector<'db> {
|
|||
let try_branch = self.alloc_expr(try_branch.map_or(Expr::Missing, Expr::Path), syntax_ptr);
|
||||
let expr = self
|
||||
.alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr);
|
||||
let continue_name = Name::generate_new_name(self.store.bindings.len());
|
||||
let continue_name = self.generate_new_name();
|
||||
let continue_binding = self.alloc_binding(
|
||||
continue_name.clone(),
|
||||
BindingAnnotation::Unannotated,
|
||||
|
|
@ -1847,7 +1855,7 @@ impl<'db> ExprCollector<'db> {
|
|||
guard: None,
|
||||
expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr),
|
||||
};
|
||||
let break_name = Name::generate_new_name(self.store.bindings.len());
|
||||
let break_name = self.generate_new_name();
|
||||
let break_binding =
|
||||
self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated, HygieneId::ROOT);
|
||||
let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
|
||||
|
|
@ -2628,9 +2636,17 @@ impl<'db> ExprCollector<'db> {
|
|||
fn ty_rel_lang_path(
|
||||
&self,
|
||||
lang: Option<impl Into<LangItemTarget>>,
|
||||
relative_name: Name,
|
||||
relative_name: Symbol,
|
||||
) -> Option<Path> {
|
||||
Some(Path::LangItem(lang?.into(), Some(relative_name)))
|
||||
Some(Path::LangItem(lang?.into(), Some(Name::new_symbol_root(relative_name))))
|
||||
}
|
||||
|
||||
fn ty_rel_lang_path_expr(
|
||||
&self,
|
||||
lang: Option<impl Into<LangItemTarget>>,
|
||||
relative_name: Symbol,
|
||||
) -> Expr {
|
||||
self.ty_rel_lang_path(lang, relative_name).map_or(Expr::Missing, Expr::Path)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use crate::{
|
|||
FormatPlaceholder, FormatSign, FormatTrait,
|
||||
},
|
||||
},
|
||||
lang_item::LangItemTarget,
|
||||
type_ref::{Mutability, Rawness},
|
||||
};
|
||||
|
||||
|
|
@ -94,6 +95,347 @@ impl<'db> ExprCollector<'db> {
|
|||
),
|
||||
};
|
||||
|
||||
let idx = if self.lang_items().FormatCount.is_none() {
|
||||
self.collect_format_args_after_1_93_0_impl(syntax_ptr, fmt)
|
||||
} else {
|
||||
self.collect_format_args_before_1_93_0_impl(syntax_ptr, fmt)
|
||||
};
|
||||
|
||||
self.store
|
||||
.template_map
|
||||
.get_or_insert_with(Default::default)
|
||||
.format_args_to_captures
|
||||
.insert(idx, (hygiene, mappings));
|
||||
idx
|
||||
}
|
||||
|
||||
fn collect_format_args_after_1_93_0_impl(
|
||||
&mut self,
|
||||
syntax_ptr: AstPtr<ast::Expr>,
|
||||
fmt: FormatArgs,
|
||||
) -> ExprId {
|
||||
let lang_items = self.lang_items();
|
||||
|
||||
// Create a list of all _unique_ (argument, format trait) combinations.
|
||||
// E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
|
||||
//
|
||||
// We use usize::MAX for arguments that don't exist, because that can never be a valid index
|
||||
// into the arguments array.
|
||||
let mut argmap = FxIndexSet::default();
|
||||
|
||||
let mut incomplete_lit = String::new();
|
||||
|
||||
let mut implicit_arg_index = 0;
|
||||
|
||||
let mut bytecode = Vec::new();
|
||||
|
||||
let template = if fmt.template.is_empty() {
|
||||
// Treat empty templates as a single literal piece (with an empty string),
|
||||
// so we produce `from_str("")` for those.
|
||||
&[FormatArgsPiece::Literal(sym::__empty)][..]
|
||||
} else {
|
||||
&fmt.template[..]
|
||||
};
|
||||
|
||||
// See library/core/src/fmt/mod.rs for the format string encoding format.
|
||||
|
||||
for (i, piece) in template.iter().enumerate() {
|
||||
match piece {
|
||||
FormatArgsPiece::Literal(sym) => {
|
||||
// Coalesce adjacent literal pieces.
|
||||
if let Some(FormatArgsPiece::Literal(_)) = template.get(i + 1) {
|
||||
incomplete_lit.push_str(sym.as_str());
|
||||
continue;
|
||||
}
|
||||
let mut s = if incomplete_lit.is_empty() {
|
||||
sym.as_str()
|
||||
} else {
|
||||
incomplete_lit.push_str(sym.as_str());
|
||||
&incomplete_lit
|
||||
};
|
||||
|
||||
// If this is the last piece and was the only piece, that means
|
||||
// there are no placeholders and the entire format string is just a literal.
|
||||
//
|
||||
// In that case, we can just use `from_str`.
|
||||
if i + 1 == template.len() && bytecode.is_empty() {
|
||||
// Generate:
|
||||
// <core::fmt::Arguments>::from_str("meow")
|
||||
let from_str = self.ty_rel_lang_path_desugared_expr(
|
||||
lang_items.FormatArguments,
|
||||
sym::from_str,
|
||||
);
|
||||
let sym =
|
||||
if incomplete_lit.is_empty() { sym.clone() } else { Symbol::intern(s) };
|
||||
let s = self.alloc_expr_desugared(Expr::Literal(Literal::String(sym)));
|
||||
let from_str = self.alloc_expr(
|
||||
Expr::Call { callee: from_str, args: Box::new([s]) },
|
||||
syntax_ptr,
|
||||
);
|
||||
return if !fmt.arguments.arguments.is_empty() {
|
||||
// With an incomplete format string (e.g. only an opening `{`), it's possible for `arguments`
|
||||
// to be non-empty when reaching this code path.
|
||||
self.alloc_expr(
|
||||
Expr::Block {
|
||||
id: None,
|
||||
statements: fmt
|
||||
.arguments
|
||||
.arguments
|
||||
.iter()
|
||||
.map(|arg| Statement::Expr {
|
||||
expr: arg.expr,
|
||||
has_semi: true,
|
||||
})
|
||||
.collect(),
|
||||
tail: Some(from_str),
|
||||
label: None,
|
||||
},
|
||||
syntax_ptr,
|
||||
)
|
||||
} else {
|
||||
from_str
|
||||
};
|
||||
}
|
||||
|
||||
// Encode the literal in chunks of up to u16::MAX bytes, split at utf-8 boundaries.
|
||||
while !s.is_empty() {
|
||||
let len = s.floor_char_boundary(usize::from(u16::MAX));
|
||||
if len < 0x80 {
|
||||
bytecode.push(len as u8);
|
||||
} else {
|
||||
bytecode.push(0x80);
|
||||
bytecode.extend_from_slice(&(len as u16).to_le_bytes());
|
||||
}
|
||||
bytecode.extend(&s.as_bytes()[..len]);
|
||||
s = &s[len..];
|
||||
}
|
||||
|
||||
incomplete_lit.clear();
|
||||
}
|
||||
FormatArgsPiece::Placeholder(p) => {
|
||||
// Push the start byte and remember its index so we can set the option bits later.
|
||||
let i = bytecode.len();
|
||||
bytecode.push(0xC0);
|
||||
|
||||
let position = match &p.argument.index {
|
||||
&Ok(it) => it,
|
||||
Err(_) => usize::MAX,
|
||||
};
|
||||
let position = argmap
|
||||
.insert_full((position, ArgumentType::Format(p.format_trait)))
|
||||
.0 as u64;
|
||||
|
||||
// This needs to match the constants in library/core/src/fmt/mod.rs.
|
||||
let o = &p.format_options;
|
||||
let align = match o.alignment {
|
||||
Some(FormatAlignment::Left) => 0,
|
||||
Some(FormatAlignment::Right) => 1,
|
||||
Some(FormatAlignment::Center) => 2,
|
||||
None => 3,
|
||||
};
|
||||
let default_flags = 0x6000_0020;
|
||||
let flags: u32 = o.fill.unwrap_or(' ') as u32
|
||||
| ((o.sign == Some(FormatSign::Plus)) as u32) << 21
|
||||
| ((o.sign == Some(FormatSign::Minus)) as u32) << 22
|
||||
| (o.alternate as u32) << 23
|
||||
| (o.zero_pad as u32) << 24
|
||||
| ((o.debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25
|
||||
| ((o.debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26
|
||||
| (o.width.is_some() as u32) << 27
|
||||
| (o.precision.is_some() as u32) << 28
|
||||
| align << 29;
|
||||
if flags != default_flags {
|
||||
bytecode[i] |= 1;
|
||||
bytecode.extend_from_slice(&flags.to_le_bytes());
|
||||
if let Some(val) = &o.width {
|
||||
let (indirect, val) = self.make_count_after_1_93_0(val, &mut argmap);
|
||||
// Only encode if nonzero; zero is the default.
|
||||
if indirect || val != 0 {
|
||||
bytecode[i] |= 1 << 1 | (indirect as u8) << 4;
|
||||
bytecode.extend_from_slice(&val.to_le_bytes());
|
||||
}
|
||||
}
|
||||
if let Some(val) = &o.precision {
|
||||
let (indirect, val) = self.make_count_after_1_93_0(val, &mut argmap);
|
||||
// Only encode if nonzero; zero is the default.
|
||||
if indirect || val != 0 {
|
||||
bytecode[i] |= 1 << 2 | (indirect as u8) << 5;
|
||||
bytecode.extend_from_slice(&val.to_le_bytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
if implicit_arg_index != position {
|
||||
bytecode[i] |= 1 << 3;
|
||||
bytecode.extend_from_slice(&(position as u16).to_le_bytes());
|
||||
}
|
||||
implicit_arg_index = position + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert!(incomplete_lit.is_empty());
|
||||
|
||||
// Zero terminator.
|
||||
bytecode.push(0);
|
||||
|
||||
// Ensure all argument indexes actually fit in 16 bits, as we truncated them to 16 bits before.
|
||||
if argmap.len() > u16::MAX as usize {
|
||||
// FIXME: Emit an error.
|
||||
// ctx.dcx().span_err(macsp, "too many format arguments");
|
||||
}
|
||||
|
||||
let arguments = &fmt.arguments.arguments[..];
|
||||
|
||||
let (mut statements, args) = if arguments.is_empty() {
|
||||
// Generate:
|
||||
// []
|
||||
(
|
||||
Vec::new(),
|
||||
self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements: Box::new([]),
|
||||
})),
|
||||
)
|
||||
} else {
|
||||
// Generate:
|
||||
// super let args = (&arg0, &arg1, &…);
|
||||
let args_name = self.generate_new_name();
|
||||
let args_path = Path::from(args_name.clone());
|
||||
let args_binding = self.alloc_binding(
|
||||
args_name.clone(),
|
||||
BindingAnnotation::Unannotated,
|
||||
HygieneId::ROOT,
|
||||
);
|
||||
let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });
|
||||
self.add_definition_to_binding(args_binding, args_pat);
|
||||
let elements = arguments
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: arg.expr,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let args_tuple = self.alloc_expr_desugared(Expr::Tuple { exprs: elements });
|
||||
// FIXME: Make this a `super let` when we have this statement.
|
||||
let let_statement_1 = Statement::Let {
|
||||
pat: args_pat,
|
||||
type_ref: None,
|
||||
initializer: Some(args_tuple),
|
||||
else_branch: None,
|
||||
};
|
||||
|
||||
// Generate:
|
||||
// super let args = [
|
||||
// <core::fmt::Argument>::new_display(args.0),
|
||||
// <core::fmt::Argument>::new_lower_hex(args.1),
|
||||
// <core::fmt::Argument>::new_debug(args.0),
|
||||
// …
|
||||
// ];
|
||||
let args = argmap
|
||||
.iter()
|
||||
.map(|&(arg_index, ty)| {
|
||||
let args_ident_expr = self.alloc_expr_desugared(Expr::Path(args_path.clone()));
|
||||
let arg = self.alloc_expr_desugared(Expr::Field {
|
||||
expr: args_ident_expr,
|
||||
name: Name::new_tuple_field(arg_index),
|
||||
});
|
||||
self.make_argument(arg, ty)
|
||||
})
|
||||
.collect();
|
||||
let args =
|
||||
self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));
|
||||
let args_binding =
|
||||
self.alloc_binding(args_name, BindingAnnotation::Unannotated, HygieneId::ROOT);
|
||||
let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });
|
||||
self.add_definition_to_binding(args_binding, args_pat);
|
||||
// FIXME: Make this a `super let` when we have this statement.
|
||||
let let_statement_2 = Statement::Let {
|
||||
pat: args_pat,
|
||||
type_ref: None,
|
||||
initializer: Some(args),
|
||||
else_branch: None,
|
||||
};
|
||||
(
|
||||
vec![let_statement_1, let_statement_2],
|
||||
self.alloc_expr_desugared(Expr::Path(args_path)),
|
||||
)
|
||||
};
|
||||
|
||||
// Generate:
|
||||
// unsafe {
|
||||
// <core::fmt::Arguments>::new(b"…", &args)
|
||||
// }
|
||||
let template = self
|
||||
.alloc_expr_desugared(Expr::Literal(Literal::ByteString(bytecode.into_boxed_slice())));
|
||||
let call = {
|
||||
let new = self.ty_rel_lang_path_desugared_expr(lang_items.FormatArguments, sym::new);
|
||||
let args = self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: args,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
});
|
||||
self.alloc_expr_desugared(Expr::Call { callee: new, args: Box::new([template, args]) })
|
||||
};
|
||||
let call = self.alloc_expr(
|
||||
Expr::Unsafe { id: None, statements: Box::new([]), tail: Some(call) },
|
||||
syntax_ptr,
|
||||
);
|
||||
|
||||
// We collect the unused expressions here so that we still infer them instead of
|
||||
// dropping them out of the expression tree. We cannot store them in the `Unsafe`
|
||||
// block because then unsafe blocks within them will get a false "unused unsafe"
|
||||
// diagnostic (rustc has a notion of builtin unsafe blocks, but we don't).
|
||||
statements
|
||||
.extend(fmt.orphans.into_iter().map(|expr| Statement::Expr { expr, has_semi: true }));
|
||||
|
||||
if !statements.is_empty() {
|
||||
// Generate:
|
||||
// {
|
||||
// super let …
|
||||
// super let …
|
||||
// <core::fmt::Arguments>::new(…)
|
||||
// }
|
||||
self.alloc_expr(
|
||||
Expr::Block {
|
||||
id: None,
|
||||
statements: statements.into_boxed_slice(),
|
||||
tail: Some(call),
|
||||
label: None,
|
||||
},
|
||||
syntax_ptr,
|
||||
)
|
||||
} else {
|
||||
call
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the value for a `width` or `precision` field.
|
||||
///
|
||||
/// Returns the value and whether it is indirect (an indexed argument) or not.
|
||||
fn make_count_after_1_93_0(
|
||||
&self,
|
||||
count: &FormatCount,
|
||||
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
|
||||
) -> (bool, u16) {
|
||||
match count {
|
||||
FormatCount::Literal(n) => (false, *n),
|
||||
FormatCount::Argument(arg) => {
|
||||
let index = match &arg.index {
|
||||
&Ok(it) => it,
|
||||
Err(_) => usize::MAX,
|
||||
};
|
||||
(true, argmap.insert_full((index, ArgumentType::Usize)).0 as u16)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_format_args_before_1_93_0_impl(
|
||||
&mut self,
|
||||
syntax_ptr: AstPtr<ast::Expr>,
|
||||
fmt: FormatArgs,
|
||||
) -> ExprId {
|
||||
// Create a list of all _unique_ (argument, format trait) combinations.
|
||||
// E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
|
||||
let mut argmap = FxIndexSet::default();
|
||||
|
|
@ -160,8 +502,14 @@ impl<'db> ExprCollector<'db> {
|
|||
let fmt_unsafe_arg = lang_items.FormatUnsafeArg;
|
||||
let use_format_args_since_1_89_0 = fmt_args.is_some() && fmt_unsafe_arg.is_none();
|
||||
|
||||
let idx = if use_format_args_since_1_89_0 {
|
||||
self.collect_format_args_impl(syntax_ptr, fmt, argmap, lit_pieces, format_options)
|
||||
if use_format_args_since_1_89_0 {
|
||||
self.collect_format_args_after_1_89_0_impl(
|
||||
syntax_ptr,
|
||||
fmt,
|
||||
argmap,
|
||||
lit_pieces,
|
||||
format_options,
|
||||
)
|
||||
} else {
|
||||
self.collect_format_args_before_1_89_0_impl(
|
||||
syntax_ptr,
|
||||
|
|
@ -170,14 +518,7 @@ impl<'db> ExprCollector<'db> {
|
|||
lit_pieces,
|
||||
format_options,
|
||||
)
|
||||
};
|
||||
|
||||
self.store
|
||||
.template_map
|
||||
.get_or_insert_with(Default::default)
|
||||
.format_args_to_captures
|
||||
.insert(idx, (hygiene, mappings));
|
||||
idx
|
||||
}
|
||||
}
|
||||
|
||||
/// `format_args!` expansion implementation for rustc versions < `1.89.0`
|
||||
|
|
@ -238,17 +579,10 @@ impl<'db> ExprCollector<'db> {
|
|||
// )
|
||||
|
||||
let lang_items = self.lang_items();
|
||||
let new_v1_formatted = self.ty_rel_lang_path(
|
||||
lang_items.FormatArguments,
|
||||
Name::new_symbol_root(sym::new_v1_formatted),
|
||||
);
|
||||
let unsafe_arg_new =
|
||||
self.ty_rel_lang_path(lang_items.FormatUnsafeArg, Name::new_symbol_root(sym::new));
|
||||
let new_v1_formatted =
|
||||
self.alloc_expr_desugared(new_v1_formatted.map_or(Expr::Missing, Expr::Path));
|
||||
|
||||
self.ty_rel_lang_path_desugared_expr(lang_items.FormatArguments, sym::new_v1_formatted);
|
||||
let unsafe_arg_new =
|
||||
self.alloc_expr_desugared(unsafe_arg_new.map_or(Expr::Missing, Expr::Path));
|
||||
self.ty_rel_lang_path_desugared_expr(lang_items.FormatUnsafeArg, sym::new);
|
||||
let unsafe_arg_new =
|
||||
self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() });
|
||||
let mut unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
|
||||
|
|
@ -284,7 +618,7 @@ impl<'db> ExprCollector<'db> {
|
|||
|
||||
/// `format_args!` expansion implementation for rustc versions >= `1.89.0`,
|
||||
/// especially since [this PR](https://github.com/rust-lang/rust/pull/140748)
|
||||
fn collect_format_args_impl(
|
||||
fn collect_format_args_after_1_89_0_impl(
|
||||
&mut self,
|
||||
syntax_ptr: AstPtr<ast::Expr>,
|
||||
fmt: FormatArgs,
|
||||
|
|
@ -422,12 +756,10 @@ impl<'db> ExprCollector<'db> {
|
|||
// )
|
||||
// }
|
||||
|
||||
let new_v1_formatted = self.ty_rel_lang_path(
|
||||
let new_v1_formatted = self.ty_rel_lang_path_desugared_expr(
|
||||
self.lang_items().FormatArguments,
|
||||
Name::new_symbol_root(sym::new_v1_formatted),
|
||||
sym::new_v1_formatted,
|
||||
);
|
||||
let new_v1_formatted =
|
||||
self.alloc_expr_desugared(new_v1_formatted.map_or(Expr::Missing, Expr::Path));
|
||||
let args = [lit_pieces, args, format_options];
|
||||
let call = self
|
||||
.alloc_expr_desugared(Expr::Call { callee: new_v1_formatted, args: args.into() });
|
||||
|
|
@ -499,8 +831,8 @@ impl<'db> ExprCollector<'db> {
|
|||
debug_hex,
|
||||
} = &placeholder.format_options;
|
||||
|
||||
let precision_expr = self.make_count(precision, argmap);
|
||||
let width_expr = self.make_count(width, argmap);
|
||||
let precision_expr = self.make_count_before_1_93_0(precision, argmap);
|
||||
let width_expr = self.make_count_before_1_93_0(width, argmap);
|
||||
|
||||
if self.krate.workspace_data(self.db).is_atleast_187() {
|
||||
// These need to match the constants in library/core/src/fmt/rt.rs.
|
||||
|
|
@ -542,16 +874,8 @@ impl<'db> ExprCollector<'db> {
|
|||
spread: None,
|
||||
})
|
||||
} else {
|
||||
let format_placeholder_new = {
|
||||
let format_placeholder_new = self.ty_rel_lang_path(
|
||||
lang_items.FormatPlaceholder,
|
||||
Name::new_symbol_root(sym::new),
|
||||
);
|
||||
match format_placeholder_new {
|
||||
Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
|
||||
None => self.missing_expr(),
|
||||
}
|
||||
};
|
||||
let format_placeholder_new =
|
||||
self.ty_rel_lang_path_desugared_expr(lang_items.FormatPlaceholder, sym::new);
|
||||
// This needs to match `Flag` in library/core/src/fmt/rt.rs.
|
||||
let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32)
|
||||
| (((sign == Some(FormatSign::Minus)) as u32) << 1)
|
||||
|
|
@ -564,21 +888,15 @@ impl<'db> ExprCollector<'db> {
|
|||
Some(BuiltinUint::U32),
|
||||
)));
|
||||
let fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' '))));
|
||||
let align = {
|
||||
let align = self.ty_rel_lang_path(
|
||||
lang_items.FormatAlignment,
|
||||
match alignment {
|
||||
Some(FormatAlignment::Left) => Name::new_symbol_root(sym::Left),
|
||||
Some(FormatAlignment::Right) => Name::new_symbol_root(sym::Right),
|
||||
Some(FormatAlignment::Center) => Name::new_symbol_root(sym::Center),
|
||||
None => Name::new_symbol_root(sym::Unknown),
|
||||
},
|
||||
);
|
||||
match align {
|
||||
Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
|
||||
None => self.missing_expr(),
|
||||
}
|
||||
};
|
||||
let align = self.ty_rel_lang_path_desugared_expr(
|
||||
lang_items.FormatAlignment,
|
||||
match alignment {
|
||||
Some(FormatAlignment::Left) => sym::Left,
|
||||
Some(FormatAlignment::Right) => sym::Right,
|
||||
Some(FormatAlignment::Center) => sym::Center,
|
||||
None => sym::Unknown,
|
||||
},
|
||||
);
|
||||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: format_placeholder_new,
|
||||
args: Box::new([position, fill, align, flags, precision_expr, width_expr]),
|
||||
|
|
@ -605,7 +923,7 @@ impl<'db> ExprCollector<'db> {
|
|||
/// ```text
|
||||
/// <core::fmt::rt::Count>::Implied
|
||||
/// ```
|
||||
fn make_count(
|
||||
fn make_count_before_1_93_0(
|
||||
&mut self,
|
||||
count: &Option<FormatCount>,
|
||||
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
|
||||
|
|
@ -618,12 +936,8 @@ impl<'db> ExprCollector<'db> {
|
|||
// FIXME: Change this to Some(BuiltinUint::U16) once we drop support for toolchains < 1.88
|
||||
None,
|
||||
)));
|
||||
let count_is = match self
|
||||
.ty_rel_lang_path(lang_items.FormatCount, Name::new_symbol_root(sym::Is))
|
||||
{
|
||||
Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)),
|
||||
None => self.missing_expr(),
|
||||
};
|
||||
let count_is =
|
||||
self.ty_rel_lang_path_desugared_expr(lang_items.FormatCount, sym::Is);
|
||||
self.alloc_expr_desugared(Expr::Call { callee: count_is, args: Box::new([args]) })
|
||||
}
|
||||
Some(FormatCount::Argument(arg)) => {
|
||||
|
|
@ -634,12 +948,8 @@ impl<'db> ExprCollector<'db> {
|
|||
i as u128,
|
||||
Some(BuiltinUint::Usize),
|
||||
)));
|
||||
let count_param = match self
|
||||
.ty_rel_lang_path(lang_items.FormatCount, Name::new_symbol_root(sym::Param))
|
||||
{
|
||||
Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
|
||||
None => self.missing_expr(),
|
||||
};
|
||||
let count_param =
|
||||
self.ty_rel_lang_path_desugared_expr(lang_items.FormatCount, sym::Param);
|
||||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: count_param,
|
||||
args: Box::new([args]),
|
||||
|
|
@ -650,9 +960,7 @@ impl<'db> ExprCollector<'db> {
|
|||
self.missing_expr()
|
||||
}
|
||||
}
|
||||
None => match self
|
||||
.ty_rel_lang_path(lang_items.FormatCount, Name::new_symbol_root(sym::Implied))
|
||||
{
|
||||
None => match self.ty_rel_lang_path(lang_items.FormatCount, sym::Implied) {
|
||||
Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
|
||||
None => self.missing_expr(),
|
||||
},
|
||||
|
|
@ -670,9 +978,9 @@ impl<'db> ExprCollector<'db> {
|
|||
use ArgumentType::*;
|
||||
use FormatTrait::*;
|
||||
|
||||
let new_fn = match self.ty_rel_lang_path(
|
||||
let new_fn = self.ty_rel_lang_path_desugared_expr(
|
||||
self.lang_items().FormatArgument,
|
||||
Name::new_symbol_root(match ty {
|
||||
match ty {
|
||||
Format(Display) => sym::new_display,
|
||||
Format(Debug) => sym::new_debug,
|
||||
Format(LowerExp) => sym::new_lower_exp,
|
||||
|
|
@ -683,13 +991,18 @@ impl<'db> ExprCollector<'db> {
|
|||
Format(LowerHex) => sym::new_lower_hex,
|
||||
Format(UpperHex) => sym::new_upper_hex,
|
||||
Usize => sym::from_usize,
|
||||
}),
|
||||
) {
|
||||
Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)),
|
||||
None => self.missing_expr(),
|
||||
};
|
||||
},
|
||||
);
|
||||
self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) })
|
||||
}
|
||||
|
||||
fn ty_rel_lang_path_desugared_expr(
|
||||
&mut self,
|
||||
lang: Option<impl Into<LangItemTarget>>,
|
||||
relative_name: Symbol,
|
||||
) -> ExprId {
|
||||
self.alloc_expr_desugared(self.ty_rel_lang_path_expr(lang, relative_name))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -161,9 +161,9 @@ fn main() {
|
|||
match builtin#lang(into_iter)(
|
||||
0..10,
|
||||
) {
|
||||
mut <ra@gennew>11 => loop {
|
||||
mut <ra@gennew>0 => loop {
|
||||
match builtin#lang(next)(
|
||||
&mut <ra@gennew>11,
|
||||
&mut <ra@gennew>0,
|
||||
) {
|
||||
builtin#lang(None) => break,
|
||||
builtin#lang(Some)(ident) => {
|
||||
|
|
@ -261,10 +261,10 @@ fn main() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn desugar_builtin_format_args() {
|
||||
fn desugar_builtin_format_args_before_1_93_0() {
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
//- minicore: fmt
|
||||
//- minicore: fmt_before_1_93_0
|
||||
fn main() {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
|
|
@ -343,6 +343,59 @@ fn main() {
|
|||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn desugar_builtin_format_args() {
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
//- minicore: fmt
|
||||
fn main() {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", orphan = (), last = "!");
|
||||
builtin#format_args("hello world");
|
||||
builtin#format_args("hello world", orphan = ());
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
expect![[r#"
|
||||
fn main() {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
{
|
||||
let <ra@gennew>0 = (&"fancy", &(), &"!", &count, &are, );
|
||||
let <ra@gennew>0 = [
|
||||
builtin#lang(Argument::new_display)(
|
||||
<ra@gennew>0.3,
|
||||
), builtin#lang(Argument::new_display)(
|
||||
<ra@gennew>0.0,
|
||||
), builtin#lang(Argument::new_debug)(
|
||||
<ra@gennew>0.4,
|
||||
), builtin#lang(Argument::new_display)(
|
||||
<ra@gennew>0.2,
|
||||
),
|
||||
];
|
||||
();
|
||||
unsafe {
|
||||
builtin#lang(Arguments::new)(
|
||||
"\x07\x1bhello \xc3 \x00\x00i\x02\x00\x01 \xc0\r friends, we \xc0\x01 \xc8\x01\x00\xc8\x03\x00\x00",
|
||||
&<ra@gennew>0,
|
||||
)
|
||||
}
|
||||
};
|
||||
builtin#lang(Arguments::from_str)(
|
||||
"hello world",
|
||||
);
|
||||
{
|
||||
();
|
||||
builtin#lang(Arguments::from_str)(
|
||||
"hello world",
|
||||
)
|
||||
};
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_hygiene() {
|
||||
let (db, body, def) = lower(
|
||||
|
|
@ -382,27 +435,16 @@ impl SsrError {
|
|||
fn main() {
|
||||
_ = ra_test_fixture::error::SsrError::new(
|
||||
{
|
||||
let args = [
|
||||
let <ra@gennew>0 = (&node.text(), );
|
||||
let <ra@gennew>0 = [
|
||||
builtin#lang(Argument::new_display)(
|
||||
&node.text(),
|
||||
<ra@gennew>0.0,
|
||||
),
|
||||
];
|
||||
unsafe {
|
||||
builtin#lang(Arguments::new_v1_formatted)(
|
||||
&[
|
||||
"Failed to resolve path `", "`",
|
||||
],
|
||||
&args,
|
||||
&[
|
||||
builtin#lang(Placeholder::new)(
|
||||
0usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
),
|
||||
],
|
||||
builtin#lang(Arguments::new)(
|
||||
"\x18Failed to resolve path `\xc0\x01`\x00",
|
||||
&<ra@gennew>0,
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@
|
|||
//! eq: sized
|
||||
//! error: fmt
|
||||
//! fmt: option, result, transmute, coerce_unsized, copy, clone, derive
|
||||
//! fmt_before_1_89_0: fmt
|
||||
//! fmt_before_1_93_0: fmt
|
||||
//! fmt_before_1_89_0: fmt_before_1_93_0
|
||||
//! fn: sized, tuple
|
||||
//! from: sized, result
|
||||
//! future: pin
|
||||
|
|
@ -1259,6 +1260,7 @@ pub mod fmt {
|
|||
Unknown,
|
||||
}
|
||||
|
||||
// region:fmt_before_1_93_0
|
||||
#[lang = "format_count"]
|
||||
pub enum Count {
|
||||
Is(usize),
|
||||
|
|
@ -1288,6 +1290,7 @@ pub mod fmt {
|
|||
Placeholder { position, fill, align, flags, precision, width }
|
||||
}
|
||||
}
|
||||
// endregion:fmt_before_1_93_0
|
||||
|
||||
// region:fmt_before_1_89_0
|
||||
#[lang = "format_unsafe_arg"]
|
||||
|
|
@ -1303,6 +1306,7 @@ pub mod fmt {
|
|||
// endregion:fmt_before_1_89_0
|
||||
}
|
||||
|
||||
// region:fmt_before_1_93_0
|
||||
#[derive(Copy, Clone)]
|
||||
#[lang = "format_arguments"]
|
||||
pub struct Arguments<'a> {
|
||||
|
|
@ -1341,6 +1345,14 @@ pub mod fmt {
|
|||
}
|
||||
// endregion:!fmt_before_1_89_0
|
||||
|
||||
pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> {
|
||||
Self::from_str(s)
|
||||
}
|
||||
|
||||
pub const fn from_str(s: &'static str) -> Arguments<'a> {
|
||||
Arguments { pieces: &[s], fmt: None, args: &[] }
|
||||
}
|
||||
|
||||
pub const fn as_str(&self) -> Option<&'static str> {
|
||||
match (self.pieces, self.args) {
|
||||
([], []) => Some(""),
|
||||
|
|
@ -1349,6 +1361,41 @@ pub mod fmt {
|
|||
}
|
||||
}
|
||||
}
|
||||
// endregion:fmt_before_1_93_0
|
||||
|
||||
// region:!fmt_before_1_93_0
|
||||
#[lang = "format_arguments"]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Arguments<'a> {
|
||||
// This is a non-faithful representation of `core::fmt::Arguments`, because the real one
|
||||
// is too complex for minicore.
|
||||
message: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> Arguments<'a> {
|
||||
pub unsafe fn new<const N: usize, const M: usize>(
|
||||
_template: &'a [u8; N],
|
||||
_args: &'a [rt::Argument<'a>; M],
|
||||
) -> Arguments<'a> {
|
||||
Arguments { message: None }
|
||||
}
|
||||
|
||||
pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> {
|
||||
Arguments { message: Some(s) }
|
||||
}
|
||||
|
||||
pub const fn from_str(s: &'static str) -> Arguments<'a> {
|
||||
Arguments { message: Some(s) }
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> Option<&'static str> {
|
||||
match self.message {
|
||||
Some(s) => unsafe { Some(&*(s as *const str)) },
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion:!fmt_before_1_93_0
|
||||
|
||||
// region:derive
|
||||
pub(crate) mod derive {
|
||||
|
|
@ -1817,7 +1864,7 @@ mod panicking {
|
|||
|
||||
#[lang = "panic"]
|
||||
pub const fn panic(expr: &'static str) -> ! {
|
||||
panic_fmt(crate::fmt::Arguments::new_const(&[expr]))
|
||||
panic_fmt(crate::fmt::Arguments::from_str(expr))
|
||||
}
|
||||
}
|
||||
// endregion:panic
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue