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:
Myriad-Dreamin 2024-04-16 14:13:37 +08:00 committed by GitHub
parent a09c068d1d
commit 879d95a74b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 164 additions and 98 deletions

View file

@ -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(),
}
}
}

View file

@ -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>,

View file

@ -0,0 +1,11 @@
#let x0 = if true {
1
}
#let x1 = if false {
2
}
#let x2 = context if here().page() > 0 {
1
} else {
2
}

View file

@ -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

View file

@ -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>() {