dev: weaken inference from outer use (#250)

* dev: weaken inference from outer use

* >_<
This commit is contained in:
Myriad-Dreamin 2024-05-07 19:21:13 +08:00 committed by GitHub
parent ca5b87efba
commit 1f9719a420
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 278 additions and 55 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,9 @@
#let tmpl(content) = {
content
}
#(tmpl(1))
#(tmpl(2))
#(tmpl(3))
#(/* position after */ tmpl)

View file

@ -0,0 +1,10 @@
#let tmpl(content) = {
content = 1
content = 2
content = 3
content
}
#(tmpl(4))
#(/* position after */ tmpl)

View file

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

View file

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

View file

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