Skip to content

Commit 9a0a831

Browse files
authored
Merge pull request #82147 from hamishknight/fix-nested-arenas
[CS] Avoid solver-allocated inputs with `typesSatisfyConstraint`
2 parents c1f6388 + db49faf commit 9a0a831

17 files changed

+97
-83
lines changed

include/swift/AST/Types.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,12 @@ class alignas(1 << TypeAlignInBits) TypeBase
720720
return getRecursiveProperties().hasTypeVariable();
721721
}
722722

723+
// Convenience for checking whether the given type either has a type
724+
// variable or placeholder.
725+
bool hasTypeVariableOrPlaceholder() const {
726+
return hasTypeVariable() || hasPlaceholder();
727+
}
728+
723729
/// Determine where this type is a type variable or a dependent
724730
/// member root in a type variable.
725731
bool isTypeVariableOrMember();

include/swift/Sema/ConstraintSystem.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,7 @@ enum class FreeTypeVariableBinding {
102102
/// Disallow any binding of such free type variables.
103103
Disallow,
104104
/// Allow the free type variables to persist in the solution.
105-
Allow,
106-
/// Bind the type variables to UnresolvedType to represent the ambiguity.
107-
UnresolvedType
105+
Allow
108106
};
109107

110108
/// Describes whether or not a result builder method is supported.

lib/AST/ASTContext.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3577,7 +3577,13 @@ void LocatableType::Profile(llvm::FoldingSetNodeID &id, SourceLoc loc,
35773577
Type ErrorType::get(const ASTContext &C) { return C.TheErrorType; }
35783578

35793579
Type ErrorType::get(Type originalType) {
3580-
assert(originalType);
3580+
ASSERT(originalType);
3581+
// We don't currently support solver-allocated error types, if we want
3582+
// this in the future we'll need to adjust `Solution::simplifyType` to fold
3583+
// them into regular ErrorTypes. Additionally, any clients of
3584+
// `typesSatisfyConstraint` will need to be taught not to pass such types.
3585+
ASSERT(!originalType->getRecursiveProperties().isSolverAllocated() &&
3586+
"Solver-allocated error types not supported");
35813587

35823588
auto originalProperties = originalType->getRecursiveProperties();
35833589
auto arena = getArena(originalProperties);
@@ -3588,14 +3594,8 @@ Type ErrorType::get(Type originalType) {
35883594

35893595
void *mem = ctx.Allocate(sizeof(ErrorType) + sizeof(Type),
35903596
alignof(ErrorType), arena);
3591-
RecursiveTypeProperties properties = RecursiveTypeProperties::HasError;
3592-
3593-
// We need to preserve the solver allocated bit, to ensure any wrapping
3594-
// types are solver allocated too.
3595-
if (originalProperties.isSolverAllocated())
3596-
properties |= RecursiveTypeProperties::SolverAllocated;
3597-
3598-
return entry = new (mem) ErrorType(ctx, originalType, properties);
3597+
return entry = new (mem) ErrorType(ctx, originalType,
3598+
RecursiveTypeProperties::HasError);
35993599
}
36003600

36013601
void ErrorUnionType::Profile(llvm::FoldingSetNodeID &id, ArrayRef<Type> terms) {

lib/AST/TypeSubstitution.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,13 @@ Type TypeSubstituter::transformDependentMemberType(DependentMemberType *dependen
446446

447447
auto result = conformance.getTypeWitness(assocType, IFS.getOptions());
448448
if (result->is<ErrorType>()) {
449+
// Substitute the base type for the original ErrorType for type printing.
450+
// Avoid doing this if the substitutions introduce type variables or
451+
// placeholders since ErrorTypes can't be solver-allocated currently (and
452+
// type variables aren't helpful when printing anyway).
449453
auto substBase = origBase.subst(IFS);
450-
return DependentMemberType::get(ErrorType::get(substBase), assocType);
454+
if (!substBase->hasTypeVariableOrPlaceholder())
455+
return DependentMemberType::get(ErrorType::get(substBase), assocType);
451456
}
452457
return result;
453458
}
@@ -1242,4 +1247,4 @@ ProtocolConformanceRef ReplaceExistentialArchetypesWithConcreteTypes::operator()
12421247

12431248
return subs.lookupConformance(
12441249
getInterfaceType(existentialArchetype)->getCanonicalType(), proto);
1245-
}
1250+
}

lib/Sema/CSFix.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,12 @@ CoerceToCheckedCast *CoerceToCheckedCast::attempt(ConstraintSystem &cs,
185185
Type fromType, Type toType,
186186
bool useConditionalCast,
187187
ConstraintLocator *locator) {
188-
// If any of the types has a type variable, don't add the fix.
189-
if (fromType->hasTypeVariable() || toType->hasTypeVariable())
188+
// If any of the types have type variables or placeholders, don't add the fix.
189+
// `typeCheckCheckedCast` doesn't support checking such types.
190+
if (fromType->hasTypeVariableOrPlaceholder() ||
191+
toType->hasTypeVariableOrPlaceholder()) {
190192
return nullptr;
193+
}
191194

192195
auto anchor = locator->getAnchor();
193196
if (auto *assignExpr = getAsExpr<AssignExpr>(anchor))

lib/Sema/CSGen.cpp

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4872,29 +4872,16 @@ bool ConstraintSystem::generateConstraints(
48724872
return convertTypeLocator;
48734873
};
48744874

4875-
// Substitute type variables in for placeholder types (and unresolved
4876-
// types, if allowed).
4877-
if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) {
4878-
convertType = convertType.transformRec([&](Type type) -> std::optional<Type> {
4879-
if (type->is<UnresolvedType>() || type->is<PlaceholderType>()) {
4880-
return Type(createTypeVariable(getLocator(type),
4881-
TVO_CanBindToNoEscape |
4882-
TVO_PrefersSubtypeBinding |
4883-
TVO_CanBindToHole));
4884-
}
4885-
return std::nullopt;
4886-
});
4887-
} else {
4888-
convertType = convertType.transformRec([&](Type type) -> std::optional<Type> {
4889-
if (type->is<PlaceholderType>()) {
4890-
return Type(createTypeVariable(getLocator(type),
4891-
TVO_CanBindToNoEscape |
4892-
TVO_PrefersSubtypeBinding |
4893-
TVO_CanBindToHole));
4894-
}
4895-
return std::nullopt;
4896-
});
4897-
}
4875+
// Substitute type variables in for placeholder types.
4876+
convertType = convertType.transformRec([&](Type type) -> std::optional<Type> {
4877+
if (type->is<PlaceholderType>()) {
4878+
return Type(createTypeVariable(getLocator(type),
4879+
TVO_CanBindToNoEscape |
4880+
TVO_PrefersSubtypeBinding |
4881+
TVO_CanBindToHole));
4882+
}
4883+
return std::nullopt;
4884+
});
48984885

48994886
addContextualConversionConstraint(expr, convertType, ctp,
49004887
convertTypeLocator);

lib/Sema/CSRanking.cpp

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,26 +1413,10 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
14131413
auto type1 = types.Type1;
14141414
auto type2 = types.Type2;
14151415

1416-
// If either of the types still contains type variables, we can't
1417-
// compare them.
1418-
// FIXME: This is really unfortunate. More type variable sharing
1419-
// (when it's sound) would help us do much better here.
1420-
if (type1->hasTypeVariable() || type2->hasTypeVariable()) {
1421-
identical = false;
1422-
continue;
1423-
}
1424-
1425-
// With introduction of holes it's currently possible to form solutions
1426-
// with UnresolvedType bindings, we need to account for that in
1427-
// ranking. If one solution has a hole for a given type variable
1428-
// it's always worse than any non-hole type other solution might have.
1429-
if (type1->is<UnresolvedType>() || type2->is<UnresolvedType>()) {
1430-
if (type1->is<UnresolvedType>()) {
1431-
++score2;
1432-
} else {
1433-
++score1;
1434-
}
1435-
1416+
// If either of the types have holes or unresolved type variables, we can't
1417+
// compare them. `isSubtypeOf` cannot be used with solver-allocated types.
1418+
if (type1->hasTypeVariableOrPlaceholder() ||
1419+
type2->hasTypeVariableOrPlaceholder()) {
14361420
identical = false;
14371421
continue;
14381422
}
@@ -1469,15 +1453,12 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
14691453
// The systems are not considered equivalent.
14701454
identical = false;
14711455

1472-
// Archetypes are worse than concrete types (i.e. non-placeholder and
1473-
// non-archetype)
1456+
// Archetypes are worse than concrete types
14741457
// FIXME: Total hack.
1475-
if (type1->is<ArchetypeType>() && !type2->is<ArchetypeType>() &&
1476-
!type2->is<PlaceholderType>()) {
1458+
if (type1->is<ArchetypeType>() && !type2->is<ArchetypeType>()) {
14771459
++score2;
14781460
continue;
1479-
} else if (type2->is<ArchetypeType>() && !type1->is<ArchetypeType>() &&
1480-
!type1->is<PlaceholderType>()) {
1461+
} else if (type2->is<ArchetypeType>() && !type1->is<ArchetypeType>()) {
14811462
++score1;
14821463
continue;
14831464
}

lib/Sema/CSSimplify.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4909,8 +4909,12 @@ static bool
49094909
repairViaBridgingCast(ConstraintSystem &cs, Type fromType, Type toType,
49104910
SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
49114911
ConstraintLocatorBuilder locator) {
4912-
if (fromType->hasTypeVariable() || toType->hasTypeVariable())
4912+
// Don't check if any of the types have type variables or placeholders,
4913+
// `typeCheckCheckedCast` doesn't support checking solver-allocated types.
4914+
if (fromType->hasTypeVariableOrPlaceholder() ||
4915+
toType->hasTypeVariableOrPlaceholder()) {
49134916
return false;
4917+
}
49144918

49154919
auto objectType1 = fromType->getOptionalObjectType();
49164920
auto objectType2 = toType->getOptionalObjectType();
@@ -9375,10 +9379,12 @@ static ConstraintFix *maybeWarnAboutExtraneousCast(
93759379
if (locator.endsWith<LocatorPathElt::GenericArgument>())
93769380
return nullptr;
93779381

9378-
// Both types have to be fixed.
9379-
if (fromType->hasTypeVariable() || toType->hasTypeVariable() ||
9380-
fromType->hasPlaceholder() || toType->hasPlaceholder())
9382+
// Both types have to be resolved, `typeCheckCheckedCast` doesn't support
9383+
// checking solver-allocated types.
9384+
if (fromType->hasTypeVariableOrPlaceholder() ||
9385+
toType->hasTypeVariableOrPlaceholder()) {
93819386
return nullptr;
9387+
}
93829388

93839389
SmallVector<LocatorPathElt, 4> path;
93849390
auto anchor = locator.getLocatorParts(path);

lib/Sema/CSSolver.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,6 @@ Solution ConstraintSystem::finalize() {
9191

9292
case FreeTypeVariableBinding::Allow:
9393
break;
94-
95-
case FreeTypeVariableBinding::UnresolvedType:
96-
assignFixedType(tv, ctx.TheUnresolvedType);
97-
break;
9894
}
9995
}
10096

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,8 +1080,14 @@ bool TypeChecker::typesSatisfyConstraint(Type type1, Type type2,
10801080
bool openArchetypes,
10811081
ConstraintKind kind, DeclContext *dc,
10821082
bool *unwrappedIUO) {
1083-
assert(!type1->hasTypeVariable() && !type2->hasTypeVariable() &&
1084-
"Unexpected type variable in constraint satisfaction testing");
1083+
// Don't allow any type variables to leak into the nested ConstraintSystem
1084+
// (including as originator types for placeholders). This also ensure that we
1085+
// avoid lifetime issues for e.g cases where we lazily populate the
1086+
// `ContextSubMap` on `NominalOrBoundGenericNominalType` in the nested arena,
1087+
// since it will be destroyed on leaving.
1088+
ASSERT(!type1->getRecursiveProperties().isSolverAllocated() &&
1089+
!type2->getRecursiveProperties().isSolverAllocated() &&
1090+
"Cannot escape solver-allocated types into a nested ConstraintSystem");
10851091

10861092
ConstraintSystem cs(dc, ConstraintSystemOptions());
10871093
if (openArchetypes) {

lib/Sema/TypeOfReference.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,8 +1134,7 @@ static void bindArchetypesFromContext(
11341134
if (parentDC->isTypeContext()) {
11351135
if (parentDC != outerDC && parentDC->getSelfProtocolDecl()) {
11361136
auto selfTy = parentDC->getSelfInterfaceType();
1137-
auto contextTy = cs.getASTContext().TheUnresolvedType;
1138-
bindPrimaryArchetype(selfTy, contextTy);
1137+
bindPrimaryArchetype(selfTy, ErrorType::get(cs.getASTContext()));
11391138
}
11401139
continue;
11411140
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %target-swift-ide-test -code-completion -code-completion-token=COMPLETE -source-filename=%s
2+
3+
// https://github.com/swiftlang/swift/pull/82147
4+
5+
protocol P {
6+
associatedtype X
7+
}
8+
9+
struct S<T> {
10+
init<U, V>() where T == (U, V) {}
11+
}
12+
extension S : P where T : P {
13+
typealias X = T.X
14+
}
15+
16+
func foo<T: P, U>(_: () -> T) where U == T.X {}
17+
18+
foo {
19+
S(#^COMPLETE^#)
20+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// {"signature":"bool llvm::function_ref<bool (swift::ProtocolConformanceRef)>::callback_fn<(anonymous namespace)::MismatchedIsolatedConformances>(long, swift::ProtocolConformanceRef)"}
2+
// RUN: not %target-swift-frontend -typecheck %s
3+
func a {
4+
{ print("\(\ " ")\n"
5+
}
6+
"\()\n"
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
// {"signature":"swift::ProtocolConformanceRef::forEachMissingConformance(llvm::function_ref<bool (swift::BuiltinProtocolConformance*)>) const"}
2-
// RUN: not --crash %target-swift-frontend -typecheck %s
3-
// REQUIRES: rdar152763265
2+
// RUN: not %target-swift-frontend -typecheck %s
43
.a == .! == b / c
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
// {"signature":"swift::GenericParamKey::findIndexIn(llvm::ArrayRef<swift::GenericTypeParamType*>) const"}
2-
// RUN: not --crash %target-swift-frontend -typecheck %s
3-
// REQUIRES: rdar152763265
2+
// RUN: not %target-swift-frontend -typecheck %s
43
a[[[[ a a?[[a a?
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// {"signature":"swift::ProtocolConformanceRef::forAbstract(swift::Type, swift::ProtocolDecl*)"}
2-
// RUN: not --crash %target-swift-frontend -typecheck %s
3-
// REQUIRES: rdar152763265
2+
// RUN: not %target-swift-frontend -typecheck %s
43
@resultBuilder struct a {
54
static buildBlock<b, c, d, e>(b, c, d, e) func f<h>(_ : Bool @a Bool->h) { f(true {
65
cond in var g : Int g 2 30\ g
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// {"signature":"(anonymous namespace)::ApplyClassifier::classifyApply(swift::ApplyExpr*, llvm::DenseSet<swift::Expr const*, llvm::DenseMapInfo<swift::Expr const*, void>>*)"}
2+
// RUN: not %target-swift-frontend -typecheck %s
3+
let a = switch \0 {
4+
case 0.0

0 commit comments

Comments
 (0)