diff --git a/crates/tinymist-query/src/analysis/global.rs b/crates/tinymist-query/src/analysis/global.rs index c5d4f083..693b49ff 100644 --- a/crates/tinymist-query/src/analysis/global.rs +++ b/crates/tinymist-query/src/analysis/global.rs @@ -71,6 +71,8 @@ impl Analysis { pub fn clear_cache(&self) { self.caches.signatures.clear(); self.caches.static_signatures.clear(); + self.caches.docstrings.clear(); + self.caches.terms.clear(); self.caches.def_use.clear(); self.caches.type_check.clear(); } @@ -85,8 +87,9 @@ pub struct AnalysisGlobalCaches { def_use: FxDashMap>)>, type_check: FxDashMap>)>, static_signatures: FxDashMap)>, - docstrings: FxDashMap>>, + docstrings: FxDashMap>)>, signatures: FxDashMap)>, + terms: FxDashMap, } /// A cache for all level of analysis results of a module. @@ -376,11 +379,6 @@ impl<'w> AnalysisContext<'w> { Some((cursor, get_deref_target(node, cursor))) } - /// Get the module-level analysis cache of a file. - pub fn get(&self, file_id: TypstFileId) -> Option<&ModuleAnalysisCache> { - self.caches.modules.get(&file_id) - } - /// Fork a new context for searching in the workspace. pub fn fork_for_search<'s>(&'s mut self) -> SearchCtx<'s, 'w> { SearchCtx { @@ -443,6 +441,11 @@ impl<'w> AnalysisContext<'w> { analyze_signature(self, SignatureTarget::Runtime(func)).unwrap() } + pub(crate) fn type_of_func(&mut self, func: Func) -> Signature { + log::debug!("convert runtime func {func:?}"); + analyze_signature(self, SignatureTarget::Convert(func)).unwrap() + } + /// Compute the signature of a function. pub fn compute_signature( &mut self, @@ -450,7 +453,7 @@ impl<'w> AnalysisContext<'w> { compute: impl FnOnce(&mut Self) -> Option, ) -> Option { if let Some(sig) = self.get_signature(&func) { - return Some(sig); + return sig; } let res = compute(self); match func { @@ -481,21 +484,27 @@ impl<'w> AnalysisContext<'w> { .3 .clone() } - SignatureTarget::Runtime(rt) => { - let key = hash128(&rt); - self.analysis - .caches - .signatures - .entry(key) - .or_insert_with(|| (self.lifetime, rt, res)) - .2 - .clone() - } + SignatureTarget::Convert(rt) => self + .analysis + .caches + .signatures + .entry(hash128(&(&rt, true))) + .or_insert_with(|| (self.lifetime, rt, res)) + .2 + .clone(), + SignatureTarget::Runtime(rt) => self + .analysis + .caches + .signatures + .entry(hash128(&rt)) + .or_insert_with(|| (self.lifetime, rt, res)) + .2 + .clone(), } } /// Get the signature of a function. - fn get_signature(&self, func: &SignatureTarget) -> Option { + fn get_signature(&self, func: &SignatureTarget) -> Option> { match func { SignatureTarget::Def(source, r) => { // todo: check performance on peeking signature source frequently @@ -524,6 +533,12 @@ impl<'w> AnalysisContext<'w> { .get(&hash128(&cache_key)) .and_then(|slot| (cache_key.1 == slot.2).then_some(slot.3.clone())) } + SignatureTarget::Convert(rt) => self + .analysis + .caches + .signatures + .get(&hash128(&(&rt, true))) + .and_then(|slot| (rt == &slot.1).then_some(slot.2.clone())), SignatureTarget::Runtime(rt) => self .analysis .caches @@ -531,7 +546,32 @@ impl<'w> AnalysisContext<'w> { .get(&hash128(rt)) .and_then(|slot| (rt == &slot.1).then_some(slot.2.clone())), } - .flatten() + } + + pub(crate) fn type_of_value(&mut self, val: &Value) -> Ty { + log::debug!("convert runtime value {val:?}"); + + // todo: check performance on peeking signature source frequently + let cache_key = val; + let cached = self + .analysis + .caches + .terms + .get(&hash128(&cache_key)) + .and_then(|slot| (cache_key == &slot.1).then_some(slot.2.clone())); + if let Some(cached) = cached { + return cached; + } + + let res = crate::analysis::term_value(self, val); + + self.analysis + .caches + .terms + .entry(hash128(&cache_key)) + .or_insert_with(|| (self.lifetime, cache_key.clone(), res.clone())); + + res } pub(crate) fn signature_docs( @@ -553,10 +593,13 @@ impl<'w> AnalysisContext<'w> { ) -> Option> { let h = hash128(&(&fid, &docs, &kind)); let res = if let Some(res) = self.analysis.caches.docstrings.get(&h) { - res.clone() + res.1.clone() } else { let res = crate::analysis::tyck::compute_docstring(self, fid, docs, kind).map(Arc::new); - self.analysis.caches.docstrings.insert(h, res.clone()); + self.analysis + .caches + .docstrings + .insert(h, (self.lifetime, res.clone())); res }; @@ -608,6 +651,7 @@ impl<'w> AnalysisContext<'w> { /// Get the def-use information of a source file. pub fn def_use(&mut self, source: Source) -> Option> { let mut search_ctx = self.fork_for_search(); + Self::def_use_(&mut search_ctx, source) } @@ -799,6 +843,14 @@ impl<'w> AnalysisContext<'w> { .caches .static_signatures .retain(|_, (l, _, _, _)| lifetime - *l < 60); + self.analysis + .caches + .terms + .retain(|_, (l, _, _)| lifetime - *l < 60); + self.analysis + .caches + .docstrings + .retain(|_, (l, _)| lifetime - *l < 60); self.analysis .caches .signatures diff --git a/crates/tinymist-query/src/analysis/post_tyck.rs b/crates/tinymist-query/src/analysis/post_tyck.rs index df554235..c150c343 100644 --- a/crates/tinymist-query/src/analysis/post_tyck.rs +++ b/crates/tinymist-query/src/analysis/post_tyck.rs @@ -134,7 +134,11 @@ impl<'a, 'w> TyCtxMut for PostTypeCheckWorker<'a, 'w> { } fn type_of_func(&mut self, func: &Func) -> Option> { - Some(self.ctx.signature_dyn(func.clone()).type_sig()) + Some(self.ctx.type_of_func(func.clone()).type_sig()) + } + + fn type_of_value(&mut self, val: &Value) -> Ty { + self.ctx.type_of_value(val) } } diff --git a/crates/tinymist-query/src/analysis/signature.rs b/crates/tinymist-query/src/analysis/signature.rs index 124599a2..31feccea 100644 --- a/crates/tinymist-query/src/analysis/signature.rs +++ b/crates/tinymist-query/src/analysis/signature.rs @@ -1,8 +1,8 @@ //! Analysis of function signatures. -use typst::foundations::{Closure, ParamInfo}; +use typst::foundations::{self, Closure, ParamInfo}; -use super::{prelude::*, resolve_callee, SigTy}; +use super::{prelude::*, resolve_callee, BuiltinTy, SigTy, TypeSources}; use crate::docs::UntypedSymbolDocs; use crate::syntax::get_non_strict_def_target; use crate::upstream::truncated_repr; @@ -204,6 +204,8 @@ pub enum SignatureTarget<'a> { Syntax(Source, LinkedNode<'a>), /// A function that is known at runtime. Runtime(Func), + /// A function that is known at runtime. + Convert(Func), } pub(crate) fn analyze_signature( @@ -222,7 +224,7 @@ fn analyze_type_signature( callee_node: &SignatureTarget<'_>, ) -> Option { let (type_info, ty) = match callee_node { - SignatureTarget::Def(..) => None, + SignatureTarget::Def(..) | SignatureTarget::Convert(..) => None, SignatureTarget::SyntaxFast(source, node) | SignatureTarget::Syntax(source, node) => { let type_info = ctx.type_check(source)?; let ty = type_info.type_of_span(node.span())?; @@ -237,73 +239,99 @@ fn analyze_type_signature( Some((type_info, ty)) } }?; - let type_var = ty.sources().into_iter().next()?; - let sig_ty = ty.sig_repr(true)?; + log::debug!("check type signature of ty: {ty:?}"); - let docstring = match type_info.var_docs.get(&type_var.def).map(|x| x.as_ref()) { - Some(UntypedSymbolDocs::Function(sig)) => sig, - _ => return None, - }; + // todo multiple sources + let mut srcs = ty.sources(); + srcs.sort(); + let type_var = srcs.into_iter().next()?; + match type_var { + TypeSources::Var(v) => { + let sig_ty = ty.sig_repr(true)?; - let mut param_specs = Vec::new(); - let mut has_fill_or_size_or_stroke = false; - let mut _broken = false; + let docstring = match type_info.var_docs.get(&v.def).map(|x| x.as_ref()) { + Some(UntypedSymbolDocs::Function(sig)) => sig, + _ => return None, + }; - for (doc, ty) in docstring.pos.iter().zip(sig_ty.positional_params()) { - let default = doc.default.clone(); - let ty = ty.clone(); + let mut param_specs = Vec::new(); + let mut has_fill_or_size_or_stroke = false; + let mut _broken = false; - let name = doc.name.clone(); - if matches!(name.as_ref(), "fill" | "stroke" | "size") { - has_fill_or_size_or_stroke = true; + for (doc, ty) in docstring.pos.iter().zip(sig_ty.positional_params()) { + let default = doc.default.clone(); + let ty = ty.clone(); + + let name = doc.name.clone(); + if matches!(name.as_ref(), "fill" | "stroke" | "size") { + has_fill_or_size_or_stroke = true; + } + + param_specs.push(ParamSpec { + name, + docs: Some(doc.docs.clone()), + default, + ty, + attrs: ParamAttrs::positional(), + }); + } + + for (name, ty) in sig_ty.named_params() { + let doc = docstring.named.get(name).unwrap(); + let default = doc.default.clone(); + let ty = ty.clone(); + + if matches!(name.as_ref(), "fill" | "stroke" | "size") { + has_fill_or_size_or_stroke = true; + } + + param_specs.push(ParamSpec { + name: name.clone(), + docs: Some(doc.docs.clone()), + default, + ty, + attrs: ParamAttrs::named(), + }); + } + + if let Some(doc) = docstring.rest.as_ref() { + let default = doc.default.clone(); + + param_specs.push(ParamSpec { + name: doc.name.clone(), + docs: Some(doc.docs.clone()), + default, + ty: sig_ty.rest_param().cloned().unwrap_or(Ty::Any), + attrs: ParamAttrs::variadic(), + }); + } + + Some(Signature::Primary(Arc::new(PrimarySignature { + docs: Some(docstring.docs.clone()), + param_specs, + has_fill_or_size_or_stroke, + sig_ty, + _broken, + }))) } - - param_specs.push(ParamSpec { - name, - docs: Some(doc.docs.clone()), - default, - ty, - attrs: ParamAttrs::positional(), - }); - } - - for (name, ty) in sig_ty.named_params() { - let doc = docstring.named.get(name).unwrap(); - let default = doc.default.clone(); - let ty = ty.clone(); - - if matches!(name.as_ref(), "fill" | "stroke" | "size") { - has_fill_or_size_or_stroke = true; + TypeSources::Builtin(BuiltinTy::Type(ty)) => { + let cons = ty.constructor().ok()?; + Some(ctx.type_of_func(cons)) } - - param_specs.push(ParamSpec { - name: name.clone(), - docs: Some(doc.docs.clone()), - default, - ty, - attrs: ParamAttrs::named(), - }); + TypeSources::Builtin(BuiltinTy::Element(ty)) => { + let cons: Func = ty.into(); + Some(ctx.type_of_func(cons)) + } + TypeSources::Builtin(..) => None, + TypeSources::Ins(i) => match &i.val { + foundations::Value::Func(f) => Some(ctx.type_of_func(f.clone())), + foundations::Value::Type(f) => { + let cons = f.constructor().ok()?; + Some(ctx.type_of_func(cons)) + } + _ => None, + }, } - - if let Some(doc) = docstring.rest.as_ref() { - let default = doc.default.clone(); - - param_specs.push(ParamSpec { - name: doc.name.clone(), - docs: Some(doc.docs.clone()), - default, - ty: sig_ty.rest_param().cloned().unwrap_or(Ty::Any), - attrs: ParamAttrs::variadic(), - }); - } - - Some(Signature::Primary(Arc::new(PrimarySignature { - docs: Some(docstring.docs.clone()), - param_specs, - has_fill_or_size_or_stroke, - sig_ty, - _broken, - }))) } fn analyze_dyn_signature( @@ -318,7 +346,7 @@ fn analyze_dyn_signature( log::debug!("got function {func:?}"); func } - SignatureTarget::Runtime(func) => func.clone(), + SignatureTarget::Convert(func) | SignatureTarget::Runtime(func) => func.clone(), }; use typst::foundations::func::Repr; diff --git a/crates/tinymist-query/src/analysis/tyck.rs b/crates/tinymist-query/src/analysis/tyck.rs index 03f8e8b3..a97a5e30 100644 --- a/crates/tinymist-query/src/analysis/tyck.rs +++ b/crates/tinymist-query/src/analysis/tyck.rs @@ -9,11 +9,13 @@ use super::{ use crate::ty::*; mod apply; +mod convert; mod docs; mod select; mod syntax; pub(crate) use apply::*; +pub(crate) use convert::*; pub(crate) use docs::*; pub(crate) use select::*; @@ -77,7 +79,11 @@ impl<'a, 'w> TyCtxMut for TypeChecker<'a, 'w> { } fn type_of_func(&mut self, func: &Func) -> Option> { - Some(self.ctx.signature_dyn(func.clone()).type_sig()) + Some(self.ctx.type_of_func(func.clone()).type_sig()) + } + + fn type_of_value(&mut self, val: &Value) -> Ty { + self.ctx.type_of_value(val) } } diff --git a/crates/tinymist-query/src/analysis/tyck/convert.rs b/crates/tinymist-query/src/analysis/tyck/convert.rs new file mode 100644 index 00000000..7bb21bb7 --- /dev/null +++ b/crates/tinymist-query/src/analysis/tyck/convert.rs @@ -0,0 +1,95 @@ +use super::*; + +pub fn term_value(ctx: &mut AnalysisContext, value: &Value) -> Ty { + match value { + Value::Array(a) => { + let values = a.iter().map(term_value_rec).collect::>(); + Ty::Tuple(values.into()) + } + // todo: term arguments + Value::Args(..) => Ty::Builtin(BuiltinTy::Args), + Value::Plugin(p) => { + // todo: create infer variables for plugin functions + let values = p + .iter() + .map(|k| (k.as_str().into(), Ty::Func(SigTy::any()), Span::detached())) + .collect(); + Ty::Dict(RecordTy::new(values)) + } + Value::Dict(d) => { + let values = d + .iter() + .map(|(k, v)| (k.as_str().into(), term_value_rec(v), Span::detached())) + .collect(); + Ty::Dict(RecordTy::new(values)) + } + Value::Module(m) => { + let values = m + .scope() + .iter() + .map(|(k, v)| (k.into(), term_value_rec(v), Span::detached())) + .collect(); + Ty::Dict(RecordTy::new(values)) + } + Value::Type(ty) => Ty::Builtin(BuiltinTy::Type(*ty)), + Value::Dyn(v) => Ty::Builtin(BuiltinTy::Type(v.ty())), + Value::Func(func) => Ty::Func(ctx.type_of_func(func.clone()).type_sig()), + Value::Label(..) + | Value::None + | Value::Auto + | Value::Bool(..) + | Value::Int(..) + | Value::Float(..) + | Value::Length(..) + | Value::Angle(..) + | Value::Ratio(..) + | Value::Relative(..) + | Value::Fraction(..) + | Value::Color(..) + | Value::Gradient(..) + | Value::Pattern(..) + | Value::Symbol(..) + | Value::Version(..) + | Value::Str(..) + | Value::Bytes(..) + | Value::Datetime(..) + | Value::Duration(..) + | Value::Content(..) + | Value::Styles(..) => Ty::Value(InsTy::new(value.clone())), + } +} + +pub fn term_value_rec(value: &Value) -> Ty { + match value { + Value::Type(ty) => Ty::Builtin(BuiltinTy::Type(*ty)), + Value::Dyn(v) => Ty::Builtin(BuiltinTy::Type(v.ty())), + Value::None + | Value::Auto + | Value::Array(..) + | Value::Args(..) + | Value::Plugin(..) + | Value::Dict(..) + | Value::Module(..) + | Value::Func(..) + | Value::Label(..) + | Value::Bool(..) + | Value::Int(..) + | Value::Float(..) + | Value::Length(..) + | Value::Angle(..) + | Value::Ratio(..) + | Value::Relative(..) + | Value::Fraction(..) + | Value::Color(..) + | Value::Gradient(..) + | Value::Pattern(..) + | Value::Symbol(..) + | Value::Version(..) + | Value::Str(..) + | Value::Bytes(..) + | Value::Datetime(..) + | Value::Duration(..) + | Value::Content(..) + | Value::Styles(..) => Ty::Value(InsTy::new(value.clone())), + } +} diff --git a/crates/tinymist-query/src/analysis/tyck/select.rs b/crates/tinymist-query/src/analysis/tyck/select.rs index 5e30ff08..8507c8f0 100644 --- a/crates/tinymist-query/src/analysis/tyck/select.rs +++ b/crates/tinymist-query/src/analysis/tyck/select.rs @@ -8,7 +8,6 @@ use crate::analysis::SelectChecker; pub struct SelectFieldChecker<'a, 'b, 'w> { pub(super) base: &'a mut TypeChecker<'b, 'w>, pub select_site: Span, - pub key: &'a Interned, pub resultant: Vec, } @@ -22,11 +21,12 @@ impl<'a, 'b, 'w> SelectChecker for SelectFieldChecker<'a, 'b, 'w> { self.base.info.witness_at_least(self.select_site, ins); } - let Some(IfaceShape { iface }) = iface.shape(Some(self.base.ctx)) else { + let Some(IfaceShape { iface }) = iface.shape(self.base) else { return; }; - let res = iface.field_by_name(self.key); + let res = iface.field_by_name(key); + log::debug!("selecting field real: {key:?} -> {res:?}"); if let Some(res) = res { self.resultant.push(res.clone()); } diff --git a/crates/tinymist-query/src/analysis/tyck/syntax.rs b/crates/tinymist-query/src/analysis/tyck/syntax.rs index f12ecb62..cfbcd8e9 100644 --- a/crates/tinymist-query/src/analysis/tyck/syntax.rs +++ b/crates/tinymist-query/src/analysis/tyck/syntax.rs @@ -305,13 +305,13 @@ impl<'a, 'w> TypeChecker<'a, 'w> { let select_site = field_access.target().span(); let ty = self.check_expr_in(select_site, root.clone()); let field = Interned::new_str(field_access.field().get()); + log::debug!("field access: {field_access:?} => {ty:?}.{field:?}"); // todo: move this to base let base = Ty::Select(SelectTy::new(ty.clone().into(), field.clone())); let mut worker = SelectFieldChecker { base: self, select_site, - key: &field, resultant: vec![base], }; ty.select(&field, true, &mut worker); diff --git a/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin.typ.snap b/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin.typ.snap index 11a8c9a1..67601683 100644 --- a/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin.typ.snap +++ b/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin.typ.snap @@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs expression: CallSnapshot(result.as_deref()) input_file: crates/tinymist-query/src/fixtures/call_info/builtin.typ --- - +1 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "angle" } diff --git a/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly.typ.snap b/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly.typ.snap index 66d7eabd..4600f47b 100644 --- a/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly.typ.snap +++ b/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly.typ.snap @@ -3,4 +3,6 @@ source: crates/tinymist-query/src/analysis.rs expression: CallSnapshot(result.as_deref()) input_file: crates/tinymist-query/src/fixtures/call_info/builtin_poly.typ --- - +255 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "red" } +255 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "green" } +255 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "blue" } diff --git a/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly2.typ.snap b/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly2.typ.snap index 811739d9..8efac0c9 100644 --- a/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly2.typ.snap +++ b/crates/tinymist-query/src/fixtures/call_info/snaps/test@builtin_poly2.typ.snap @@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs expression: CallSnapshot(result.as_deref()) input_file: crates/tinymist-query/src/fixtures/call_info/builtin_poly2.typ --- - +"#fff" -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "red" } diff --git a/crates/tinymist-query/src/ty/bound.rs b/crates/tinymist-query/src/ty/bound.rs index 1ce63d48..cf61dae0 100644 --- a/crates/tinymist-query/src/ty/bound.rs +++ b/crates/tinymist-query/src/ty/bound.rs @@ -1,9 +1,18 @@ +use typst::foundations; + use crate::ty::prelude::*; pub trait BoundChecker: TyCtx { fn collect(&mut self, ty: &Ty, pol: bool); } +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum TypeSources { + Var(Interned), + Ins(Interned), + Builtin(BuiltinTy), +} + impl Ty { /// Check if the given type has bounds (is combinated). pub fn has_bounds(&self) -> bool { @@ -11,12 +20,12 @@ impl Ty { } /// Get the sources of the given type. - pub fn sources(&self) -> Vec> { + pub fn sources(&self) -> Vec { let mut results = vec![]; - fn collect(ty: &Ty, results: &mut Vec>) { + fn collect(ty: &Ty, results: &mut Vec) { use Ty::*; match ty { - Any | Boolean(_) | If(..) | Builtin(..) | Value(..) => {} + Any | Boolean(_) | If(..) => {} Dict(..) | Array(..) | Tuple(..) | Func(..) | Args(..) => {} Unary(..) | Binary(..) => {} Field(ty) => { @@ -36,8 +45,18 @@ impl Ty { } } Var(ty) => { - results.push(ty.clone()); + results.push(TypeSources::Var(ty.clone())); } + Builtin(ty @ (BuiltinTy::Type(..) | BuiltinTy::Element(..))) => { + results.push(TypeSources::Builtin(ty.clone())); + } + Builtin(..) => {} + Value(ty) => match &ty.val { + foundations::Value::Type(..) | foundations::Value::Func(..) => { + results.push(TypeSources::Ins(ty.clone())); + } + _ => {} + }, With(ty) => collect(&ty.sig, results), Select(ty) => collect(&ty.ty, results), } diff --git a/crates/tinymist-query/src/ty/def.rs b/crates/tinymist-query/src/ty/def.rs index f793f28c..7c22b5f8 100644 --- a/crates/tinymist-query/src/ty/def.rs +++ b/crates/tinymist-query/src/ty/def.rs @@ -561,6 +561,19 @@ impl SigTy { }) } + /// Any constructor + pub fn any() -> Interned { + let rest = Ty::Array(Interned::new(Ty::Any)); + Interned::new(Self { + inputs: Interned::new(vec![rest]), + body: Some(Ty::Any), + names: NameBone::empty(), + name_started: 0, + spread_left: false, + spread_right: true, + }) + } + /// Unary constructor #[comemo::memoize] pub fn unary(inp: Ty, ret: Ty) -> Interned { @@ -1042,6 +1055,10 @@ impl TyCtxMut for TypeScheme { fn type_of_func(&mut self, _func: &typst::foundations::Func) -> Option> { None } + + fn type_of_value(&mut self, _val: &Value) -> Ty { + Ty::Any + } } /// A type variable bounds diff --git a/crates/tinymist-query/src/ty/iface.rs b/crates/tinymist-query/src/ty/iface.rs index 11a99bf0..b430df2e 100644 --- a/crates/tinymist-query/src/ty/iface.rs +++ b/crates/tinymist-query/src/ty/iface.rs @@ -1,4 +1,4 @@ -use typst::foundations::Dict; +use typst::foundations::{Dict, Module}; use super::BoundChecker; use crate::ty::prelude::*; @@ -18,6 +18,10 @@ pub enum Iface<'a> { val: &'a Dict, at: &'a Ty, }, + Module { + val: &'a Module, + at: &'a Ty, + }, ArrayCons(&'a TyRef), Partialize(&'a Iface<'a>), } @@ -34,11 +38,12 @@ impl<'a> Iface<'a> { Iface::Type { val, .. } => Ty::Builtin(BuiltinTy::Type(*val)), Iface::Element { val, .. } => Ty::Builtin(BuiltinTy::Element(*val)), Iface::Value { at, .. } => at.clone(), + Iface::Module { at, .. } => at.clone(), Iface::Partialize(..) => return None, }) } - pub fn shape(self, _ctx: Option<&mut AnalysisContext>) -> Option { + pub fn shape(self, ctx: &mut impl TyCtxMut) -> Option { log::debug!("iface shape: {self:?}"); let record_ins = match self { @@ -50,7 +55,8 @@ impl<'a> Iface<'a> { Iface::Partialize(..) => return None, Iface::Element { .. } => return None, Iface::Type { .. } => return None, - Iface::Value { .. } => return None, + Iface::Value { val, at: _ } => ctx.type_of_dict(val), + Iface::Module { val, at: _ } => ctx.type_of_module(val), }; Some(IfaceShape { iface: record_ins }) @@ -142,10 +148,13 @@ impl<'a> IfaceCheckDriver<'a> { Ty::Value(v) => { if self.value_as_iface() { match &v.val { - // Value::Func(f) => { - // self.checker - // .check(Iface::Value { val: f, at: ty }, &mut self.ctx, pol); - // } + Value::Module(t) => { + self.checker.check( + Iface::Module { val: t, at: ty }, + &mut self.ctx, + pol, + ); + } Value::Dict(d) => { self.checker .check(Iface::Value { val: d, at: ty }, &mut self.ctx, pol); diff --git a/crates/tinymist-query/src/ty/mod.rs b/crates/tinymist-query/src/ty/mod.rs index a737fba6..98f352be 100644 --- a/crates/tinymist-query/src/ty/mod.rs +++ b/crates/tinymist-query/src/ty/mod.rs @@ -21,7 +21,7 @@ pub(crate) use iface::*; pub(crate) use mutate::*; pub(crate) use select::*; pub(crate) use sig::*; -use typst::foundations::Func; +use typst::foundations::{self, Func, Module, Value}; /// A type context. pub trait TyCtx { @@ -53,6 +53,24 @@ pub trait TyCtxMut: TyCtx { fn bind_local(&mut self, var: &Interned, ty: Ty); /// Get the type of a runtime function. fn type_of_func(&mut self, func: &Func) -> Option>; + /// Get the type of a runtime value. + fn type_of_value(&mut self, val: &Value) -> Ty; + /// Get the type of a runtime dict. + fn type_of_dict(&mut self, dict: &foundations::Dict) -> Interned { + let ty = self.type_of_value(&Value::Dict(dict.clone())); + let Ty::Dict(ty) = ty else { + panic!("expected dict type, found {ty:?}"); + }; + ty + } + /// Get the type of a runtime module. + fn type_of_module(&mut self, module: &Module) -> Interned { + let ty = self.type_of_value(&Value::Module(module.clone())); + let Ty::Dict(ty) = ty else { + panic!("expected dict type, found {ty:?}"); + }; + ty + } } impl TyCtx for () { @@ -75,6 +93,9 @@ impl TyCtxMut for () { fn type_of_func(&mut self, _func: &Func) -> Option> { None } + fn type_of_value(&mut self, _val: &Value) -> Ty { + Ty::Any + } } #[cfg(test)] diff --git a/crates/tinymist-query/src/ty/prelude.rs b/crates/tinymist-query/src/ty/prelude.rs index 8a9f0df4..9747b524 100644 --- a/crates/tinymist-query/src/ty/prelude.rs +++ b/crates/tinymist-query/src/ty/prelude.rs @@ -5,4 +5,3 @@ pub use typst::foundations::Value; pub use super::builtin::*; pub use super::def::*; -pub use crate::analysis::AnalysisContext;