|
23 | 23 | #include "absl/log/absl_check.h" |
24 | 24 | #include "absl/log/absl_log.h" |
25 | 25 | #include "absl/strings/match.h" |
| 26 | +#include "absl/strings/str_cat.h" |
| 27 | +#include "absl/strings/str_join.h" |
26 | 28 | #include "absl/strings/string_view.h" |
27 | 29 | #include "absl/types/optional.h" |
28 | 30 | #include "absl/types/span.h" |
| 31 | +#include "checker/internal/format_type_name.h" |
29 | 32 | #include "common/decl.h" |
30 | 33 | #include "common/type.h" |
31 | 34 | #include "common/type_kind.h" |
@@ -267,14 +270,15 @@ bool TypeInferenceContext::IsAssignableInternal( |
267 | 270 | // Checking assignability to a specific type var |
268 | 271 | // that has a prospective type assignment. |
269 | 272 | to.kind() == TypeKind::kTypeParam && |
270 | | - prospective_substitutions.contains(to.AsTypeParam()->name())) { |
271 | | - auto prospective_subs_cpy(prospective_substitutions); |
| 273 | + prospective_substitutions.contains(to.GetTypeParam().name())) { |
| 274 | + SubstitutionMap prospective_subs_cpy = prospective_substitutions; |
272 | 275 | if (CompareGenerality(from_subs, to_subs, prospective_subs_cpy) == |
273 | 276 | RelativeGenerality::kMoreGeneral) { |
274 | 277 | if (IsAssignableInternal(to_subs, from_subs, prospective_subs_cpy) && |
275 | | - !OccursWithin(to.name(), from_subs, prospective_subs_cpy)) { |
276 | | - prospective_subs_cpy[to.AsTypeParam()->name()] = from_subs; |
277 | | - prospective_substitutions = prospective_subs_cpy; |
| 278 | + !OccursWithin(to.GetTypeParam().name(), from_subs, |
| 279 | + prospective_subs_cpy)) { |
| 280 | + prospective_subs_cpy[to.GetTypeParam().name()] = from_subs; |
| 281 | + prospective_substitutions = std::move(prospective_subs_cpy); |
278 | 282 | return true; |
279 | 283 | // otherwise, continue with normal assignability check. |
280 | 284 | } |
@@ -454,17 +458,35 @@ bool TypeInferenceContext::OccursWithin( |
454 | 458 | // |
455 | 459 | // This check guarantees that we don't introduce a recursive type definition |
456 | 460 | // (a cycle in the substitution map). |
457 | | - if (type.kind() == TypeKind::kTypeParam) { |
458 | | - if (type.AsTypeParam()->name() == var_name) { |
| 461 | + // |
| 462 | + // We can't reuse Substitute here because it does the pointer chasing and |
| 463 | + // might hide a cycle. |
| 464 | + // |
| 465 | + // E.g. |
| 466 | + // T2 in T3 when |
| 467 | + // T3 -> T2 -> null_type; |
| 468 | + Type substitution = type; |
| 469 | + while (substitution.kind() == TypeKind::kTypeParam) { |
| 470 | + absl::string_view param_name = substitution.AsTypeParam()->name(); |
| 471 | + if (param_name == var_name) { |
459 | 472 | return true; |
460 | 473 | } |
461 | | - auto typeSubs = Substitute(type, substitutions); |
462 | | - if (typeSubs != type && OccursWithin(var_name, typeSubs, substitutions)) { |
463 | | - return true; |
| 474 | + |
| 475 | + if (auto it = substitutions.find(param_name); it != substitutions.end()) { |
| 476 | + substitution = it->second; |
| 477 | + continue; |
| 478 | + } |
| 479 | + if (auto it = type_parameter_bindings_.find(param_name); |
| 480 | + it != type_parameter_bindings_.end() && it->second.type.has_value()) { |
| 481 | + substitution = it->second.type.value(); |
| 482 | + continue; |
464 | 483 | } |
| 484 | + |
| 485 | + // Type parameter is free. |
| 486 | + return false; |
465 | 487 | } |
466 | 488 |
|
467 | | - for (const auto& param : type.GetParameters()) { |
| 489 | + for (const auto& param : substitution.GetParameters()) { |
468 | 490 | if (OccursWithin(var_name, param, substitutions)) { |
469 | 491 | return true; |
470 | 492 | } |
@@ -526,19 +548,18 @@ TypeInferenceContext::ResolveOverload(const FunctionDecl& decl, |
526 | 548 | ABSL_DCHECK_EQ(argument_types.size(), |
527 | 549 | call_type_instance.param_types.size()); |
528 | 550 | bool is_match = true; |
529 | | - SubstitutionMap prospective_substitutions; |
| 551 | + AssignabilityContext assignability_context = CreateAssignabilityContext(); |
530 | 552 | for (int i = 0; i < argument_types.size(); ++i) { |
531 | | - if (!IsAssignableInternal(argument_types[i], |
532 | | - call_type_instance.param_types[i], |
533 | | - prospective_substitutions)) { |
| 553 | + if (!assignability_context.IsAssignable( |
| 554 | + argument_types[i], call_type_instance.param_types[i])) { |
534 | 555 | is_match = false; |
535 | 556 | break; |
536 | 557 | } |
537 | 558 | } |
538 | 559 |
|
539 | 560 | if (is_match) { |
540 | 561 | matching_overloads.push_back(ovl); |
541 | | - UpdateTypeParameterBindings(prospective_substitutions); |
| 562 | + assignability_context.UpdateInferredTypeAssignments(); |
542 | 563 | if (!result_type.has_value()) { |
543 | 564 | result_type = call_type_instance.result_type; |
544 | 565 | } else { |
@@ -625,10 +646,23 @@ bool TypeInferenceContext::AssignabilityContext::IsAssignable(const Type& from, |
625 | 646 | prospective_substitutions_); |
626 | 647 | } |
627 | 648 |
|
| 649 | +std::string TypeInferenceContext::DebugString() const { |
| 650 | + return absl::StrCat( |
| 651 | + "type_parameter_bindings: ", |
| 652 | + absl::StrJoin( |
| 653 | + type_parameter_bindings_, "\n ", |
| 654 | + [](std::string* out, const auto& binding) { |
| 655 | + absl::StrAppend( |
| 656 | + out, binding.first, " (", binding.second.name, ") -> ", |
| 657 | + checker_internal::FormatTypeName( |
| 658 | + binding.second.type.value_or(Type(TypeParamType("none"))))); |
| 659 | + })); |
| 660 | +} |
| 661 | + |
628 | 662 | void TypeInferenceContext::AssignabilityContext:: |
629 | 663 | UpdateInferredTypeAssignments() { |
630 | | - inference_context_.UpdateTypeParameterBindings( |
631 | | - std::move(prospective_substitutions_)); |
| 664 | + inference_context_.UpdateTypeParameterBindings(prospective_substitutions_); |
| 665 | + prospective_substitutions_.clear(); |
632 | 666 | } |
633 | 667 |
|
634 | 668 | void TypeInferenceContext::AssignabilityContext::Reset() { |
|
0 commit comments