starlark/eval/compiler/
module.rs1use starlark_syntax::eval_exception::EvalException;
21use starlark_syntax::syntax::ast::LoadP;
22use starlark_syntax::syntax::ast::StmtP;
23use starlark_syntax::syntax::top_level_stmts::top_level_stmts_mut;
24
25use crate::codemap::Spanned;
26use crate::const_frozen_string;
27use crate::eval::bc::frame::alloca_frame;
28use crate::eval::compiler::add_span_to_expr_error;
29use crate::eval::compiler::expr_throw;
30use crate::eval::compiler::scope::payload::CstPayload;
31use crate::eval::compiler::scope::payload::CstStmt;
32use crate::eval::compiler::scope::ScopeId;
33use crate::eval::compiler::scope::Slot;
34use crate::eval::compiler::Compiler;
35use crate::eval::runtime::frame_span::FrameSpan;
36use crate::eval::runtime::frozen_file_span::FrozenFileSpan;
37use crate::typing::bindings::BindingsCollect;
38use crate::typing::error::InternalError;
39use crate::typing::fill_types_for_lint::ModuleVarTypes;
40use crate::typing::mode::TypecheckMode;
41use crate::typing::typecheck::solve_bindings;
42use crate::typing::Ty;
43use crate::typing::TypingOracleCtx;
44use crate::values::FrozenRef;
45use crate::values::FrozenStringValue;
46use crate::values::Value;
47
48#[derive(Debug, thiserror::Error)]
49enum ModuleError {
50 #[error("No imports are available, you tried `{0}` (no call to `Evaluator.set_loader`)")]
51 NoImportsAvailable(String),
52 #[error("Unexpected statement (internal error)")]
53 UnexpectedStatement,
54 #[error("Top level stmt count mismatch (internal error)")]
55 TopLevelStmtCountMismatch,
56}
57
58impl<'v> Compiler<'v, '_, '_, '_> {
59 fn eval_load(&mut self, load: Spanned<&LoadP<CstPayload>>) -> Result<(), EvalException> {
60 let name = &load.node.module.node;
61
62 let span = FrameSpan::new(FrozenFileSpan::new(self.codemap, load.span));
63
64 let loadenv = match self.eval.loader.as_ref() {
65 None => {
66 return Err(add_span_to_expr_error(
67 crate::Error::new_other(ModuleError::NoImportsAvailable(name.to_owned())),
68 span,
69 self.eval,
70 ));
71 }
72 Some(loader) => expr_throw(loader.load(name), span, self.eval)?,
73 };
74
75 for load_arg in &load.node.args {
76 let (slot, _captured) = self
77 .scope_data
78 .get_assign_ident_slot(&load_arg.local, &self.codemap);
79 let slot = match slot {
80 Slot::Local(..) => unreachable!("symbol need to be resolved to module"),
81 Slot::Module(slot) => slot,
82 };
83 let value = expr_throw(
84 self.eval
85 .module_env
86 .load_symbol(&loadenv, &load_arg.their.node),
87 FrameSpan::new(FrozenFileSpan::new(self.codemap, load_arg.span())),
88 self.eval,
89 )?;
90 self.eval.set_slot_module(slot, value)
91 }
92
93 Ok(())
94 }
95
96 fn eval_regular_top_level_stmt(
99 &mut self,
100 stmt: &mut CstStmt,
101 local_names: FrozenRef<'static, [FrozenStringValue]>,
102 ) -> Result<Value<'v>, EvalException> {
103 if matches!(stmt.node, StmtP::Statements(_) | StmtP::Load(_)) {
104 return Err(EvalException::new_anyhow(
105 ModuleError::UnexpectedStatement.into(),
106 stmt.span,
107 &self.codemap,
108 ));
109 }
110
111 let stmt = self
112 .module_top_level_stmt(stmt)
113 .map_err(|e| e.into_eval_exception())?;
114 let bc = stmt.as_bc(
115 &self.compile_context(false),
116 local_names,
117 0,
118 self.eval.module_env.frozen_heap(),
119 );
120 let local_count = local_names.len().try_into().unwrap();
124 alloca_frame(
125 self.eval,
126 local_count,
127 bc.max_stack_size,
128 bc.max_loop_depth,
129 |eval| eval.eval_bc(const_frozen_string!("module").to_value(), &bc),
130 )
131 }
132
133 #[allow(clippy::mut_mut)] fn eval_top_level_stmt(
135 &mut self,
136 stmt: &mut CstStmt,
137 local_names: FrozenRef<'static, [FrozenStringValue]>,
138 ) -> Result<Value<'v>, EvalException> {
139 let mut stmts = top_level_stmts_mut(stmt);
140
141 if stmts.len() != self.top_level_stmt_count {
142 return Err(EvalException::new_anyhow(
143 ModuleError::TopLevelStmtCountMismatch.into(),
144 stmt.span,
145 &self.codemap,
146 ));
147 }
148
149 let mut last = Value::new_none();
150 for stmt in stmts.iter_mut() {
151 self.populate_types_in_stmt(stmt)?;
152
153 match &mut stmt.node {
154 StmtP::Load(load) => {
155 self.eval_load(Spanned {
156 node: load,
157 span: stmt.span,
158 })?;
159 last = Value::new_none();
160 }
161 _ => last = self.eval_regular_top_level_stmt(stmt, local_names)?,
162 }
163 }
164
165 self.typecheck(&mut stmts)?;
166
167 Ok(last)
168 }
169
170 fn typecheck(&mut self, stmts: &mut [&mut CstStmt]) -> Result<(), EvalException> {
171 let typecheck = self.eval.static_typechecking || self.typecheck;
172 if !typecheck {
173 return Ok(());
174 }
175
176 let oracle = TypingOracleCtx {
177 codemap: &self.codemap,
178 };
179 let module_var_types = self.mk_module_var_types();
180 for top in stmts.iter_mut() {
181 if let StmtP::Def(_) = &mut top.node {
182 let BindingsCollect { bindings, .. } = BindingsCollect::collect_one(
183 top,
184 TypecheckMode::Compiler,
185 &self.codemap,
186 &mut Vec::new(),
187 )
188 .map_err(InternalError::into_eval_exception)?;
189 let (errors, ..) = match solve_bindings(bindings, oracle, &module_var_types) {
190 Ok(x) => x,
191 Err(e) => return Err(e.into_eval_exception()),
192 };
193
194 if let Some(error) = errors.into_iter().next() {
195 return Err(error.into_eval_exception());
196 }
197 }
198 }
199
200 Ok(())
201 }
202
203 fn mk_module_var_types(&self) -> ModuleVarTypes {
204 let types = self
205 .eval
206 .module_env
207 .values_by_slot_id()
208 .into_iter()
209 .map(|(module_slot_id, value)| (module_slot_id, Ty::of_value(value)))
210 .collect();
211 ModuleVarTypes { types }
212 }
213
214 pub(crate) fn eval_module(
215 &mut self,
216 mut stmt: CstStmt,
217 local_names: FrozenRef<'static, [FrozenStringValue]>,
218 ) -> Result<Value<'v>, EvalException> {
219 self.enter_scope(ScopeId::module());
220 let value = self.eval_top_level_stmt(&mut stmt, local_names)?;
221 self.exit_scope();
222 assert!(self.locals.is_empty());
223 Ok(value)
224 }
225}