Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
283 changes: 241 additions & 42 deletions pyrefly/lib/alt/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -867,54 +867,253 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
metadata,
},
),
) => self.callable_infer(
signature,
Some(&metadata.kind),
tparams.as_deref(),
Some(obj),
args,
keywords,
range,
errors,
errors,
context,
hint,
ctor_targs,
),
CallTarget::Callable(TargetWithTParams(tparams, callable)) => self.callable_infer(
callable,
None,
tparams.as_deref(),
None,
args,
keywords,
range,
errors,
errors,
context,
hint,
ctor_targs,
),
) => {
// If we have a hint and type parameters, try with hint first.
// If there are call errors (but NOT BadSpecialization errors which indicate
// legitimate bound violations), retry without hint.
// This prevents outer context hints from causing argument type errors.
if tparams.is_some() && hint.is_some() {
let call_errors = self.error_collector();
let res = self.callable_infer(
signature.clone(),
Some(&metadata.kind),
tparams.as_deref(),
Some(obj.clone()),
args,
keywords,
range,
errors,
&call_errors,
context,
hint,
None, // Don't pass ctor_targs in trial call
);
// Only fall back if there are errors that are NOT BadSpecialization.
// BadSpecialization errors indicate legitimate type variable bound violations
// that should be preserved, not worked around.
let should_fallback = !call_errors.is_empty()
&& !call_errors.has_error_kind(ErrorKind::BadSpecialization);
if call_errors.is_empty() {
// First try succeeded, but we need to redo with ctor_targs if present
if ctor_targs.is_some() {
self.callable_infer(
signature,
Some(&metadata.kind),
tparams.as_deref(),
Some(obj),
args,
keywords,
range,
errors,
errors,
context,
hint,
ctor_targs,
)
} else {
errors.extend(call_errors);
res
}
} else if should_fallback {
// Retry without hint - errors are hint-induced, not bound violations
self.callable_infer(
signature,
Some(&metadata.kind),
tparams.as_deref(),
Some(obj),
args,
keywords,
range,
errors,
errors,
context,
None,
ctor_targs,
)
} else {
// Keep the errors (including bound violations)
errors.extend(call_errors);
res
}
} else {
self.callable_infer(
signature,
Some(&metadata.kind),
tparams.as_deref(),
Some(obj),
args,
keywords,
range,
errors,
errors,
context,
hint,
ctor_targs,
)
}
}
CallTarget::Callable(TargetWithTParams(tparams, callable)) => {
if tparams.is_some() && hint.is_some() {
let call_errors = self.error_collector();
let res = self.callable_infer(
callable.clone(),
None,
tparams.as_deref(),
None,
args,
keywords,
range,
errors,
&call_errors,
context,
hint,
None,
);
// Only fall back if there are errors that are NOT BadSpecialization.
let should_fallback = !call_errors.is_empty()
&& !call_errors.has_error_kind(ErrorKind::BadSpecialization);
if call_errors.is_empty() {
if ctor_targs.is_some() {
self.callable_infer(
callable,
None,
tparams.as_deref(),
None,
args,
keywords,
range,
errors,
errors,
context,
hint,
ctor_targs,
)
} else {
errors.extend(call_errors);
res
}
} else if should_fallback {
// Retry without hint - errors are hint-induced, not bound violations
self.callable_infer(
callable,
None,
tparams.as_deref(),
None,
args,
keywords,
range,
errors,
errors,
context,
None,
ctor_targs,
)
} else {
// Keep the errors (including bound violations)
errors.extend(call_errors);
res
}
} else {
self.callable_infer(
callable,
None,
tparams.as_deref(),
None,
args,
keywords,
range,
errors,
errors,
context,
hint,
ctor_targs,
)
}
}
CallTarget::Function(TargetWithTParams(
tparams,
Function {
signature: callable,
metadata,
},
)) => self.callable_infer(
callable,
Some(&metadata.kind),
tparams.as_deref(),
None,
args,
keywords,
range,
errors,
errors,
context,
hint,
ctor_targs,
),
)) => {
if tparams.is_some() && hint.is_some() {
let call_errors = self.error_collector();
let res = self.callable_infer(
callable.clone(),
Some(&metadata.kind),
tparams.as_deref(),
None,
args,
keywords,
range,
errors,
&call_errors,
context,
hint,
None,
);
// Only fall back if there are errors that are NOT BadSpecialization.
let should_fallback = !call_errors.is_empty()
&& !call_errors.has_error_kind(ErrorKind::BadSpecialization);
if call_errors.is_empty() {
if ctor_targs.is_some() {
self.callable_infer(
callable,
Some(&metadata.kind),
tparams.as_deref(),
None,
args,
keywords,
range,
errors,
errors,
context,
hint,
ctor_targs,
)
} else {
errors.extend(call_errors);
res
}
} else if should_fallback {
// Retry without hint - errors are hint-induced, not bound violations
self.callable_infer(
callable,
Some(&metadata.kind),
tparams.as_deref(),
None,
args,
keywords,
range,
errors,
errors,
context,
None,
ctor_targs,
)
} else {
// Keep the errors (including bound violations)
errors.extend(call_errors);
res
}
} else {
self.callable_infer(
callable,
Some(&metadata.kind),
tparams.as_deref(),
None,
args,
keywords,
range,
errors,
errors,
context,
hint,
ctor_targs,
)
}
}
CallTarget::FunctionOverload(overloads, metadata) => {
self.call_overloads(
overloads, metadata, None, args, keywords, range, errors, context, hint,
Expand Down
5 changes: 5 additions & 0 deletions pyrefly/lib/error/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ impl ErrorCollector {
self.errors.lock().len()
}

/// Check if any error has the given error kind.
pub fn has_error_kind(&self, kind: ErrorKind) -> bool {
self.errors.lock().iter().any(|e| e.error_kind() == kind)
}

pub fn collect_into(&self, error_config: &ErrorConfig, result: &mut CollectedErrors) {
let mut errors = self.errors.lock();
if !(self.module_info.is_generated() && error_config.ignore_errors_in_generated_code) {
Expand Down
Loading
Loading