1use std::collections::{BTreeMap, BTreeSet, HashSet};
4
5use crate::{NamespaceImportBinding, Node, SNode, TypeExpr, TypePredicate};
6
7const MAX_INLINE_DEPTH: usize = 16;
8
9#[derive(Debug, Clone, PartialEq)]
11pub struct NamespaceMemberSignature {
12 pub param_names: Vec<String>,
13 pub required_params: usize,
15 pub fn_type: TypeExpr,
16 pub type_predicate: Option<TypePredicate>,
17}
18
19impl NamespaceImportBinding {
20 pub fn from_member_signatures(
22 module_path: String,
23 members: BTreeSet<String>,
24 signatures: BTreeMap<String, NamespaceMemberSignature>,
25 ) -> Self {
26 let mut binding = Self {
27 module_path,
28 members,
29 member_types: BTreeMap::new(),
30 member_param_names: BTreeMap::new(),
31 member_required_params: BTreeMap::new(),
32 member_type_predicates: BTreeMap::new(),
33 };
34 for (name, signature) in signatures {
35 binding.member_types.insert(name.clone(), signature.fn_type);
36 binding
37 .member_param_names
38 .insert(name.clone(), signature.param_names);
39 binding
40 .member_required_params
41 .insert(name.clone(), signature.required_params);
42 if let Some(predicate) = signature.type_predicate {
43 binding.member_type_predicates.insert(name, predicate);
44 }
45 }
46 binding
47 }
48}
49
50pub fn lower_callable_signature(
55 declaration: &SNode,
56 resolve_alias: impl Fn(&str) -> Option<SNode>,
57) -> Option<NamespaceMemberSignature> {
58 let inner = match &declaration.node {
59 Node::AttributedDecl { inner, .. } => inner.as_ref(),
60 _ => declaration,
61 };
62 let (params, return_type, type_predicate) = match &inner.node {
63 Node::FnDecl {
64 params,
65 return_type,
66 type_predicate,
67 type_params,
68 ..
69 } if type_params.is_empty() => (params, return_type, type_predicate.as_ref()),
70 Node::Pipeline {
71 params,
72 return_type,
73 ..
74 } => (params, return_type, None),
75 _ => return None,
76 };
77 if params.iter().any(|param| param.rest) {
78 return None;
79 }
80 let inline = |ty: &TypeExpr| inline_named_types(ty, &resolve_alias, &mut HashSet::new(), 0);
81 let lowered = params
82 .iter()
83 .map(|param| match ¶m.type_expr {
84 Some(ty) if param.default_value.is_none() => inline(ty),
85 _ => TypeExpr::Named("any".into()),
86 })
87 .collect();
88 let ret = return_type
89 .as_ref()
90 .map(inline)
91 .unwrap_or(TypeExpr::Named("any".into()));
92 let type_predicate = type_predicate.and_then(|predicate| {
93 let type_expr = inline(&predicate.type_expr);
94 (!contains_gradual_type(&type_expr)).then(|| TypePredicate {
95 parameter: predicate.parameter.clone(),
96 type_expr,
97 one_sided: predicate.one_sided,
98 span: predicate.span,
99 })
100 });
101 Some(NamespaceMemberSignature {
102 param_names: params.iter().map(|param| param.name.clone()).collect(),
103 required_params: params
104 .iter()
105 .position(|param| param.default_value.is_some())
106 .unwrap_or(params.len()),
107 fn_type: TypeExpr::FnType {
108 params: lowered,
109 return_type: Box::new(ret),
110 },
111 type_predicate,
112 })
113}
114
115fn is_builtin_type_name(name: &str) -> bool {
116 matches!(
117 name,
118 "int"
119 | "float"
120 | "string"
121 | "bool"
122 | "nil"
123 | "list"
124 | "dict"
125 | "set"
126 | "closure"
127 | "bytes"
128 | "any"
129 | "unknown"
130 | "never"
131 | "number"
132 | "Harness"
133 | "_"
134 )
135}
136
137fn contains_gradual_type(ty: &TypeExpr) -> bool {
138 match ty {
139 TypeExpr::Named(name) => matches!(name.as_str(), "any" | "unknown" | "_"),
140 TypeExpr::Union(items) | TypeExpr::Intersection(items) | TypeExpr::Tuple(items) => {
141 items.iter().any(contains_gradual_type)
142 }
143 TypeExpr::Shape(fields) => fields
144 .iter()
145 .any(|field| contains_gradual_type(&field.type_expr)),
146 TypeExpr::OpenShape { fields, rests } => {
147 fields
148 .iter()
149 .any(|field| contains_gradual_type(&field.type_expr))
150 || rests.iter().any(contains_gradual_type)
151 }
152 TypeExpr::List(inner)
153 | TypeExpr::Iter(inner)
154 | TypeExpr::Generator(inner)
155 | TypeExpr::Stream(inner)
156 | TypeExpr::Owned(inner) => contains_gradual_type(inner),
157 TypeExpr::DictType(key, value) => {
158 contains_gradual_type(key) || contains_gradual_type(value)
159 }
160 TypeExpr::Applied { args, .. } => args.iter().any(contains_gradual_type),
161 TypeExpr::FnType {
162 params,
163 return_type,
164 } => params.iter().any(contains_gradual_type) || contains_gradual_type(return_type),
165 TypeExpr::Never | TypeExpr::LitString(_) | TypeExpr::LitInt(_) => false,
166 }
167}
168
169fn inline_named_types(
170 ty: &TypeExpr,
171 resolve_alias: &impl Fn(&str) -> Option<SNode>,
172 visited: &mut HashSet<String>,
173 depth: usize,
174) -> TypeExpr {
175 let recurse = |inner: &TypeExpr, visited: &mut HashSet<String>| {
176 inline_named_types(inner, resolve_alias, visited, depth + 1)
177 };
178 match ty {
179 TypeExpr::Named(name) => {
180 if is_builtin_type_name(name) {
181 return ty.clone();
182 }
183 if depth >= MAX_INLINE_DEPTH || !visited.insert(name.clone()) {
184 return TypeExpr::Named("any".into());
185 }
186 let resolved = resolve_alias(name);
187 let body = match resolved.as_ref().map(|decl| &decl.node) {
188 Some(Node::TypeDecl {
189 type_params,
190 type_expr,
191 ..
192 }) if type_params.is_empty() => {
193 inline_named_types(type_expr, resolve_alias, visited, depth + 1)
194 }
195 _ => TypeExpr::Named("any".into()),
196 };
197 visited.remove(name);
198 body
199 }
200 TypeExpr::Union(items) => {
201 TypeExpr::Union(items.iter().map(|item| recurse(item, visited)).collect())
202 }
203 TypeExpr::Intersection(items) => {
204 TypeExpr::Intersection(items.iter().map(|item| recurse(item, visited)).collect())
205 }
206 TypeExpr::Shape(fields) => TypeExpr::Shape(
207 fields
208 .iter()
209 .map(|field| {
210 let mut next = field.clone();
211 next.type_expr = recurse(&field.type_expr, visited);
212 next
213 })
214 .collect(),
215 ),
216 TypeExpr::List(inner) => TypeExpr::List(Box::new(recurse(inner, visited))),
217 TypeExpr::Iter(inner) => TypeExpr::Iter(Box::new(recurse(inner, visited))),
218 TypeExpr::Owned(inner) => TypeExpr::Owned(Box::new(recurse(inner, visited))),
219 TypeExpr::Tuple(items) => {
220 TypeExpr::Tuple(items.iter().map(|item| recurse(item, visited)).collect())
221 }
222 TypeExpr::DictType(key, value) => TypeExpr::DictType(
223 Box::new(recurse(key, visited)),
224 Box::new(recurse(value, visited)),
225 ),
226 TypeExpr::OpenShape { .. }
227 | TypeExpr::Generator(_)
228 | TypeExpr::Stream(_)
229 | TypeExpr::Applied { .. }
230 | TypeExpr::FnType { .. } => TypeExpr::Named("any".into()),
231 TypeExpr::Never | TypeExpr::LitString(_) | TypeExpr::LitInt(_) => ty.clone(),
232 }
233}