mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-03 17:58:17 +00:00
dev: introduce type checking on if and block join (#193)
* dev: clean some todo * dev: remove FlowBinaryRepr * dev: simple if and type join
This commit is contained in:
parent
a09c068d1d
commit
879d95a74b
8 changed files with 164 additions and 98 deletions
|
@ -118,10 +118,12 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
SyntaxKind::CodeBlock => return self.check_in_mode(root, InterpretMode::Code),
|
||||
SyntaxKind::ContentBlock => return self.check_in_mode(root, InterpretMode::Markup),
|
||||
|
||||
// todo: space effect
|
||||
SyntaxKind::Space => FlowType::None,
|
||||
SyntaxKind::Parbreak => FlowType::None,
|
||||
|
||||
SyntaxKind::Text => FlowType::Content,
|
||||
SyntaxKind::Space => FlowType::Content,
|
||||
SyntaxKind::Linebreak => FlowType::Content,
|
||||
SyntaxKind::Parbreak => FlowType::Content,
|
||||
SyntaxKind::Escape => FlowType::Content,
|
||||
SyntaxKind::Shorthand => FlowType::Content,
|
||||
SyntaxKind::SmartQuote => FlowType::Content,
|
||||
|
@ -265,10 +267,12 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
}
|
||||
|
||||
fn check_children(&mut self, root: LinkedNode<'_>) -> Option<FlowType> {
|
||||
let mut joiner = Joiner::default();
|
||||
|
||||
for child in root.children() {
|
||||
self.check(child);
|
||||
joiner.join(self.check(child));
|
||||
}
|
||||
Some(FlowType::Content)
|
||||
Some(joiner.finalize())
|
||||
}
|
||||
|
||||
fn check_ident(&mut self, root: LinkedNode<'_>, mode: InterpretMode) -> Option<FlowType> {
|
||||
|
@ -350,32 +354,10 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
let op = binary.op();
|
||||
let lhs = self.check_expr_in(binary.lhs().span(), root.clone());
|
||||
let rhs = self.check_expr_in(binary.rhs().span(), root);
|
||||
let repr = FlowBinaryRepr(Box::new((lhs, rhs)));
|
||||
|
||||
let ty = match op {
|
||||
ast::BinOp::Add => FlowBinaryType::Add(repr),
|
||||
ast::BinOp::Sub => FlowBinaryType::Sub(repr),
|
||||
ast::BinOp::Mul => FlowBinaryType::Mul(repr),
|
||||
ast::BinOp::Div => FlowBinaryType::Div(repr),
|
||||
ast::BinOp::And => FlowBinaryType::And(repr),
|
||||
ast::BinOp::Or => FlowBinaryType::Or(repr),
|
||||
ast::BinOp::Eq => FlowBinaryType::Eq(repr),
|
||||
ast::BinOp::Neq => FlowBinaryType::Neq(repr),
|
||||
ast::BinOp::Lt => FlowBinaryType::Lt(repr),
|
||||
ast::BinOp::Leq => FlowBinaryType::Leq(repr),
|
||||
ast::BinOp::Gt => FlowBinaryType::Gt(repr),
|
||||
ast::BinOp::Geq => FlowBinaryType::Geq(repr),
|
||||
ast::BinOp::Assign => FlowBinaryType::Assign(repr),
|
||||
ast::BinOp::In => FlowBinaryType::In(repr),
|
||||
ast::BinOp::NotIn => FlowBinaryType::NotIn(repr),
|
||||
ast::BinOp::AddAssign => FlowBinaryType::AddAssign(repr),
|
||||
ast::BinOp::SubAssign => FlowBinaryType::SubAssign(repr),
|
||||
ast::BinOp::MulAssign => FlowBinaryType::MulAssign(repr),
|
||||
ast::BinOp::DivAssign => FlowBinaryType::DivAssign(repr),
|
||||
};
|
||||
let operands = Box::new((lhs, rhs));
|
||||
|
||||
// Some(FlowType::Binary(ty))
|
||||
Some(FlowType::Binary(ty))
|
||||
Some(FlowType::Binary(FlowBinaryType { op, operands }))
|
||||
}
|
||||
|
||||
fn check_field_access(&mut self, root: LinkedNode<'_>) -> Option<FlowType> {
|
||||
|
@ -545,25 +527,26 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
Some(FlowType::Any)
|
||||
}
|
||||
|
||||
// currently we do nothing on contextual
|
||||
fn check_contextual(&mut self, root: LinkedNode<'_>) -> Option<FlowType> {
|
||||
let contextual: ast::Contextual = root.cast()?;
|
||||
|
||||
let _body = self.check_expr_in(contextual.body().span(), root);
|
||||
let body = self.check_expr_in(contextual.body().span(), root);
|
||||
|
||||
Some(FlowType::Content)
|
||||
Some(FlowType::Unary(FlowUnaryType::Context(Box::new(body))))
|
||||
}
|
||||
|
||||
fn check_conditional(&mut self, root: LinkedNode<'_>) -> Option<FlowType> {
|
||||
let conditional: ast::Conditional = root.cast()?;
|
||||
|
||||
let _cond = self.check_expr_in(conditional.condition().span(), root.clone());
|
||||
let _then = self.check_expr_in(conditional.if_body().span(), root.clone());
|
||||
let _else = conditional
|
||||
let cond = self.check_expr_in(conditional.condition().span(), root.clone());
|
||||
let then = self.check_expr_in(conditional.if_body().span(), root.clone());
|
||||
let else_ = conditional
|
||||
.else_body()
|
||||
.map(|else_body| self.check_expr_in(else_body.span(), root.clone()))
|
||||
.unwrap_or(FlowType::None);
|
||||
|
||||
Some(FlowType::Any)
|
||||
Some(FlowType::If(Box::new(FlowIfType { cond, then, else_ })))
|
||||
}
|
||||
|
||||
fn check_while_loop(&mut self, root: LinkedNode<'_>) -> Option<FlowType> {
|
||||
|
@ -749,6 +732,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
}
|
||||
FlowType::Unary(_) => {}
|
||||
FlowType::Binary(_) => {}
|
||||
FlowType::If(_) => {}
|
||||
FlowType::Element(_elem) => {}
|
||||
}
|
||||
|
||||
|
@ -931,6 +915,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
FlowType::At(e) => self.check_primary_type(e.0 .0.clone()),
|
||||
FlowType::Unary(_) => e,
|
||||
FlowType::Binary(_) => e,
|
||||
FlowType::If(_) => e,
|
||||
FlowType::Element(_) => e,
|
||||
}
|
||||
}
|
||||
|
@ -1134,9 +1119,14 @@ impl<'a, 'b> TypeSimplifier<'a, 'b> {
|
|||
}
|
||||
FlowType::Unary(u) => self.analyze(u.lhs(), pol),
|
||||
FlowType::Binary(b) => {
|
||||
let repr = b.repr();
|
||||
self.analyze(&repr.0 .0, pol);
|
||||
self.analyze(&repr.0 .1, pol);
|
||||
let (lhs, rhs) = b.repr();
|
||||
self.analyze(lhs, pol);
|
||||
self.analyze(rhs, pol);
|
||||
}
|
||||
FlowType::If(i) => {
|
||||
self.analyze(&i.cond, pol);
|
||||
self.analyze(&i.then, pol);
|
||||
self.analyze(&i.else_, pol);
|
||||
}
|
||||
FlowType::Union(v) => {
|
||||
for ty in v.iter() {
|
||||
|
@ -1267,6 +1257,11 @@ impl<'a, 'b> TypeSimplifier<'a, 'b> {
|
|||
|
||||
FlowType::Binary(b2)
|
||||
}
|
||||
FlowType::If(i) => {
|
||||
let i2 = i.clone();
|
||||
|
||||
FlowType::If(i2)
|
||||
}
|
||||
FlowType::Union(v) => {
|
||||
let v2 = v.iter().map(|ty| self.transform(ty, pol)).collect();
|
||||
|
||||
|
@ -1302,3 +1297,89 @@ fn to_ident_ref(root: &LinkedNode, c: ast::Ident) -> Option<IdentRef> {
|
|||
range: root.find(c.span())?.range(),
|
||||
})
|
||||
}
|
||||
|
||||
struct Joiner {
|
||||
break_or_continue_or_return: bool,
|
||||
definite: FlowType,
|
||||
possibles: Vec<FlowType>,
|
||||
}
|
||||
impl Joiner {
|
||||
fn finalize(self) -> FlowType {
|
||||
if self.possibles.is_empty() {
|
||||
return self.definite;
|
||||
}
|
||||
|
||||
// let mut definite = self.definite.clone();
|
||||
// for p in &self.possibles {
|
||||
// definite = definite.join(p);
|
||||
// }
|
||||
|
||||
// println!("possibles: {:?} {:?}", self.definite, self.possibles);
|
||||
|
||||
FlowType::Any
|
||||
}
|
||||
|
||||
fn join(&mut self, child: FlowType) {
|
||||
if self.break_or_continue_or_return {
|
||||
return;
|
||||
}
|
||||
|
||||
match (child, &self.definite) {
|
||||
(FlowType::Clause, _) => {}
|
||||
(FlowType::Undef, _) => {}
|
||||
(FlowType::Any, _) | (_, FlowType::Any) => {}
|
||||
(FlowType::Infer, _) => {}
|
||||
(FlowType::None, _) => {}
|
||||
// todo: mystery flow none
|
||||
(FlowType::FlowNone, _) => {}
|
||||
(FlowType::Content, FlowType::Content) => {}
|
||||
(FlowType::Content, FlowType::None) => self.definite = FlowType::Content,
|
||||
(FlowType::Content, _) => self.definite = FlowType::Undef,
|
||||
(FlowType::Var(v), _) => self.possibles.push(FlowType::Var(v)),
|
||||
// todo: check possibles
|
||||
(FlowType::Array, FlowType::None) => self.definite = FlowType::Array,
|
||||
(FlowType::Array, _) => self.definite = FlowType::Undef,
|
||||
// todo: possible some style
|
||||
(FlowType::Auto, FlowType::None) => self.definite = FlowType::Auto,
|
||||
(FlowType::Auto, _) => self.definite = FlowType::Undef,
|
||||
(FlowType::Builtin(b), FlowType::None) => self.definite = FlowType::Builtin(b),
|
||||
(FlowType::Builtin(..), _) => self.definite = FlowType::Undef,
|
||||
// todo: value join
|
||||
(FlowType::Value(v), FlowType::None) => self.definite = FlowType::Value(v),
|
||||
(FlowType::Value(..), _) => self.definite = FlowType::Undef,
|
||||
(FlowType::ValueDoc(v), FlowType::None) => self.definite = FlowType::ValueDoc(v),
|
||||
(FlowType::ValueDoc(..), _) => self.definite = FlowType::Undef,
|
||||
(FlowType::Element(e), FlowType::None) => self.definite = FlowType::Element(e),
|
||||
(FlowType::Element(..), _) => self.definite = FlowType::Undef,
|
||||
(FlowType::Func(f), FlowType::None) => self.definite = FlowType::Func(f),
|
||||
(FlowType::Func(..), _) => self.definite = FlowType::Undef,
|
||||
(FlowType::Dict(w), FlowType::None) => self.definite = FlowType::Dict(w),
|
||||
(FlowType::Dict(..), _) => self.definite = FlowType::Undef,
|
||||
(FlowType::With(w), FlowType::None) => self.definite = FlowType::With(w),
|
||||
(FlowType::With(..), _) => self.definite = FlowType::Undef,
|
||||
(FlowType::Args(w), FlowType::None) => self.definite = FlowType::Args(w),
|
||||
(FlowType::Args(..), _) => self.definite = FlowType::Undef,
|
||||
(FlowType::At(w), FlowType::None) => self.definite = FlowType::At(w),
|
||||
(FlowType::At(..), _) => self.definite = FlowType::Undef,
|
||||
(FlowType::Unary(w), FlowType::None) => self.definite = FlowType::Unary(w),
|
||||
(FlowType::Unary(..), _) => self.definite = FlowType::Undef,
|
||||
(FlowType::Binary(w), FlowType::None) => self.definite = FlowType::Binary(w),
|
||||
(FlowType::Binary(..), _) => self.definite = FlowType::Undef,
|
||||
(FlowType::If(w), FlowType::None) => self.definite = FlowType::If(w),
|
||||
(FlowType::If(..), _) => self.definite = FlowType::Undef,
|
||||
(FlowType::Union(w), FlowType::None) => self.definite = FlowType::Union(w),
|
||||
(FlowType::Union(..), _) => self.definite = FlowType::Undef,
|
||||
(FlowType::Let(w), FlowType::None) => self.definite = FlowType::Let(w),
|
||||
(FlowType::Let(..), _) => self.definite = FlowType::Undef,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Default for Joiner {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
break_or_continue_or_return: false,
|
||||
definite: FlowType::None,
|
||||
possibles: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use parking_lot::RwLock;
|
|||
use reflexo::vector::ir::DefId;
|
||||
use typst::{
|
||||
foundations::{CastInfo, Element, Func, ParamInfo, Value},
|
||||
syntax::Span,
|
||||
syntax::{ast, Span},
|
||||
};
|
||||
|
||||
use crate::analysis::ty::param_mapping;
|
||||
|
@ -49,6 +49,7 @@ pub(crate) enum FlowType {
|
|||
At(FlowAt),
|
||||
Unary(FlowUnaryType),
|
||||
Binary(FlowBinaryType),
|
||||
If(Box<FlowIfType>),
|
||||
Union(Box<Vec<FlowType>>),
|
||||
Let(Arc<FlowVarStore>),
|
||||
}
|
||||
|
@ -85,6 +86,7 @@ impl fmt::Debug for FlowType {
|
|||
FlowType::Var(v) => write!(f, "@{}", v.1),
|
||||
FlowType::Unary(u) => write!(f, "{u:?}"),
|
||||
FlowType::Binary(b) => write!(f, "{b:?}"),
|
||||
FlowType::If(i) => write!(f, "{i:?}"),
|
||||
FlowType::Value(v) => write!(f, "{v:?}", v = v.0),
|
||||
FlowType::ValueDoc(v) => write!(f, "{v:?}"),
|
||||
FlowType::Element(e) => write!(f, "{e:?}"),
|
||||
|
@ -152,6 +154,7 @@ pub(crate) enum FlowUnaryType {
|
|||
Pos(Box<FlowType>),
|
||||
Neg(Box<FlowType>),
|
||||
Not(Box<FlowType>),
|
||||
Context(Box<FlowType>),
|
||||
}
|
||||
|
||||
impl FlowUnaryType {
|
||||
|
@ -160,69 +163,32 @@ impl FlowUnaryType {
|
|||
FlowUnaryType::Pos(e) => e,
|
||||
FlowUnaryType::Neg(e) => e,
|
||||
FlowUnaryType::Not(e) => e,
|
||||
FlowUnaryType::Context(e) => e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub(crate) enum FlowBinaryType {
|
||||
Add(FlowBinaryRepr),
|
||||
Sub(FlowBinaryRepr),
|
||||
Mul(FlowBinaryRepr),
|
||||
Div(FlowBinaryRepr),
|
||||
And(FlowBinaryRepr),
|
||||
Or(FlowBinaryRepr),
|
||||
Eq(FlowBinaryRepr),
|
||||
Neq(FlowBinaryRepr),
|
||||
Lt(FlowBinaryRepr),
|
||||
Leq(FlowBinaryRepr),
|
||||
Gt(FlowBinaryRepr),
|
||||
Geq(FlowBinaryRepr),
|
||||
Assign(FlowBinaryRepr),
|
||||
In(FlowBinaryRepr),
|
||||
NotIn(FlowBinaryRepr),
|
||||
AddAssign(FlowBinaryRepr),
|
||||
SubAssign(FlowBinaryRepr),
|
||||
MulAssign(FlowBinaryRepr),
|
||||
DivAssign(FlowBinaryRepr),
|
||||
pub(crate) struct FlowBinaryType {
|
||||
pub op: ast::BinOp,
|
||||
pub operands: Box<(FlowType, FlowType)>,
|
||||
}
|
||||
|
||||
impl FlowBinaryType {
|
||||
pub fn repr(&self) -> &FlowBinaryRepr {
|
||||
match self {
|
||||
FlowBinaryType::Add(r)
|
||||
| FlowBinaryType::Sub(r)
|
||||
| FlowBinaryType::Mul(r)
|
||||
| FlowBinaryType::Div(r)
|
||||
| FlowBinaryType::And(r)
|
||||
| FlowBinaryType::Or(r)
|
||||
| FlowBinaryType::Eq(r)
|
||||
| FlowBinaryType::Neq(r)
|
||||
| FlowBinaryType::Lt(r)
|
||||
| FlowBinaryType::Leq(r)
|
||||
| FlowBinaryType::Gt(r)
|
||||
| FlowBinaryType::Geq(r)
|
||||
| FlowBinaryType::Assign(r)
|
||||
| FlowBinaryType::In(r)
|
||||
| FlowBinaryType::NotIn(r)
|
||||
| FlowBinaryType::AddAssign(r)
|
||||
| FlowBinaryType::SubAssign(r)
|
||||
| FlowBinaryType::MulAssign(r)
|
||||
| FlowBinaryType::DivAssign(r) => r,
|
||||
}
|
||||
pub fn repr(&self) -> (&FlowType, &FlowType) {
|
||||
(&self.operands.0, &self.operands.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash)]
|
||||
pub(crate) struct FlowBinaryRepr(pub Box<(FlowType, FlowType)>);
|
||||
|
||||
impl fmt::Debug for FlowBinaryRepr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// shorter
|
||||
write!(f, "{:?}, {:?}", RefDebug(&self.0 .0), RefDebug(&self.0 .1))
|
||||
}
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub(crate) struct FlowIfType {
|
||||
pub cond: FlowType,
|
||||
pub then: FlowType,
|
||||
pub else_: FlowType,
|
||||
}
|
||||
|
||||
impl FlowIfType {}
|
||||
|
||||
#[derive(Clone, Hash)]
|
||||
pub(crate) struct FlowVarStore {
|
||||
pub lbs: Vec<FlowType>,
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#let x0 = if true {
|
||||
1
|
||||
}
|
||||
#let x1 = if false {
|
||||
2
|
||||
}
|
||||
#let x2 = context if here().page() > 0 {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/type_check/control_flow.typ
|
||||
---
|
||||
"x0" = FlowIfType { cond: true, then: 1, else_: None }
|
||||
"x1" = FlowIfType { cond: false, then: 2, else_: None }
|
||||
"x2" = Context(FlowIfType { cond: FlowBinaryType { op: Gt, operands: (Any, 0) }, then: 1, else_: 2 })
|
||||
---
|
||||
5..7 -> @x0
|
||||
31..33 -> @x1
|
||||
58..60 -> @x2
|
||||
74..80 -> Type(location)
|
||||
74..87 -> Any
|
|
@ -441,6 +441,7 @@ fn type_completion(
|
|||
FlowType::Var(_) => return None,
|
||||
FlowType::Unary(_) => return None,
|
||||
FlowType::Binary(_) => return None,
|
||||
FlowType::If(_) => return None,
|
||||
FlowType::Value(v) => {
|
||||
if let Value::Type(ty) = &v.0 {
|
||||
if *ty == Type::of::<NoneValue>() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue