Skip to content

Commit 228feb1

Browse files
authored
Merge pull request #7146 from DougGregor/string-interpolation-typecheck-perf
[3.1] Improve compile-time performance of string interpolation
2 parents 06dcf30 + aebe73a commit 228feb1

File tree

10 files changed

+102
-112
lines changed

10 files changed

+102
-112
lines changed

lib/Sema/CSApply.cpp

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,45 +2099,32 @@ namespace {
20992099
// Create a tuple containing all of the segments.
21002100
SmallVector<Expr *, 4> segments;
21012101
SmallVector<Identifier, 4> names;
2102-
unsigned index = 0;
21032102
ConstraintLocatorBuilder locatorBuilder(cs.getConstraintLocator(expr));
21042103
for (auto segment : expr->getSegments()) {
2105-
auto locator = cs.getConstraintLocator(
2106-
locatorBuilder.withPathElement(
2107-
LocatorPathElt::getInterpolationArgument(index++)));
2108-
2109-
// Find the initializer we chose.
2110-
auto choice = getOverloadChoice(locator);
2111-
2112-
auto memberRef = buildMemberRef(
2113-
typeRef, choice.openedFullType,
2114-
segment->getStartLoc(), choice.choice.getDecl(),
2115-
DeclNameLoc(segment->getStartLoc()),
2116-
choice.openedType,
2117-
locator, locator, /*Implicit=*/true,
2118-
choice.choice.getFunctionRefKind(),
2119-
AccessSemantics::Ordinary,
2120-
/*isDynamic=*/false);
21212104
ApplyExpr *apply =
21222105
CallExpr::createImplicit(
2123-
tc.Context, memberRef,
2106+
tc.Context, typeRef,
21242107
{ segment },
21252108
{ tc.Context.Id_stringInterpolationSegment });
21262109
cs.cacheType(apply->getArg());
21272110

2128-
auto converted = finishApply(apply, openedType, locatorBuilder);
2129-
if (!converted)
2130-
return nullptr;
2111+
Expr *convertedSegment = apply;
2112+
if (tc.typeCheckExpressionShallow(convertedSegment, cs.DC))
2113+
continue;
21312114

2132-
segments.push_back(converted);
2115+
segments.push_back(convertedSegment);
21332116

2134-
if (index == 1) {
2117+
if (names.empty()) {
21352118
names.push_back(tc.Context.Id_stringInterpolation);
21362119
} else {
21372120
names.push_back(Identifier());
21382121
}
21392122
}
21402123

2124+
// If all of the segments had errors, bail out.
2125+
if (segments.empty())
2126+
return nullptr;
2127+
21412128
// Call the init(stringInterpolation:) initializer with the arguments.
21422129
ApplyExpr *apply = CallExpr::createImplicit(tc.Context, memberRef,
21432130
segments, names);
@@ -4115,22 +4102,7 @@ findCalleeDeclRef(ConstraintSystem &cs, const Solution &solution,
41154102

41164103
unsigned newFlags = locator->getSummaryFlags();
41174104

4118-
// If we have an interpolation argument, dig out the constructor if we
4119-
// can.
4120-
// FIXME: This representation is actually quite awful
4121-
if (newPath.size() == 1 &&
4122-
newPath[0].getKind() == ConstraintLocator::InterpolationArgument) {
4123-
newPath.push_back(ConstraintLocator::ConstructorMember);
4124-
4125-
locator = cs.getConstraintLocator(locator->getAnchor(), newPath, newFlags);
4126-
auto known = solution.overloadChoices.find(locator);
4127-
if (known != solution.overloadChoices.end()) {
4128-
auto &choice = known->second.choice;
4129-
if (choice.getKind() == OverloadChoiceKind::Decl)
4130-
return cast<AbstractFunctionDecl>(choice.getDecl());
4131-
}
4132-
return nullptr;
4133-
} else if (isSubscript) {
4105+
if (isSubscript) {
41344106
newPath.push_back(ConstraintLocator::SubscriptMember);
41354107
} else {
41364108
newPath.push_back(ConstraintLocator::ApplyFunction);

lib/Sema/CSDiag.cpp

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -222,22 +222,6 @@ void constraints::simplifyLocator(Expr *&anchor,
222222
}
223223
break;
224224

225-
case ConstraintLocator::InterpolationArgument:
226-
if (auto interp = dyn_cast<InterpolatedStringLiteralExpr>(anchor)) {
227-
unsigned index = path[0].getValue();
228-
if (index < interp->getSegments().size()) {
229-
// No additional target locator information.
230-
// FIXME: Dig out the constructor we're trying to call?
231-
targetAnchor = nullptr;
232-
targetPath.clear();
233-
234-
anchor = interp->getSegments()[index];
235-
path = path.slice(1);
236-
continue;
237-
}
238-
}
239-
break;
240-
241225
case ConstraintLocator::SubscriptIndex:
242226
if (auto subscript = dyn_cast<SubscriptExpr>(anchor)) {
243227
targetAnchor = subscript->getBase();

lib/Sema/CSGen.cpp

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,16 @@ namespace {
10221022
DeclContext *CurDC;
10231023
SmallVector<DeclContext*, 4> DCStack;
10241024

1025+
static const unsigned numEditorPlaceholderVariables = 2;
1026+
1027+
/// A buffer of type variables used for editor placeholders. We only
1028+
/// use a small number of these (rotating through), to prevent expressions
1029+
/// with a large number of editor placeholders from flooding the constraint
1030+
/// system with type variables.
1031+
TypeVariableType *editorPlaceholderVariables[numEditorPlaceholderVariables]
1032+
= { nullptr, nullptr };
1033+
unsigned currentEditorPlaceholderVariable = 0;
1034+
10251035
/// \brief Add constraints for a reference to a named member of the given
10261036
/// base type, and return the type of such a reference.
10271037
Type addMemberRefConstraints(Expr *expr, Expr *base, DeclName name,
@@ -1224,7 +1234,6 @@ namespace {
12241234
visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *expr) {
12251235
// Dig out the ExpressibleByStringInterpolation protocol.
12261236
auto &tc = CS.getTypeChecker();
1227-
auto &C = CS.getASTContext();
12281237
auto interpolationProto
12291238
= tc.getProtocol(expr->getLoc(),
12301239
KnownProtocolKind::ExpressibleByStringInterpolation);
@@ -1241,26 +1250,6 @@ namespace {
12411250
interpolationProto->getDeclaredType(),
12421251
locator);
12431252

1244-
// Each of the segments is passed as an argument to
1245-
// init(stringInterpolationSegment:).
1246-
unsigned index = 0;
1247-
auto tvMeta = MetatypeType::get(tv);
1248-
for (auto segment : expr->getSegments()) {
1249-
auto locator = CS.getConstraintLocator(
1250-
expr,
1251-
LocatorPathElt::getInterpolationArgument(index++));
1252-
auto segmentTyV = CS.createTypeVariable(locator, /*options=*/0);
1253-
auto returnTyV = CS.createTypeVariable(locator, /*options=*/0);
1254-
auto methodTy = FunctionType::get(segmentTyV, returnTyV);
1255-
1256-
CS.addConstraint(ConstraintKind::Conversion, CS.getType(segment),
1257-
segmentTyV, locator);
1258-
1259-
DeclName segmentName(C, C.Id_init, { C.Id_stringInterpolationSegment });
1260-
CS.addValueMemberConstraint(tvMeta, segmentName, methodTy, CurDC,
1261-
FunctionRefKind::DoubleApply, locator);
1262-
}
1263-
12641253
return tv;
12651254
}
12661255

@@ -2760,14 +2749,28 @@ namespace {
27602749
Type visitEditorPlaceholderExpr(EditorPlaceholderExpr *E) {
27612750
if (E->getTypeLoc().isNull()) {
27622751
auto locator = CS.getConstraintLocator(E);
2763-
auto placeholderTy = CS.createTypeVariable(locator, /*options*/0);
2752+
27642753
// A placeholder may have any type, but default to Void type if
27652754
// otherwise unconstrained.
2766-
CS.addConstraint(ConstraintKind::Defaultable,
2767-
placeholderTy, TupleType::getEmpty(CS.getASTContext()),
2768-
locator);
2755+
auto &placeholderTy
2756+
= editorPlaceholderVariables[currentEditorPlaceholderVariable];
2757+
if (!placeholderTy) {
2758+
placeholderTy = CS.createTypeVariable(locator, /*options*/0);
2759+
2760+
CS.addConstraint(ConstraintKind::Defaultable,
2761+
placeholderTy,
2762+
TupleType::getEmpty(CS.getASTContext()),
2763+
locator);
2764+
}
2765+
2766+
// Move to the next placeholder variable.
2767+
currentEditorPlaceholderVariable
2768+
= (currentEditorPlaceholderVariable + 1) %
2769+
numEditorPlaceholderVariables;
2770+
27692771
return placeholderTy;
27702772
}
2773+
27712774
// NOTE: The type loc may be there but have failed to validate, in which
27722775
// case we return the null type.
27732776
return E->getType();

lib/Sema/CSSolver.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2465,17 +2465,6 @@ bool ConstraintSystem::solveSimplified(
24652465
if (*restriction == ConversionRestrictionKind::TupleToTuple)
24662466
break;
24672467
}
2468-
2469-
// Or, if we see a conversion successfully applied to a string
2470-
// interpolation argument, we're done.
2471-
// FIXME: Probably should be more general, as mentioned above.
2472-
if (auto locator = disjunction->getLocator()) {
2473-
if (!locator->getPath().empty() &&
2474-
locator->getPath().back().getKind()
2475-
== ConstraintLocator::InterpolationArgument &&
2476-
constraint->getKind() == ConstraintKind::Conversion)
2477-
break;
2478-
}
24792468
}
24802469

24812470
if (TC.getLangOpts().DebugConstraintSolver) {

lib/Sema/ConstraintLocator.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
7070
case ScalarToTuple:
7171
case Load:
7272
case GenericArgument:
73-
case InterpolationArgument:
7473
case NamedTupleElement:
7574
case TupleElement:
7675
case ApplyArgToParam:
@@ -164,10 +163,6 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) {
164163
out << "instance type";
165164
break;
166165

167-
case InterpolationArgument:
168-
out << "interpolation argument #" << llvm::utostr(elt.getValue());
169-
break;
170-
171166
case Load:
172167
out << "load";
173168
break;

lib/Sema/ConstraintLocator.h

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,6 @@ class ConstraintLocator : public llvm::FoldingSetNode {
9191
SubscriptIndex,
9292
/// \brief The result of a subscript expression.
9393
SubscriptResult,
94-
/// \brief An argument to string interpolation.
95-
InterpolationArgument,
9694
/// \brief The lookup for a constructor member.
9795
ConstructorMember,
9896
/// \brief Rvalue adjustment.
@@ -155,7 +153,6 @@ class ConstraintLocator : public llvm::FoldingSetNode {
155153
return 0;
156154

157155
case GenericArgument:
158-
case InterpolationArgument:
159156
case NamedTupleElement:
160157
case TupleElement:
161158
return 1;
@@ -204,7 +201,6 @@ class ConstraintLocator : public llvm::FoldingSetNode {
204201
case Archetype:
205202
case AssociatedType:
206203
case GenericArgument:
207-
case InterpolationArgument:
208204
case NamedTupleElement:
209205
case TupleElement:
210206
case Requirement:
@@ -335,12 +331,6 @@ class ConstraintLocator : public llvm::FoldingSetNode {
335331
return PathElement(GenericArgument, position);
336332
}
337333

338-
/// \brief Retrieve a path element for an argument to string
339-
/// interpolation.
340-
static PathElement getInterpolationArgument(unsigned position) {
341-
return PathElement(InterpolationArgument, position);
342-
}
343-
344334
/// \brief Retrieve the kind of path element.
345335
PathElementKind getKind() const {
346336
switch (static_cast<StoredKind>(storedKind)) {

stdlib/public/core/StringInterpolation.swift.gyb

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,29 +68,35 @@ extension String : _ExpressibleByStringInterpolation {
6868
self = String(describing: expr)
6969
}
7070

71-
% for Type in StreamableTypes:
7271
/// Creates a string containing the given value's textual representation.
7372
///
7473
/// Do not call this initializer directly. It is used by the compiler when
7574
/// interpreting string interpolations.
7675
///
7776
/// - SeeAlso: `ExpressibleByStringInterpolation`
78-
public init(stringInterpolationSegment expr: ${Type}) {
77+
public init<T: TextOutputStreamable> (stringInterpolationSegment expr: T) {
7978
self = _toStringReadOnlyStreamable(expr)
8079
}
81-
% end
8280

83-
% for Type in PrintableTypes:
8481
/// Creates a string containing the given value's textual representation.
8582
///
8683
/// Do not call this initializer directly. It is used by the compiler when
8784
/// interpreting string interpolations.
8885
///
8986
/// - SeeAlso: `ExpressibleByStringInterpolation`
90-
public init(stringInterpolationSegment expr: ${Type}) {
87+
public init<T: CustomStringConvertible> (stringInterpolationSegment expr: T) {
9188
self = _toStringReadOnlyPrintable(expr)
9289
}
93-
% end
90+
91+
/// Creates a string containing the given value's textual representation.
92+
///
93+
/// Do not call this initializer directly. It is used by the compiler when
94+
/// interpreting string interpolations.
95+
///
96+
/// - SeeAlso: `ExpressibleByStringInterpolation`
97+
public init<T: TextOutputStreamable & CustomStringConvertible> (stringInterpolationSegment expr: T) {
98+
self = _toStringReadOnlyStreamable(expr)
99+
}
94100
}
95101

96102
// ${'Local Variables'}:
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %target-typecheck-verify-swift -typecheck -debug-constraints %s > %t.dump 2>&1
2+
// RUN: %FileCheck %s < %t.dump
3+
4+
// Make sure that the interpolation segments get placed into separate connected
5+
// components.
6+
// CHECK: ---Connected components---
7+
// CHECK-NEXT: 0:
8+
// CHECK-NEXT: 1:
9+
// CHECK-NEXT: 2:
10+
// CHECK-NEXT: 3:
11+
// CHECK-NEXT: 4:
12+
// CHECK-NEXT: 5:
13+
// CHECK-NEXT: 6:
14+
// CHECK-NEXT: 7:
15+
// CHECK-NEXT: 8:
16+
// CHECK-NEXT: 9:
17+
18+
// CHECK: (solving component #
19+
// CHECK: literal=3 bindings=(subtypes of) (default from ExpressibleByStringLiteral) String)
20+
21+
// CHECK: (solving component #
22+
// CHECK: literal=3 bindings=(subtypes of) (default from ExpressibleByIntegerLiteral) Int)
23+
24+
// CHECK: (solving component #
25+
// CHECK: literal=3 bindings=(subtypes of) (default from ExpressibleByStringLiteral) String)
26+
27+
// CHECK: (solving component #
28+
// CHECK: literal=3 bindings=(subtypes of) (default from ExpressibleByIntegerLiteral) Int)
29+
30+
// CHECK: (solving component #
31+
// CHECK: literal=3 bindings=(subtypes of) (default from ExpressibleByStringLiteral) String)
32+
33+
// CHECK: (solving component #
34+
// CHECK: literal=3 bindings=(subtypes of) (default from ExpressibleByIntegerLiteral) Int)
35+
36+
// CHECK: (solving component #
37+
// CHECK: literal=3 bindings=(subtypes of) (default from ExpressibleByStringLiteral) String)
38+
39+
// CHECK: (solving component #
40+
// CHECK: literal=3 bindings=(subtypes of) (default from ExpressibleByIntegerLiteral) Int)
41+
42+
// CHECK: (solving component #
43+
// CHECK: literal=3 bindings=(subtypes of) (default from ExpressibleByStringLiteral) String)
44+
45+
_ = "\(1), \(2), \(3), \(4)"

test/api-digester/source-stability.swift.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Protocol IndexableBase has been removed (deprecated)
1212
Protocol MutableIndexable has been removed (deprecated)
1313
Protocol RandomAccessIndexable has been removed (deprecated)
1414
Protocol RangeReplaceableIndexable has been removed (deprecated)
15+
Constructor String.init(stringInterpolationSegment:) has been removed
1516
Func Array.append(contentsOf:) has been removed
1617
Func ArraySlice.append(contentsOf:) has been removed
1718
Func ContiguousArray.append(contentsOf:) has been removed
@@ -34,6 +35,7 @@ Constructor RangeReplaceableBidirectionalSlice.init(base:bounds:) has 2nd parame
3435
Constructor RangeReplaceableRandomAccessSlice.init(base:bounds:) has 2nd parameter type change from Range<Base.Index> to Range<RangeReplaceableRandomAccessSlice.Index>
3536
Constructor RangeReplaceableSlice.init(base:bounds:) has 2nd parameter type change from Range<Base.Index> to Range<RangeReplaceableSlice.Index>
3637
Constructor Slice.init(base:bounds:) has 2nd parameter type change from Range<Base.Index> to Range<Slice.Index>
38+
Constructor String.init(stringInterpolationSegment:) has 1st parameter type change from String to T
3739
Func AnyBidirectionalCollection.makeIterator() has return type change from AnyIterator<Element> to AnyBidirectionalCollection.Iterator
3840
Func AnyCollection.makeIterator() has return type change from AnyIterator<Element> to AnyCollection.Iterator
3941
Func AnyRandomAccessCollection.makeIterator() has return type change from AnyIterator<Element> to AnyRandomAccessCollection.Iterator
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// RUN: not %target-swift-frontend -typecheck %s
2+
3+
let query = "<#...#>=\(<#...#>)&<#...#>=\(<#...#>)&<#...#>=\(<#...#>)&<#...#>=\(<#...#>)&<#...#>=\(<#...#>)&<#...#>=\(<#...#>)&<#...#>=\(<#...#>)&<#...#>=\(<#...#>)&<#...#>=\(<#...#>)&<#...#>=\(<#...#>)&<#...#>=\(<#...#>)&<#...#>=\(<#...#>)&<#...#>=\(<#...#>)&<#...#>=\(<#...#>)&<#...#>=\(<#...#>)&<#...#>=\(<#...#>)"
4+

0 commit comments

Comments
 (0)