Implement builtin#format_args, using rustc's format_args parser

This commit is contained in:
Lukas Wirth 2023-09-05 19:06:15 +02:00
parent 3431d586e5
commit abe8f1ece4
19 changed files with 1740 additions and 14 deletions

View file

@ -9,7 +9,10 @@ use chalk_ir::{
};
use hir_def::{
data::adt::VariantData,
hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp},
hir::{
format_args::FormatArgumentKind, Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat,
PatId, Statement, UnaryOp,
},
lang_item::LangItem,
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
DefWithBodyId, FieldId, HasModule, VariantId,
@ -453,6 +456,14 @@ impl InferenceContext<'_> {
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
match &self.body[tgt_expr] {
Expr::OffsetOf(_) => (),
Expr::FormatArgs(fa) => {
self.walk_expr_without_adjust(fa.template_expr);
fa.arguments
.arguments
.iter()
.filter(|it| !matches!(it.kind, FormatArgumentKind::Captured(_)))
.for_each(|it| self.walk_expr_without_adjust(it.expr));
}
Expr::InlineAsm(e) => self.walk_expr_without_adjust(e.e),
Expr::If { condition, then_branch, else_branch } => {
self.consume_expr(*condition);

View file

@ -9,7 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin
use hir_def::{
generics::TypeOrConstParamData,
hir::{
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
format_args::FormatArgumentKind, ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId,
LabelId, Literal, Statement, UnaryOp,
},
lang_item::{LangItem, LangItemTarget},
path::{GenericArg, GenericArgs},
@ -848,6 +849,25 @@ impl InferenceContext<'_> {
self.infer_expr_no_expect(it.e);
self.result.standard_types.unit.clone()
}
Expr::FormatArgs(fa) => {
fa.arguments
.arguments
.iter()
.filter(|it| !matches!(it.kind, FormatArgumentKind::Captured(_)))
.for_each(|it| _ = self.infer_expr_no_expect(it.expr));
match self
.resolve_lang_item(LangItem::FormatArguments)
.and_then(|it| it.as_struct())
{
Some(s) => {
// NOTE: This struct has a lifetime parameter, but we don't currently emit
// those to chalk
TyKind::Adt(AdtId(s.into()), Substitution::empty(Interner)).intern(Interner)
}
None => self.err_ty(),
}
}
};
// use a new type variable if we got unknown here
let ty = self.insert_type_vars_shallow(ty);

View file

@ -3,7 +3,10 @@
use chalk_ir::Mutability;
use hir_def::{
hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
hir::{
format_args::FormatArgumentKind, Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId,
Statement, UnaryOp,
},
lang_item::LangItem,
};
use hir_expand::name;
@ -37,6 +40,13 @@ impl InferenceContext<'_> {
Expr::Missing => (),
Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not),
Expr::OffsetOf(_) => (),
Expr::FormatArgs(fa) => {
fa.arguments
.arguments
.iter()
.filter(|it| !matches!(it.kind, FormatArgumentKind::Captured(_)))
.for_each(|arg| self.infer_mut_expr_without_adjust(arg.expr, Mutability::Not));
}
&Expr::If { condition, then_branch, else_branch } => {
self.infer_mut_expr(condition, Mutability::Not);
self.infer_mut_expr(then_branch, Mutability::Not);

View file

@ -376,6 +376,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
Expr::InlineAsm(_) => {
not_supported!("builtin#asm")
}
Expr::FormatArgs(_) => {
not_supported!("builtin#format_args")
}
Expr::Missing => {
if let DefWithBodyId::FunctionId(f) = self.owner {
let assoc = f.lookup(self.db.upcast());

View file

@ -3612,3 +3612,25 @@ fn main() {
"#,
);
}
#[test]
fn builtin_format_args() {
check_infer(
r#"
#[lang = "format_arguments"]
pub struct Arguments<'a>;
fn main() {
let are = "are";
builtin#format_args("hello {} friends, we {are} {0}{last}", "fancy", last = "!");
}
"#,
expect![[r#"
65..175 '{ ...!"); }': ()
75..78 'are': &str
81..86 '"are"': &str
92..172 'builti...= "!")': Arguments<'_>
152..159 '"fancy"': &str
168..171 '"!"': &str
"#]],
);
}