Merge record lit's ellipsis into pre-existing spread's variant

This commit is contained in:
Shoyu Vanilla 2025-01-26 17:23:23 +09:00
parent e6a103ae50
commit c134b20c9c
10 changed files with 55 additions and 34 deletions

View file

@ -25,7 +25,7 @@ use crate::{
db::DefDatabase, db::DefDatabase,
hir::{ hir::{
Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat, Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat,
PatId, RecordFieldPat, Statement, PatId, RecordFieldPat, Spread, Statement,
}, },
nameres::DefMap, nameres::DefMap,
path::{ModPath, Path}, path::{ModPath, Path},
@ -362,7 +362,7 @@ impl ExpressionStore {
for field in fields.iter() { for field in fields.iter() {
f(field.expr); f(field.expr);
} }
if let &Some(expr) = spread { if let &Spread::Base(expr) = spread {
f(expr); f(expr);
} }
} }
@ -490,7 +490,7 @@ impl ExpressionStore {
for field in fields.iter() { for field in fields.iter() {
f(field.expr); f(field.expr);
} }
if let &Some(expr) = spread { if let &Spread::Base(expr) = spread {
f(expr); f(expr);
} }
} }

View file

@ -45,7 +45,7 @@ use crate::{
}, },
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
Expr, ExprId, Item, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, Expr, ExprId, Item, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Spread, Statement,
}, },
item_scope::BuiltinShadowMode, item_scope::BuiltinShadowMode,
lang_item::LangItem, lang_item::LangItem,
@ -602,11 +602,13 @@ impl ExprCollector<'_> {
Some(RecordLitField { name, expr }) Some(RecordLitField { name, expr })
}) })
.collect(); .collect();
let spread = nfl.spread().map(|s| self.collect_expr(s)); let spread = nfl.spread().map(|s| self.collect_expr(s)).map_or_else(
let ellipsis = nfl.dotdot_token().is_some(); || if nfl.dotdot_token().is_some() { Spread::Yes } else { Spread::No },
Expr::RecordLit { path, fields, spread, ellipsis } Spread::Base,
);
Expr::RecordLit { path, fields, spread }
} else { } else {
Expr::RecordLit { path, fields: Box::default(), spread: None, ellipsis: false } Expr::RecordLit { path, fields: Box::default(), spread: Spread::No }
}; };
self.alloc_expr(record_lit, syntax_ptr) self.alloc_expr(record_lit, syntax_ptr)

View file

@ -8,7 +8,7 @@ use span::Edition;
use crate::{ use crate::{
hir::{ hir::{
Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, LiteralOrConst, Movability, Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, LiteralOrConst, Movability,
Statement, Spread, Statement,
}, },
pretty::{print_generic_args, print_path, print_type_ref}, pretty::{print_generic_args, print_path, print_type_ref},
VariantId, VariantId,
@ -398,7 +398,7 @@ impl Printer<'_> {
self.print_expr(*expr); self.print_expr(*expr);
} }
} }
Expr::RecordLit { path, fields, spread, ellipsis: _ } => { Expr::RecordLit { path, fields, spread } => {
match path { match path {
Some(path) => self.print_path(path), Some(path) => self.print_path(path),
None => w!(self, "<EFBFBD>"), None => w!(self, "<EFBFBD>"),
@ -412,10 +412,16 @@ impl Printer<'_> {
p.print_expr(field.expr); p.print_expr(field.expr);
wln!(p, ","); wln!(p, ",");
} }
if let Some(spread) = spread { match spread {
w!(p, ".."); Spread::No => {}
p.print_expr(*spread); Spread::Yes => {
wln!(p); w!(p, "..");
}
Spread::Base(expr) => {
w!(p, "..");
p.print_expr(*expr);
wln!(p);
}
} }
}); });
w!(self, "}}"); w!(self, "}}");

View file

@ -251,8 +251,7 @@ pub enum Expr {
RecordLit { RecordLit {
path: Option<Box<Path>>, path: Option<Box<Path>>,
fields: Box<[RecordLitField]>, fields: Box<[RecordLitField]>,
spread: Option<ExprId>, spread: Spread,
ellipsis: bool,
}, },
Field { Field {
expr: ExprId, expr: ExprId,
@ -479,6 +478,13 @@ pub struct RecordLitField {
pub expr: ExprId, pub expr: ExprId,
} }
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Spread {
No,
Yes,
Base(ExprId),
}
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub enum Statement { pub enum Statement {
Let { Let {

View file

@ -8,6 +8,7 @@ use base_db::CrateId;
use chalk_solve::rust_ir::AdtKind; use chalk_solve::rust_ir::AdtKind;
use either::Either; use either::Either;
use hir_def::{ use hir_def::{
hir::Spread,
lang_item::LangItem, lang_item::LangItem,
resolver::{HasResolver, ValueNs}, resolver::{HasResolver, ValueNs},
AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup, AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup,
@ -546,9 +547,11 @@ pub fn record_literal_missing_fields(
infer: &InferenceResult, infer: &InferenceResult,
id: ExprId, id: ExprId,
expr: &Expr, expr: &Expr,
) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> { ) -> Option<(VariantId, Vec<LocalFieldId>, /*has spread expr*/ bool)> {
let (fields, exhaustive, ellipsis) = match expr { let (fields, has_spread_expr, has_ellipsis) = match expr {
Expr::RecordLit { fields, spread, ellipsis, .. } => (fields, spread.is_none(), *ellipsis), Expr::RecordLit { fields, spread, .. } => {
(fields, matches!(spread, Spread::Base(_)), matches!(spread, Spread::Yes))
}
_ => return None, _ => return None,
}; };
@ -564,7 +567,7 @@ pub fn record_literal_missing_fields(
.fields() .fields()
.iter() .iter()
.filter_map(|(f, d)| { .filter_map(|(f, d)| {
if (ellipsis && d.has_default) || specified_fields.contains(&d.name) { if (has_ellipsis && d.has_default) || specified_fields.contains(&d.name) {
None None
} else { } else {
Some(f) Some(f)
@ -574,7 +577,7 @@ pub fn record_literal_missing_fields(
if missed_fields.is_empty() { if missed_fields.is_empty() {
return None; return None;
} }
Some((variant_def, missed_fields, exhaustive)) Some((variant_def, missed_fields, has_spread_expr))
} }
pub fn record_pattern_missing_fields( pub fn record_pattern_missing_fields(

View file

@ -12,7 +12,7 @@ use hir_def::{
data::adt::VariantData, data::adt::VariantData,
hir::{ hir::{
Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId, Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId,
Statement, UnaryOp, Spread, Statement, UnaryOp,
}, },
lang_item::LangItem, lang_item::LangItem,
path::Path, path::Path,
@ -796,7 +796,7 @@ impl InferenceContext<'_> {
self.consume_expr(expr); self.consume_expr(expr);
} }
Expr::RecordLit { fields, spread, .. } => { Expr::RecordLit { fields, spread, .. } => {
if let &Some(expr) = spread { if let &Spread::Base(expr) = spread {
self.consume_expr(expr); self.consume_expr(expr);
} }
self.consume_exprs(fields.iter().map(|it| it.expr)); self.consume_exprs(fields.iter().map(|it| it.expr));

View file

@ -10,7 +10,7 @@ use either::Either;
use hir_def::{ use hir_def::{
hir::{ hir::{
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, ExprOrPatId, ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, ExprOrPatId,
LabelId, Literal, Pat, PatId, Statement, UnaryOp, LabelId, Literal, Pat, PatId, Spread, Statement, UnaryOp,
}, },
lang_item::{LangItem, LangItemTarget}, lang_item::{LangItem, LangItemTarget},
path::{GenericArg, GenericArgs, Path}, path::{GenericArg, GenericArgs, Path},
@ -775,7 +775,7 @@ impl InferenceContext<'_> {
} }
} }
} }
if let Some(expr) = spread { if let Spread::Base(expr) = spread {
self.infer_expr(*expr, &Expectation::has_type(ty.clone()), ExprIsRead::Yes); self.infer_expr(*expr, &Expectation::has_type(ty.clone()), ExprIsRead::Yes);
} }
ty ty

View file

@ -4,8 +4,8 @@
use chalk_ir::{cast::Cast, Mutability}; use chalk_ir::{cast::Cast, Mutability};
use hir_def::{ use hir_def::{
hir::{ hir::{
Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Statement, Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Spread,
UnaryOp, Statement, UnaryOp,
}, },
lang_item::LangItem, lang_item::LangItem,
}; };
@ -121,8 +121,12 @@ impl InferenceContext<'_> {
Expr::Become { expr } => { Expr::Become { expr } => {
self.infer_mut_expr(*expr, Mutability::Not); self.infer_mut_expr(*expr, Mutability::Not);
} }
Expr::RecordLit { path: _, fields, spread, ellipsis: _ } => { Expr::RecordLit { path: _, fields, spread } => {
self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread)) let spread_expr = match spread {
Spread::Base(expr) => Some(*expr),
_ => None,
};
self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(spread_expr))
} }
&Expr::Index { base, index } => { &Expr::Index { base, index } => {
if mutability == Mutability::Mut { if mutability == Mutability::Mut {

View file

@ -9,7 +9,7 @@ use hir_def::{
expr_store::{Body, HygieneId}, expr_store::{Body, HygieneId},
hir::{ hir::{
ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal,
LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, Spread,
}, },
lang_item::{LangItem, LangItemTarget}, lang_item::{LangItem, LangItemTarget},
path::Path, path::Path,
@ -823,16 +823,16 @@ impl<'ctx> MirLowerCtx<'ctx> {
} }
Expr::Become { .. } => not_supported!("tail-calls"), Expr::Become { .. } => not_supported!("tail-calls"),
Expr::Yield { .. } => not_supported!("yield"), Expr::Yield { .. } => not_supported!("yield"),
Expr::RecordLit { fields, path, spread, ellipsis: _ } => { Expr::RecordLit { fields, path, spread } => {
let spread_place = match spread { let spread_place = match spread {
&Some(it) => { &Spread::Base(it) => {
let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else { let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else {
return Ok(None); return Ok(None);
}; };
current = c; current = c;
Some(p) Some(p)
} }
None => None, _ => None,
}; };
let variant_id = let variant_id =
self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path { self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path {

View file

@ -1023,7 +1023,7 @@ impl SourceAnalyzer {
let expr_id = self.expr_id(db, &literal.clone().into())?; let expr_id = self.expr_id(db, &literal.clone().into())?;
let substs = infer[expr_id].as_adt()?.1; let substs = infer[expr_id].as_adt()?.1;
let (variant, missing_fields, _exhaustive) = match expr_id { let (variant, missing_fields, _) = match expr_id {
ExprOrPatId::ExprId(expr_id) => { ExprOrPatId::ExprId(expr_id) => {
record_literal_missing_fields(db, infer, expr_id, &body[expr_id])? record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?
} }