Skip to content

Commit 9e81823

Browse files
authored
Merge pull request #33296 from hborla/merge-joined-literal-typevars
[ConstraintSystem] Add a type variable merging heuristic to addJoinConstraint
2 parents a422bfb + 19fce32 commit 9e81823

File tree

8 files changed

+136
-63
lines changed

8 files changed

+136
-63
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4954,6 +4954,9 @@ bool CollectionElementContextualFailure::diagnoseAsError() {
49544954

49554955
Optional<InFlightDiagnostic> diagnostic;
49564956
if (isExpr<ArrayExpr>(anchor)) {
4957+
if (diagnoseMergedLiteralElements())
4958+
return true;
4959+
49574960
diagnostic.emplace(emitDiagnostic(diag::cannot_convert_array_element,
49584961
eltType, contextualType));
49594962
}
@@ -5004,6 +5007,30 @@ bool CollectionElementContextualFailure::diagnoseAsError() {
50045007
return true;
50055008
}
50065009

5010+
bool CollectionElementContextualFailure::diagnoseMergedLiteralElements() {
5011+
auto elementAnchor = simplifyLocatorToAnchor(getLocator());
5012+
if (!elementAnchor)
5013+
return false;
5014+
5015+
auto *typeVar = getRawType(elementAnchor)->getAs<TypeVariableType>();
5016+
if (!typeVar || !typeVar->getImpl().getAtomicLiteralKind())
5017+
return false;
5018+
5019+
// This element is a literal whose type variable could have been merged with others,
5020+
// but the conversion constraint to the array element type was only placed on one
5021+
// of them. So, we want to emit the error for each element whose type variable is in
5022+
// this equivalence class.
5023+
auto &cs = getConstraintSystem();
5024+
auto node = cs.getRepresentative(typeVar)->getImpl().getGraphNode();
5025+
for (const auto *typeVar : node->getEquivalenceClass()) {
5026+
auto anchor = typeVar->getImpl().getLocator()->getAnchor();
5027+
emitDiagnosticAt(constraints::getLoc(anchor), diag::cannot_convert_array_element,
5028+
getFromType(), getToType());
5029+
}
5030+
5031+
return true;
5032+
}
5033+
50075034
bool MissingContextualConformanceFailure::diagnoseAsError() {
50085035
auto anchor = getAnchor();
50095036
auto path = getLocator()->getPath();

lib/Sema/CSDiagnostics.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1637,6 +1637,8 @@ class CollectionElementContextualFailure final : public ContextualFailure {
16371637
: ContextualFailure(solution, eltType, contextualType, locator) {}
16381638

16391639
bool diagnoseAsError() override;
1640+
1641+
bool diagnoseMergedLiteralElements();
16401642
};
16411643

16421644
class MissingContextualConformanceFailure final : public ContextualFailure {

lib/Sema/CSGen.cpp

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,31 +1803,28 @@ namespace {
18031803

18041804
auto locator = CS.getConstraintLocator(expr);
18051805
auto contextualType = CS.getContextualType(expr);
1806-
Type contextualArrayType = nullptr;
1807-
Type contextualArrayElementType = nullptr;
1808-
1806+
1807+
auto joinElementTypes = [&](Optional<Type> elementType) {
1808+
const auto elements = expr->getElements();
1809+
unsigned index = 0;
1810+
1811+
using Iterator = decltype(elements)::iterator;
1812+
CS.addJoinConstraint<Iterator>(locator, elements.begin(), elements.end(),
1813+
elementType, [&](const auto it) {
1814+
auto *locator = CS.getConstraintLocator(expr, LocatorPathElt::TupleElement(index++));
1815+
return std::make_pair(CS.getType(*it), locator);
1816+
});
1817+
};
1818+
18091819
// If a contextual type exists for this expression, apply it directly.
18101820
Optional<Type> arrayElementType;
18111821
if (contextualType &&
18121822
(arrayElementType = ConstraintSystem::isArrayType(contextualType))) {
1813-
// Is the array type a contextual type
1814-
contextualArrayType = contextualType;
1815-
contextualArrayElementType = *arrayElementType;
1816-
18171823
CS.addConstraint(ConstraintKind::LiteralConformsTo, contextualType,
18181824
arrayProto->getDeclaredInterfaceType(),
18191825
locator);
1820-
1821-
unsigned index = 0;
1822-
for (auto element : expr->getElements()) {
1823-
CS.addConstraint(ConstraintKind::Conversion,
1824-
CS.getType(element),
1825-
contextualArrayElementType,
1826-
CS.getConstraintLocator(
1827-
expr, LocatorPathElt::TupleElement(index++)));
1828-
}
1829-
1830-
return contextualArrayType;
1826+
joinElementTypes(arrayElementType);
1827+
return contextualType;
18311828
}
18321829

18331830
// Produce a specialized diagnostic if this is an attempt to initialize
@@ -1893,14 +1890,7 @@ namespace {
18931890

18941891
// Introduce conversions from each element to the element type of the
18951892
// array.
1896-
unsigned index = 0;
1897-
for (auto element : expr->getElements()) {
1898-
CS.addConstraint(ConstraintKind::Conversion,
1899-
CS.getType(element),
1900-
arrayElementTy,
1901-
CS.getConstraintLocator(
1902-
expr, LocatorPathElt::TupleElement(index++)));
1903-
}
1893+
joinElementTypes(arrayElementTy);
19041894

19051895
// The array element type defaults to 'Any'.
19061896
CS.addConstraint(ConstraintKind::Defaultable, arrayElementTy,

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4220,6 +4220,7 @@ bool ConstraintSystem::repairFailures(
42204220

42214221
conversionsOrFixes.push_back(CollectionElementContextualMismatch::create(
42224222
*this, lhs, rhs, getConstraintLocator(locator)));
4223+
break;
42234224
}
42244225

42254226
// Drop the `tuple element` locator element so that all tuple element
@@ -10435,35 +10436,6 @@ void ConstraintSystem::addContextualConversionConstraint(
1043510436
convertTypeLocator, /*isFavored*/ true);
1043610437
}
1043710438

10438-
Type ConstraintSystem::addJoinConstraint(
10439-
ConstraintLocator *locator,
10440-
ArrayRef<std::pair<Type, ConstraintLocator *>> inputs) {
10441-
switch (inputs.size()) {
10442-
case 0:
10443-
return Type();
10444-
10445-
case 1:
10446-
return inputs.front().first;
10447-
10448-
default:
10449-
// Produce the join below.
10450-
break;
10451-
}
10452-
10453-
// Create a type variable to capture the result of the join.
10454-
Type resultTy = createTypeVariable(locator,
10455-
(TVO_PrefersSubtypeBinding |
10456-
TVO_CanBindToNoEscape));
10457-
10458-
// Introduce conversions from each input type to the type variable.
10459-
for (const auto &input : inputs) {
10460-
addConstraint(
10461-
ConstraintKind::Conversion, input.first, resultTy, input.second);
10462-
}
10463-
10464-
return resultTy;
10465-
}
10466-
1046710439
void ConstraintSystem::addFixConstraint(ConstraintFix *fix, ConstraintKind kind,
1046810440
Type first, Type second,
1046910441
ConstraintLocatorBuilder locator,

lib/Sema/ConstraintSystem.h

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,11 @@ class TypeVariableType::Implementation {
306306
/// Retrieve the generic parameter opened by this type variable.
307307
GenericTypeParamType *getGenericParameter() const;
308308

309+
/// Returns the \c ExprKind of this type variable if it's the type of an
310+
/// atomic literal expression, meaning the literal can't be composed of subexpressions.
311+
/// Otherwise, returns \c None.
312+
Optional<ExprKind> getAtomicLiteralKind() const;
313+
309314
/// Determine whether this type variable represents a closure type.
310315
bool isClosureType() const;
311316

@@ -3076,16 +3081,78 @@ class ConstraintSystem {
30763081
Expr *expr, Type conversionType, ContextualTypePurpose purpose,
30773082
bool isOpaqueReturnType);
30783083

3084+
/// Convenience function to pass an \c ArrayRef to \c addJoinConstraint
3085+
Type addJoinConstraint(ConstraintLocator *locator,
3086+
ArrayRef<std::pair<Type, ConstraintLocator *>> inputs,
3087+
Optional<Type> supertype = None) {
3088+
return addJoinConstraint<decltype(inputs)::iterator>(
3089+
locator, inputs.begin(), inputs.end(), supertype, [](auto it) { return *it; });
3090+
}
3091+
30793092
/// Add a "join" constraint between a set of types, producing the common
30803093
/// supertype.
30813094
///
30823095
/// Currently, a "join" is modeled by a set of conversion constraints to
3083-
/// a new type variable. At some point, we may want a new constraint kind
3084-
/// to cover the join.
3096+
/// a new type variable or a specified supertype. At some point, we may want
3097+
/// a new constraint kind to cover the join.
30853098
///
3086-
/// \returns the joined type, which is generally a new type variable.
3099+
/// \note This method will merge any input type variables for atomic literal
3100+
/// expressions of the same kind. It assumes that if same-kind literal type
3101+
/// variables are joined, there will be no differing constraints on those
3102+
/// type variables.
3103+
///
3104+
/// \returns the joined type, which is generally a new type variable, unless there are
3105+
/// fewer than 2 input types or the \c supertype parameter is specified.
3106+
template<typename Iterator>
30873107
Type addJoinConstraint(ConstraintLocator *locator,
3088-
ArrayRef<std::pair<Type, ConstraintLocator *>> inputs);
3108+
Iterator begin, Iterator end,
3109+
Optional<Type> supertype,
3110+
std::function<std::pair<Type, ConstraintLocator *>(Iterator)> getType) {
3111+
if (begin == end)
3112+
return Type();
3113+
3114+
// No need to generate a new type variable if there's only one type to join
3115+
if ((begin + 1 == end) && !supertype.hasValue())
3116+
return getType(begin).first;
3117+
3118+
// The type to capture the result of the join, which is either the specified supertype,
3119+
// or a new type variable.
3120+
Type resultTy = supertype.hasValue() ? supertype.getValue() :
3121+
createTypeVariable(locator, (TVO_PrefersSubtypeBinding | TVO_CanBindToNoEscape));
3122+
3123+
using RawExprKind = uint8_t;
3124+
llvm::SmallDenseMap<RawExprKind, TypeVariableType *> representativeForKind;
3125+
3126+
// Join the input types.
3127+
while (begin != end) {
3128+
Type type;
3129+
ConstraintLocator *locator;
3130+
std::tie(type, locator) = getType(begin++);
3131+
3132+
// We can merge the type variables of same-kind atomic literal expressions because they
3133+
// will all have the same set of constraints and therefore can never resolve to anything
3134+
// different.
3135+
if (auto *typeVar = type->getAs<TypeVariableType>()) {
3136+
if (auto literalKind = typeVar->getImpl().getAtomicLiteralKind()) {
3137+
auto *&originalRep = representativeForKind[RawExprKind(*literalKind)];
3138+
auto *currentRep = getRepresentative(typeVar);
3139+
3140+
if (originalRep) {
3141+
if (originalRep != currentRep)
3142+
mergeEquivalenceClasses(currentRep, originalRep);
3143+
continue;
3144+
}
3145+
3146+
originalRep = currentRep;
3147+
}
3148+
}
3149+
3150+
// Introduce conversions from each input type to the supertype.
3151+
addConstraint(ConstraintKind::Conversion, type, resultTy, locator);
3152+
}
3153+
3154+
return resultTy;
3155+
}
30893156

30903157
/// Add a constraint to the constraint system with an associated fix.
30913158
void addFixConstraint(ConstraintFix *fix, ConstraintKind kind,

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,24 @@ TypeVariableType::Implementation::getGenericParameter() const {
8282
return locator ? locator->getGenericParameter() : nullptr;
8383
}
8484

85+
Optional<ExprKind>
86+
TypeVariableType::Implementation::getAtomicLiteralKind() const {
87+
if (!locator || !locator->directlyAt<LiteralExpr>())
88+
return None;
89+
90+
auto kind = getAsExpr(locator->getAnchor())->getKind();
91+
switch (kind) {
92+
case ExprKind::IntegerLiteral:
93+
case ExprKind::FloatLiteral:
94+
case ExprKind::StringLiteral:
95+
case ExprKind::BooleanLiteral:
96+
case ExprKind::NilLiteral:
97+
return kind;
98+
default:
99+
return None;
100+
}
101+
}
102+
85103
bool TypeVariableType::Implementation::isClosureType() const {
86104
if (!(locator && locator->getAnchor()))
87105
return false;

validation-test/SILOptimizer/large_string_array.swift.gyb

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %gyb %s > %t/main.swift
33

4-
// FIXME: <rdar://problem/66219627> Re-enable SILOptimizer/large_string_array.swift.gyb
5-
// REQUIRES: rdar66219627
6-
74
// The compiler should finish in less than 1 minute. To give some slack,
85
// specify a timeout of 5 minutes.
96
// If the compiler needs more than 5 minutes, there is probably a real problem.

validation-test/Sema/type_checker_perf/slow/rdar30596744_2.swift.gyb renamed to validation-test/Sema/type_checker_perf/fast/rdar30596744_2.swift.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %scale-test --invert-result --begin 1 --end 5 --step 1 --select NumLeafScopes %s --expected-exit-code 1
1+
// RUN: %scale-test --begin 1 --end 5 --step 1 --select NumLeafScopes %s --expected-exit-code 1
22
// REQUIRES: asserts,no_asan
33

44
% enum_cases = 3

0 commit comments

Comments
 (0)