Skip to content

Commit 4cc41e5

Browse files
committed
[Constraint solver] Perform the "occurs" check properly.
We've been performing the "occurs" check when computing potential bindings for type variables, but we weren't actually performing the check for bindings that *must* occur. Perform the occurs check before binding type variables, which fixes a few crashers and is far more principled. Note that this obviates the need for tracking the type variables we've substituted in simplifyType(), so simplify that as well. Fixes rdar://problem/27879334 / SR-2351.
1 parent c2279d2 commit 4cc41e5

File tree

6 files changed

+41
-34
lines changed

6 files changed

+41
-34
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,17 @@ static bool allowsBridgingFromObjC(TypeChecker &tc, DeclContext *dc,
12081208
return true;
12091209
}
12101210

1211+
/// Determine whether the given type variables occurs in the given type.
1212+
static bool typeVarOccursInType(TypeVariableType *typeVar, Type type) {
1213+
SmallVector<TypeVariableType *, 4> typeVars;
1214+
type->getTypeVariables(typeVars);
1215+
for (auto referencedTypeVar : typeVars) {
1216+
if (referencedTypeVar == typeVar) return true;
1217+
}
1218+
1219+
return false;
1220+
}
1221+
12111222
ConstraintSystem::SolutionKind
12121223
ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
12131224
TypeMatchOptions flags,
@@ -1287,6 +1298,11 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
12871298
// Provide a fixed type for the type variable.
12881299
bool wantRvalue = kind == ConstraintKind::Equal;
12891300
if (typeVar1) {
1301+
// Simplify the right-hand type and perform the "occurs" check.
1302+
type2 = simplifyType(type2);
1303+
if (typeVarOccursInType(typeVar1, type2))
1304+
return formUnsolvedResult();
1305+
12901306
// If we want an rvalue, get the rvalue.
12911307
if (wantRvalue)
12921308
type2 = type2->getRValueType();
@@ -1310,7 +1326,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
13101326

13111327
// A constraint that binds any pointer to a void pointer is
13121328
// ineffective, since any pointer can be converted to a void pointer.
1313-
if (kind == ConstraintKind::BindToPointerType && desugar2->isVoid()) {
1329+
if (kind == ConstraintKind::BindToPointerType && type2->isVoid()) {
13141330
// Bind type1 to Void only as a last resort.
13151331
addConstraint(ConstraintKind::Defaultable, typeVar1, type2,
13161332
getConstraintLocator(locator));
@@ -1327,6 +1343,11 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
13271343
return SolutionKind::Solved;
13281344
}
13291345

1346+
// Simplify the left-hand type and perform the "occurs" check.
1347+
type1 = simplifyType(type1);
1348+
if (typeVarOccursInType(typeVar2, type1))
1349+
return formUnsolvedResult();
1350+
13301351
// If we want an rvalue, get the rvalue.
13311352
if (wantRvalue)
13321353
type1 = type1->getRValueType();
@@ -1344,14 +1365,24 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
13441365

13451366
case ConstraintKind::BindParam: {
13461367
if (typeVar2 && !typeVar1) {
1347-
if (auto *iot = dyn_cast<InOutType>(desugar1)) {
1368+
// Simplify the left-hand type and perform the "occurs" check.
1369+
type1 = simplifyType(type1);
1370+
if (typeVarOccursInType(typeVar2, type1))
1371+
return formUnsolvedResult();
1372+
1373+
if (auto *iot = type1->getAs<InOutType>()) {
13481374
assignFixedType(typeVar2, LValueType::get(iot->getObjectType()));
13491375
} else {
13501376
assignFixedType(typeVar2, type1);
13511377
}
13521378
return SolutionKind::Solved;
13531379
} else if (typeVar1 && !typeVar2) {
1354-
if (auto *lvt = dyn_cast<LValueType>(desugar2)) {
1380+
// Simplify the right-hand type and perform the "occurs" check.
1381+
type2 = simplifyType(type2);
1382+
if (typeVarOccursInType(typeVar1, type2))
1383+
return formUnsolvedResult();
1384+
1385+
if (auto *lvt = type2->getAs<LValueType>()) {
13551386
assignFixedType(typeVar1, InOutType::get(lvt->getObjectType()));
13561387
} else {
13571388
assignFixedType(typeVar1, type2);

lib/Sema/ConstraintSystem.cpp

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,17 +1578,12 @@ Type ConstraintSystem::lookThroughImplicitlyUnwrappedOptionalType(Type type) {
15781578
return Type();
15791579
}
15801580

1581-
Type ConstraintSystem::simplifyType(Type type,
1582-
llvm::SmallPtrSet<TypeVariableType *, 16> &substituting) {
1581+
Type ConstraintSystem::simplifyType(Type type) {
15831582
return type.transform([&](Type type) -> Type {
15841583
if (auto tvt = dyn_cast<TypeVariableType>(type.getPointer())) {
15851584
tvt = getRepresentative(tvt);
15861585
if (auto fixed = getFixedType(tvt)) {
1587-
if (substituting.insert(tvt).second) {
1588-
auto result = simplifyType(fixed, substituting);
1589-
substituting.erase(tvt);
1590-
return result;
1591-
}
1586+
return simplifyType(fixed);
15921587
}
15931588

15941589
return tvt;

lib/Sema/ConstraintSystem.h

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,10 +1894,7 @@ class ConstraintSystem {
18941894
///
18951895
/// The resulting types can be compared canonically, so long as additional
18961896
/// type equivalence requirements aren't introduced between comparisons.
1897-
Type simplifyType(Type type){
1898-
llvm::SmallPtrSet<TypeVariableType *, 16> substituting;
1899-
return simplifyType(type, substituting);
1900-
}
1897+
Type simplifyType(Type type);
19011898

19021899
/// Given a ValueMember, UnresolvedValueMember, or TypeMember constraint,
19031900
/// perform a lookup into the specified base type to find a candidate list.
@@ -1913,22 +1910,7 @@ class ConstraintSystem {
19131910
ConstraintLocator *memberLocator,
19141911
bool includeInaccessibleMembers);
19151912

1916-
private:
1917-
1918-
/// \brief Simplify a type, by replacing type variables with either their
1919-
/// fixed types (if available) or their representatives.
1920-
///
1921-
/// \param type the type to be simplified.
1922-
///
1923-
/// \param substituting the set of type variables that we're already
1924-
/// substituting for. These type variables will not be substituted again,
1925-
/// to avoid infinite recursion.
1926-
///
1927-
/// The resulting types can be compared canonically, so long as additional
1928-
/// type equivalence requirements aren't introduced between comparisons.
1929-
Type simplifyType(Type type,
1930-
llvm::SmallPtrSet<TypeVariableType *, 16> &substituting);
1931-
1913+
private:
19321914
/// \brief Attempt to simplify the given construction constraint.
19331915
///
19341916
/// \param valueType The type being constructed.

validation-test/Sema/type_checker_crashers/rdar27879334.swift renamed to validation-test/Sema/type_checker_crashers_fixed/rdar27879334.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: not --crash %target-swift-frontend %s -parse
1+
// RUN: not %target-swift-frontend %s -parse
22

33
class N {}
44

validation-test/compiler_crashers/28362-swift-constraints-constraintgraphnode-getadjacency.swift renamed to validation-test/compiler_crashers_fixed/28362-swift-constraints-constraintgraphnode-getadjacency.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
// See http://swift.org/LICENSE.txt for license information
66
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
77

8-
// RUN: not --crash %target-swift-frontend %s -parse
8+
// RUN: not %target-swift-frontend %s -parse
99
// REQUIRES: asserts
1010
[1,{[1,{[]

validation-test/compiler_crashers/28400-swift-nominaltypedecl-prepareextensions.swift renamed to validation-test/compiler_crashers_fixed/28400-swift-nominaltypedecl-prepareextensions.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
// See http://swift.org/LICENSE.txt for license information
66
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
77

8-
// RUN: not --crash %target-swift-frontend %s -parse
9-
// REQUIRES: asserts
8+
// RUN: %target-swift-frontend %s -parse
109
// Discovered by https://github.com/airspeedswift (airspeedswift)
1110

1211
let s1: Set<Int>? = []

0 commit comments

Comments
 (0)