Skip to content

Commit 7b5b752

Browse files
authored
Merge pull request #1391 from harehare/fix/mq-check-overload
🐛 fix(mq-check): correct deferred overload replacement and add regression test
2 parents 6307a54 + dc5c59d commit 7b5b752

File tree

2 files changed

+16
-6
lines changed

2 files changed

+16
-6
lines changed

crates/mq-check/src/builtin.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,6 +1545,7 @@ mod tests {
15451545
#[case::piped_index("\"hello\" | index(\"ll\")", true)]
15461546
#[case::piped_replace("\"hello\" | replace(\"l\", \"r\")", true)]
15471547
#[case::piped_gsub("\"hello\" | gsub(\"l\", \"r\")", true)]
1548+
#[case::piped_gsub_variable_arg("def slugify(s, separator = \"-\"): s | gsub(\"[^a-z0-9]+\", separator) end", true)] // regression: default param used in gsub should not produce false error
15481549
#[case::piped_slice("[1, 2, 3, 4] | slice(1, 3)", true)]
15491550
#[case::piped_repeat("\"x\" | repeat(3)", true)]
15501551
#[case::piped_join_wrong_type("42 | join(\",\")", false)] // number piped to join (expects array)

crates/mq-check/src/infer.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,9 @@ pub struct InferenceContext {
163163
errors: Vec<TypeError>,
164164
/// Piped input types for symbols in a pipe chain
165165
piped_inputs: FxHashMap<SymbolId, Type>,
166-
/// Deferred overload resolutions for operators with unresolved type variable operands
167-
deferred_overloads: Vec<DeferredOverload>,
166+
/// Deferred overload resolutions for operators with unresolved type variable operands,
167+
/// keyed by `SymbolId` so that insert/replace is O(1).
168+
deferred_overloads: FxHashMap<SymbolId, DeferredOverload>,
168169
/// Deferred user-defined function calls for post-unification type checking
169170
deferred_user_calls: Vec<DeferredUserCall>,
170171
/// Deferred inner calls to function parameters for higher-order function checking
@@ -199,7 +200,7 @@ impl InferenceContext {
199200
builtins: FxHashMap::default(),
200201
errors: Vec::new(),
201202
piped_inputs: FxHashMap::default(),
202-
deferred_overloads: Vec::new(),
203+
deferred_overloads: FxHashMap::default(),
203204
deferred_user_calls: Vec::new(),
204205
deferred_parameter_calls: Vec::new(),
205206
deferred_record_accesses: Vec::new(),
@@ -251,14 +252,22 @@ impl InferenceContext {
251252
self.piped_inputs.get(&symbol)
252253
}
253254

254-
/// Adds a deferred overload resolution
255+
/// Adds a deferred overload resolution.
256+
///
257+
/// For a given `symbol_id` there should be at most one deferred overload entry.
258+
/// If an entry for the same symbol already exists, it is replaced with the new
259+
/// one. This keeps the deferred operand types in sync when a node is re-processed
260+
/// after its operand types or argument list have changed (for example, due to
261+
/// additional inference or piped input being attached).
262+
///
263+
/// The map-backed storage makes both insert and replace O(1).
255264
pub fn add_deferred_overload(&mut self, deferred: DeferredOverload) {
256-
self.deferred_overloads.push(deferred);
265+
self.deferred_overloads.insert(deferred.symbol_id, deferred);
257266
}
258267

259268
/// Takes all deferred overloads (consumes them)
260269
pub fn take_deferred_overloads(&mut self) -> Vec<DeferredOverload> {
261-
std::mem::take(&mut self.deferred_overloads)
270+
self.deferred_overloads.drain().map(|(_, v)| v).collect()
262271
}
263272

264273
/// Adds a deferred user-defined function call

0 commit comments

Comments
 (0)