Skip to content

Commit 21084b4

Browse files
authored
Merge pull request #37981 from xedin/record-explored-types-incrementally
[ConstraintSystem] Record produced types incrementally
2 parents 9c54d79 + 8dc2251 commit 21084b4

File tree

3 files changed

+77
-5
lines changed

3 files changed

+77
-5
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5369,6 +5369,9 @@ class TypeVariableBinding {
53695369
TypeVariableBinding(TypeVariableType *typeVar, PotentialBinding &binding)
53705370
: TypeVar(typeVar), Binding(binding) {}
53715371

5372+
TypeVariableType *getTypeVariable() const { return TypeVar; }
5373+
Type getType() const { return Binding.BindingType; }
5374+
53725375
bool isDefaultable() const { return Binding.isDefaultableBinding(); }
53735376

53745377
bool hasDefaultedProtocol() const {
@@ -5453,7 +5456,21 @@ class TypeVarBindingProducer : public BindingProducer<TypeVariableBinding> {
54535456
if (needsToComputeNext() && !computeNext())
54545457
return None;
54555458

5456-
return TypeVariableBinding(TypeVar, Bindings[Index++]);
5459+
auto &binding = Bindings[Index++];
5460+
5461+
// Record produced type as bound/explored early, otherwise
5462+
// it could be possible to re-discover it during `computeNext()`,
5463+
// which leads to duplicate bindings e.g. inferring fallback
5464+
// `Void` for a closure result type when `Void` was already
5465+
// inferred as a direct/transitive binding.
5466+
{
5467+
auto type = binding.BindingType;
5468+
5469+
BoundTypes.insert(type.getPointer());
5470+
ExploredTypes.insert(type->getCanonicalType());
5471+
}
5472+
5473+
return TypeVariableBinding(TypeVar, binding);
54575474
}
54585475

54595476
bool needsToComputeNext() const override { return Index >= Bindings.size(); }

lib/Sema/CSBindings.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,10 +1705,6 @@ bool TypeVarBindingProducer::computeNext() {
17051705
const auto type = binding.BindingType;
17061706
assert(!type->hasError());
17071707

1708-
// After our first pass, note that we've explored these types.
1709-
if (NumTries == 0)
1710-
ExploredTypes.insert(type->getCanonicalType());
1711-
17121708
// If we have a protocol with a default type, look for alternative
17131709
// types to the default.
17141710
if (NumTries == 0 && binding.hasDefaultedLiteralProtocol()) {

unittests/Sema/BindingInferenceTests.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,62 @@ TEST_F(SemaTest, TestTransitiveProtocolInferenceThroughEquivalenceChains) {
336336
verifyProtocolInferenceResults(*bindings.TransitiveProtocols,
337337
{protocolTy0, protocolTy1});
338338
}
339+
340+
TEST_F(SemaTest, TestNoDoubleVoidClosureResultInference) {
341+
ConstraintSystemOptions options;
342+
ConstraintSystem cs(DC, options);
343+
344+
auto verifyInference = [&](TypeVariableType *typeVar, unsigned numExpected) {
345+
auto bindings = cs.getBindingsFor(typeVar);
346+
TypeVarBindingProducer producer(bindings);
347+
348+
llvm::SmallPtrSet<Type, 2> inferredTypes;
349+
350+
while (auto binding = producer()) {
351+
ASSERT_TRUE(binding.hasValue());
352+
ASSERT_EQ(binding->getTypeVariable(), typeVar);
353+
ASSERT_TRUE(inferredTypes.insert(binding->getType()).second);
354+
}
355+
356+
ASSERT_EQ(inferredTypes.size(), numExpected);
357+
};
358+
359+
auto *closureResultLoc =
360+
cs.getConstraintLocator({}, ConstraintLocator::ClosureResult);
361+
362+
auto *closureResult = cs.createTypeVariable(closureResultLoc, /*options=*/0);
363+
364+
cs.addConstraint(ConstraintKind::Subtype, getStdlibType("Int"), closureResult,
365+
closureResultLoc);
366+
cs.addConstraint(ConstraintKind::Subtype, closureResult, getStdlibType("Void"),
367+
closureResultLoc);
368+
369+
verifyInference(closureResult, 2);
370+
371+
auto closureResultWithTransitiveVoid = cs.createTypeVariable(closureResultLoc,
372+
/*options=*/0);
373+
374+
auto contextualVar = cs.createTypeVariable({}, /*options=*/0);
375+
376+
cs.addConstraint(ConstraintKind::Subtype, getStdlibType("Void"),
377+
contextualVar, cs.getConstraintLocator({}));
378+
379+
cs.addConstraint(ConstraintKind::Subtype, contextualVar,
380+
closureResultWithTransitiveVoid, closureResultLoc);
381+
382+
cs.addConstraint(ConstraintKind::Subtype, getStdlibType("Int"),
383+
closureResultWithTransitiveVoid, closureResultLoc);
384+
385+
verifyInference(closureResultWithTransitiveVoid, 2);
386+
387+
auto closureResultWithoutVoid =
388+
cs.createTypeVariable(closureResultLoc, /*options=*/0);
389+
390+
// Supertype triggers `Void` inference
391+
cs.addConstraint(ConstraintKind::Subtype, getStdlibType("Int"),
392+
closureResultWithoutVoid, closureResultLoc);
393+
cs.addConstraint(ConstraintKind::Subtype, closureResultWithoutVoid,
394+
getStdlibType("String"), closureResultLoc);
395+
396+
verifyInference(closureResultWithoutVoid, 3);
397+
}

0 commit comments

Comments
 (0)