mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-03 17:58:17 +00:00
dev: weaken inference from outer use (#250)
* dev: weaken inference from outer use * >_<
This commit is contained in:
parent
ca5b87efba
commit
1f9719a420
10 changed files with 278 additions and 55 deletions
|
@ -531,6 +531,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
v.ever_be(exp);
|
||||
named.insert(e.name().get().clone(), v.get_ref());
|
||||
}
|
||||
// todo: spread left/right
|
||||
ast::Param::Spread(a) => {
|
||||
if let Some(e) = a.sink_ident() {
|
||||
let exp = FlowType::Builtin(FlowBuiltinType::Args);
|
||||
|
@ -545,9 +546,22 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
|
||||
let body = self.check_expr_in(closure.body().span(), root);
|
||||
|
||||
let named: Vec<(EcoString, FlowType)> = named.into_iter().collect();
|
||||
|
||||
// freeze the signature
|
||||
for pos in pos.iter() {
|
||||
self.weaken(pos);
|
||||
}
|
||||
for (_, named) in named.iter() {
|
||||
self.weaken(named);
|
||||
}
|
||||
if let Some(rest) = &rest {
|
||||
self.weaken(rest);
|
||||
}
|
||||
|
||||
Some(FlowType::Func(Box::new(FlowSignature {
|
||||
pos,
|
||||
named: named.into_iter().collect(),
|
||||
named,
|
||||
rest,
|
||||
ret: body,
|
||||
})))
|
||||
|
@ -565,7 +579,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
.unwrap_or_else(|| FlowType::Infer);
|
||||
|
||||
let v = self.get_var(c.span(), to_ident_ref(&root, c)?)?;
|
||||
v.as_strong(value);
|
||||
v.ever_be(value);
|
||||
// todo lbs is the lexical signature.
|
||||
}
|
||||
ast::LetBindingKind::Normal(pattern) => {
|
||||
|
@ -709,7 +723,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
#[allow(clippy::map_entry)]
|
||||
if !self.info.vars.contains_key(&def_id) {
|
||||
let def = self.check_external(def_id);
|
||||
let kind = FlowVarKind::Weak(Arc::new(RwLock::new(self.init_var(def))));
|
||||
let kind = FlowVarKind::Strong(Arc::new(RwLock::new(self.init_var(def))));
|
||||
self.info.vars.insert(
|
||||
def_id,
|
||||
FlowVar {
|
||||
|
@ -793,7 +807,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
FlowType::Var(v) => {
|
||||
let w = self.info.vars.get(&v.0).cloned()?;
|
||||
match &w.kind {
|
||||
FlowVarKind::Weak(w) => {
|
||||
FlowVarKind::Strong(w) | FlowVarKind::Weak(w) => {
|
||||
// It is instantiated here by clone.
|
||||
let w = w.read().clone();
|
||||
for lb in w.lbs.iter() {
|
||||
|
@ -917,20 +931,23 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
(FlowType::Var(v), rhs) => {
|
||||
log::debug!("constrain var {v:?} ⪯ {rhs:?}");
|
||||
let w = self.info.vars.get_mut(&v.0).unwrap();
|
||||
// strict constraint on upper bound
|
||||
let bound = rhs.clone();
|
||||
match &w.kind {
|
||||
FlowVarKind::Weak(w) => {
|
||||
FlowVarKind::Strong(w) | FlowVarKind::Weak(w) => {
|
||||
let mut w = w.write();
|
||||
w.ubs.push(rhs.clone());
|
||||
w.ubs.push(bound);
|
||||
}
|
||||
}
|
||||
}
|
||||
(lhs, FlowType::Var(v)) => {
|
||||
log::debug!("constrain var {lhs:?} ⪯ {v:?}");
|
||||
let v = self.info.vars.get(&v.0).unwrap();
|
||||
match &v.kind {
|
||||
FlowVarKind::Weak(v) => {
|
||||
let w = self.info.vars.get(&v.0).unwrap();
|
||||
let bound = self.weaken_constraint(lhs, &w.kind);
|
||||
log::debug!("constrain var {v:?} ⪰ {bound:?}");
|
||||
match &w.kind {
|
||||
FlowVarKind::Strong(v) | FlowVarKind::Weak(v) => {
|
||||
let mut v = v.write();
|
||||
v.lbs.push(lhs.clone());
|
||||
v.lbs.push(bound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1031,7 +1048,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
FlowType::Var(v) => {
|
||||
let w = self.info.vars.get(&v.0).unwrap();
|
||||
match &w.kind {
|
||||
FlowVarKind::Weak(w) => {
|
||||
FlowVarKind::Strong(w) | FlowVarKind::Weak(w) => {
|
||||
let w = w.read();
|
||||
if !w.ubs.is_empty() {
|
||||
return w.ubs[0].clone();
|
||||
|
@ -1238,7 +1255,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
FlowType::Var(v) => {
|
||||
let w = self.info.vars.get(&v.0).unwrap();
|
||||
match &w.kind {
|
||||
FlowVarKind::Weak(w) => {
|
||||
FlowVarKind::Strong(w) | FlowVarKind::Weak(w) => {
|
||||
let w = w.read();
|
||||
store.lbs.extend(w.lbs.iter().cloned());
|
||||
store.ubs.extend(w.ubs.iter().cloned());
|
||||
|
@ -1256,6 +1273,117 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
|
||||
store
|
||||
}
|
||||
|
||||
fn weaken(&mut self, v: &FlowType) {
|
||||
match v {
|
||||
FlowType::Var(v) => {
|
||||
let w = self.info.vars.get_mut(&v.0).unwrap();
|
||||
w.weaken();
|
||||
}
|
||||
FlowType::Clause
|
||||
| FlowType::Undef
|
||||
| FlowType::Content
|
||||
| FlowType::Any
|
||||
| FlowType::Space
|
||||
| FlowType::None
|
||||
| FlowType::Infer
|
||||
| FlowType::FlowNone
|
||||
| FlowType::Auto
|
||||
| FlowType::Boolean(_)
|
||||
| FlowType::Builtin(_)
|
||||
| FlowType::Value(_) => {}
|
||||
FlowType::Element(_) => {}
|
||||
FlowType::ValueDoc(_) => {}
|
||||
FlowType::Field(v) => {
|
||||
self.weaken(&v.1);
|
||||
}
|
||||
FlowType::Func(v) => {
|
||||
for ty in v.pos.iter() {
|
||||
self.weaken(ty);
|
||||
}
|
||||
for (_, ty) in v.named.iter() {
|
||||
self.weaken(ty);
|
||||
}
|
||||
if let Some(ty) = &v.rest {
|
||||
self.weaken(ty);
|
||||
}
|
||||
self.weaken(&v.ret);
|
||||
}
|
||||
FlowType::Dict(v) => {
|
||||
for (_, ty, _) in v.fields.iter() {
|
||||
self.weaken(ty);
|
||||
}
|
||||
}
|
||||
FlowType::Array(v) => {
|
||||
self.weaken(v);
|
||||
}
|
||||
FlowType::Tuple(v) => {
|
||||
for ty in v.iter() {
|
||||
self.weaken(ty);
|
||||
}
|
||||
}
|
||||
FlowType::With(v) => {
|
||||
self.weaken(&v.0);
|
||||
for args in v.1.iter() {
|
||||
for ty in args.args.iter() {
|
||||
self.weaken(ty);
|
||||
}
|
||||
for (_, ty) in args.named.iter() {
|
||||
self.weaken(ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
FlowType::Args(v) => {
|
||||
for ty in v.args.iter() {
|
||||
self.weaken(ty);
|
||||
}
|
||||
for (_, ty) in v.named.iter() {
|
||||
self.weaken(ty);
|
||||
}
|
||||
}
|
||||
FlowType::At(v) => {
|
||||
self.weaken(&v.0 .0);
|
||||
}
|
||||
FlowType::Unary(v) => {
|
||||
self.weaken(&v.lhs);
|
||||
}
|
||||
FlowType::Binary(v) => {
|
||||
let (lhs, rhs) = v.repr();
|
||||
self.weaken(lhs);
|
||||
self.weaken(rhs);
|
||||
}
|
||||
FlowType::If(v) => {
|
||||
self.weaken(&v.cond);
|
||||
self.weaken(&v.then);
|
||||
self.weaken(&v.else_);
|
||||
}
|
||||
FlowType::Union(v) => {
|
||||
for ty in v.iter() {
|
||||
self.weaken(ty);
|
||||
}
|
||||
}
|
||||
FlowType::Let(v) => {
|
||||
for ty in v.lbs.iter() {
|
||||
self.weaken(ty);
|
||||
}
|
||||
for ty in v.ubs.iter() {
|
||||
self.weaken(ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn weaken_constraint(&self, c: &FlowType, kind: &FlowVarKind) -> FlowType {
|
||||
if matches!(kind, FlowVarKind::Strong(_)) {
|
||||
return c.clone();
|
||||
}
|
||||
|
||||
if let FlowType::Value(v) = c {
|
||||
return FlowBuiltinType::from_value(&v.0);
|
||||
}
|
||||
|
||||
c.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -1460,7 +1588,7 @@ impl<'a, 'b> TypeSimplifier<'a, 'b> {
|
|||
FlowType::Var(v) => {
|
||||
let w = self.vars.get(&v.0).unwrap();
|
||||
match &w.kind {
|
||||
FlowVarKind::Weak(w) => {
|
||||
FlowVarKind::Strong(w) | FlowVarKind::Weak(w) => {
|
||||
let w = w.read();
|
||||
let inserted = if pol {
|
||||
self.positives.insert(v.0)
|
||||
|
@ -1514,6 +1642,9 @@ impl<'a, 'b> TypeSimplifier<'a, 'b> {
|
|||
for arg in m.args.iter() {
|
||||
self.analyze(arg, pol);
|
||||
}
|
||||
for (_, arg) in m.named.iter() {
|
||||
self.analyze(arg, pol);
|
||||
}
|
||||
}
|
||||
}
|
||||
FlowType::Args(args) => {
|
||||
|
@ -1581,7 +1712,7 @@ impl<'a, 'b> TypeSimplifier<'a, 'b> {
|
|||
.insert((v.0, self.principal), FlowType::Any);
|
||||
|
||||
let res = match &self.vars.get(&v.0).unwrap().kind {
|
||||
FlowVarKind::Weak(w) => {
|
||||
FlowVarKind::Strong(w) | FlowVarKind::Weak(w) => {
|
||||
let w = w.read();
|
||||
|
||||
self.transform_let(&w, Some(&v.0), pol)
|
||||
|
@ -1631,7 +1762,23 @@ impl<'a, 'b> TypeSimplifier<'a, 'b> {
|
|||
}
|
||||
FlowType::With(w) => {
|
||||
let primary = self.transform(&w.0, pol);
|
||||
FlowType::With(Box::new((primary, w.1.clone())))
|
||||
let args =
|
||||
w.1.iter()
|
||||
.map(|a| {
|
||||
let args_res = a.args.iter().map(|a| self.transform(a, pol)).collect();
|
||||
let named = a
|
||||
.named
|
||||
.iter()
|
||||
.map(|(n, a)| (n.clone(), self.transform(a, pol)))
|
||||
.collect();
|
||||
|
||||
FlowArgs {
|
||||
args: args_res,
|
||||
named,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
FlowType::With(Box::new((primary, args)))
|
||||
}
|
||||
FlowType::Args(args) => {
|
||||
let args_res = args.args.iter().map(|a| self.transform(a, pol)).collect();
|
||||
|
|
|
@ -2,7 +2,8 @@ use ecow::EcoVec;
|
|||
use once_cell::sync::Lazy;
|
||||
use regex::RegexSet;
|
||||
use typst::{
|
||||
foundations::{Func, ParamInfo, Type, Value},
|
||||
foundations::{AutoValue, Content, Func, NoneValue, ParamInfo, Type, Value},
|
||||
layout::Length,
|
||||
syntax::Span,
|
||||
};
|
||||
|
||||
|
@ -100,9 +101,45 @@ pub(crate) enum FlowBuiltinType {
|
|||
Outset,
|
||||
Radius,
|
||||
|
||||
Type(typst::foundations::Type),
|
||||
Path(PathPreference),
|
||||
}
|
||||
|
||||
impl FlowBuiltinType {
|
||||
pub fn from_value(builtin: &Value) -> FlowType {
|
||||
if let Value::Bool(v) = builtin {
|
||||
return FlowType::Boolean(Some(*v));
|
||||
}
|
||||
|
||||
Self::from_builtin(builtin.ty())
|
||||
}
|
||||
|
||||
pub fn from_builtin(builtin: Type) -> FlowType {
|
||||
if builtin == Type::of::<AutoValue>() {
|
||||
return FlowType::Auto;
|
||||
}
|
||||
if builtin == Type::of::<NoneValue>() {
|
||||
return FlowType::None;
|
||||
}
|
||||
if builtin == Type::of::<typst::visualize::Color>() {
|
||||
return Color.literally();
|
||||
}
|
||||
if builtin == Type::of::<bool>() {
|
||||
return FlowType::None;
|
||||
}
|
||||
if builtin == Type::of::<f64>() {
|
||||
return Float.literally();
|
||||
}
|
||||
if builtin == Type::of::<Length>() {
|
||||
return Length.literally();
|
||||
}
|
||||
if builtin == Type::of::<Content>() {
|
||||
return FlowType::Content;
|
||||
}
|
||||
|
||||
FlowBuiltinType::Type(builtin).literally()
|
||||
}
|
||||
|
||||
pub(crate) fn describe(&self) -> &'static str {
|
||||
match self {
|
||||
FlowBuiltinType::Args => "args",
|
||||
|
@ -119,6 +156,7 @@ impl FlowBuiltinType {
|
|||
FlowBuiltinType::Inset => "inset",
|
||||
FlowBuiltinType::Outset => "outset",
|
||||
FlowBuiltinType::Radius => "radius",
|
||||
FlowBuiltinType::Type(ty) => ty.short_name(),
|
||||
FlowBuiltinType::Path(s) => match s {
|
||||
PathPreference::None => "[any]",
|
||||
PathPreference::Special => "[any]",
|
||||
|
|
|
@ -283,6 +283,7 @@ impl fmt::Debug for FlowVarStore {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum FlowVarKind {
|
||||
Strong(Arc<RwLock<FlowVarStore>>),
|
||||
Weak(Arc<RwLock<FlowVarStore>>),
|
||||
}
|
||||
|
||||
|
@ -304,8 +305,7 @@ impl fmt::Debug for FlowVar {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "@{}", self.name)?;
|
||||
match &self.kind {
|
||||
// FlowVarKind::Strong(t) => write!(f, " = {:?}", t),
|
||||
FlowVarKind::Weak(w) => write!(f, "{w:?}"),
|
||||
FlowVarKind::Strong(w) | FlowVarKind::Weak(w) => write!(f, "{w:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -326,21 +326,19 @@ impl FlowVar {
|
|||
pub fn ever_be(&self, exp: FlowType) {
|
||||
match &self.kind {
|
||||
// FlowVarKind::Strong(_t) => {}
|
||||
FlowVarKind::Weak(w) => {
|
||||
FlowVarKind::Strong(w) | FlowVarKind::Weak(w) => {
|
||||
let mut w = w.write();
|
||||
w.lbs.push(exp.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_strong(&mut self, exp: FlowType) {
|
||||
// self.kind = FlowVarKind::Strong(value);
|
||||
pub(crate) fn weaken(&mut self) {
|
||||
match &self.kind {
|
||||
// FlowVarKind::Strong(_t) => {}
|
||||
FlowVarKind::Weak(w) => {
|
||||
let mut w = w.write();
|
||||
w.lbs.push(exp.clone());
|
||||
FlowVarKind::Strong(w) => {
|
||||
self.kind = FlowVarKind::Weak(w.clone());
|
||||
}
|
||||
FlowVarKind::Weak(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -433,7 +433,7 @@ impl<'a, 'w> PostTypeCheckWorker<'a, 'w> {
|
|||
return;
|
||||
};
|
||||
match &v.kind {
|
||||
FlowVarKind::Weak(w) => {
|
||||
FlowVarKind::Strong(w) | FlowVarKind::Weak(w) => {
|
||||
let r = w.read();
|
||||
for lb in &r.ubs {
|
||||
self.check_signatures_(lb, pol, sig_kind, args, checker);
|
||||
|
|
|
@ -5,7 +5,7 @@ input_file: crates/tinymist-query/src/fixtures/type_check/with.typ
|
|||
---
|
||||
"f" = (Any) -> Any
|
||||
"g" = ((Any) -> Any).with(..[&(1)])
|
||||
"x" = ( ⪰ Any | 1)
|
||||
"x" = ( ⪰ Any | Type(Type(integer)))
|
||||
---
|
||||
5..6 -> @f
|
||||
7..8 -> @x
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#let tmpl(content) = {
|
||||
content
|
||||
}
|
||||
|
||||
#(tmpl(1))
|
||||
#(tmpl(2))
|
||||
#(tmpl(3))
|
||||
|
||||
#(/* position after */ tmpl)
|
|
@ -0,0 +1,10 @@
|
|||
#let tmpl(content) = {
|
||||
content = 1
|
||||
content = 2
|
||||
content = 3
|
||||
content
|
||||
}
|
||||
|
||||
#(tmpl(4))
|
||||
|
||||
#(/* position after */ tmpl)
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
description: "Check on \"tmpl\" (93)"
|
||||
expression: literal_type
|
||||
input_file: crates/tinymist-query/src/fixtures/type_describe/ever_call.typ
|
||||
---
|
||||
(int) => int
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
description: "Check on \"tmpl\" (135)"
|
||||
expression: literal_type
|
||||
input_file: crates/tinymist-query/src/fixtures/type_describe/ever_intern_use.typ
|
||||
---
|
||||
(1 | 2 | 3 | int) => 1 | 2 | 3 | int
|
|
@ -861,6 +861,35 @@ fn type_completion(
|
|||
FlowBuiltinType::Float => {
|
||||
ctx.snippet_completion("exponential notation", "${1}e${0}", "Exponential notation");
|
||||
}
|
||||
FlowBuiltinType::Type(ty) => {
|
||||
if *ty == Type::of::<NoneValue>() {
|
||||
type_completion(ctx, Some(&FlowType::None), docs);
|
||||
} else if *ty == Type::of::<AutoValue>() {
|
||||
type_completion(ctx, Some(&FlowType::Auto), docs);
|
||||
} else if *ty == Type::of::<bool>() {
|
||||
ctx.snippet_completion("false", "false", "No / Disabled.");
|
||||
ctx.snippet_completion("true", "true", "Yes / Enabled.");
|
||||
} else if *ty == Type::of::<Color>() {
|
||||
type_completion(ctx, Some(&FlowType::Builtin(FlowBuiltinType::Color)), None);
|
||||
} else if *ty == Type::of::<Label>() {
|
||||
ctx.label_completions()
|
||||
} else if *ty == Type::of::<Func>() {
|
||||
ctx.snippet_completion(
|
||||
"function",
|
||||
"(${params}) => ${output}",
|
||||
"A custom function.",
|
||||
);
|
||||
} else {
|
||||
ctx.completions.push(Completion {
|
||||
kind: CompletionKind::Syntax,
|
||||
label: ty.long_name().into(),
|
||||
apply: Some(eco_format!("${{{ty}}}")),
|
||||
detail: Some(eco_format!("A value of type {ty}.")),
|
||||
..Completion::default()
|
||||
});
|
||||
ctx.strict_scope_completions(false, |value| value.ty() == *ty);
|
||||
}
|
||||
}
|
||||
},
|
||||
FlowType::Args(_) => return None,
|
||||
FlowType::Func(_) => return None,
|
||||
|
@ -890,33 +919,11 @@ fn type_completion(
|
|||
}
|
||||
|
||||
if let Value::Type(ty) = &v.0 {
|
||||
if *ty == Type::of::<NoneValue>() {
|
||||
type_completion(ctx, Some(&FlowType::None), docs);
|
||||
} else if *ty == Type::of::<AutoValue>() {
|
||||
type_completion(ctx, Some(&FlowType::Auto), docs);
|
||||
} else if *ty == Type::of::<bool>() {
|
||||
ctx.snippet_completion("false", "false", "No / Disabled.");
|
||||
ctx.snippet_completion("true", "true", "Yes / Enabled.");
|
||||
} else if *ty == Type::of::<Color>() {
|
||||
type_completion(ctx, Some(&FlowType::Builtin(FlowBuiltinType::Color)), None);
|
||||
} else if *ty == Type::of::<Label>() {
|
||||
ctx.label_completions()
|
||||
} else if *ty == Type::of::<Func>() {
|
||||
ctx.snippet_completion(
|
||||
"function",
|
||||
"(${params}) => ${output}",
|
||||
"A custom function.",
|
||||
);
|
||||
} else {
|
||||
ctx.completions.push(Completion {
|
||||
kind: CompletionKind::Syntax,
|
||||
label: ty.long_name().into(),
|
||||
apply: Some(eco_format!("${{{ty}}}")),
|
||||
detail: Some(eco_format!("A value of type {ty}.")),
|
||||
..Completion::default()
|
||||
});
|
||||
ctx.strict_scope_completions(false, |value| value.ty() == *ty);
|
||||
}
|
||||
type_completion(
|
||||
ctx,
|
||||
Some(&FlowType::Builtin(FlowBuiltinType::Type(*ty))),
|
||||
docs,
|
||||
);
|
||||
} else if v.0.ty() == Type::of::<NoneValue>() {
|
||||
type_completion(ctx, Some(&FlowType::None), docs);
|
||||
} else if v.0.ty() == Type::of::<AutoValue>() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue