Skip to content

Commit a1c1693

Browse files
committed
Change the names of the object-literal initializers to be
semantically unambiguous. We didn't actually intend to change how programmers normally constructed these types, but the change to the object literal syntax accidentally caused these initializers to have very natural-seeming signatures. These initializers also created possible ambiguities with the actual initializers. Renaming them to refer to their function as literal initializers is the right thing to do. Unfortunately, this provided to be somewhat annoying, as the code was written to assume that the argument tuple following e.g. #colorLiteral could be directly passed to the initializer. We solve this by hacking on both ends of the constraint system: during generation we form a conversion constraint to the original, idealized parameter type, and during application we rewrite the argument tuple type to use the actual labels. This nicely limits the additional complexity to just the parts dealing with object literals. Note that we can't just implicitly rewrite the tuple expression because that would break invariants tying the labels to physical source ranges. We also don't want to just change the literal syntax again and break compatibility with existing uses. rdar://26148507
1 parent 949a6b9 commit a1c1693

13 files changed

+116
-33
lines changed

lib/Sema/CSApply.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2047,6 +2047,38 @@ namespace {
20472047
}
20482048
}
20492049

2050+
/// Adjust the type of the arguments to an object literal.
2051+
///
2052+
/// The constraint system has verified that the arguments are convertible
2053+
/// to an idealized initializer signature, but we need them to be
2054+
/// convertible to the actual initializer signature, which differs only
2055+
/// by labels. We make this work by rewriting the type of the argument
2056+
/// expression to use the correct labels. This creates an inconsistency
2057+
/// between the labels of the TupleExpr and its type, but it has the
2058+
/// great merit of not requiring any complexity outside of the treatment
2059+
/// of object literals.
2060+
void adjustObjectLiteralArgumentType(Expr *arg, DeclName constrName) {
2061+
// If the argument expression isn't a tuple, propagating this type
2062+
// is more complicated than we can do here. Bailing out will probably
2063+
// cause a terrible diagnostic, but fortunately, this is not a case we
2064+
// need excellent QoI for.
2065+
if (!isa<TupleExpr>(arg))
2066+
return;
2067+
2068+
auto paramLabels = constrName.getArgumentNames();
2069+
2070+
auto argType = arg->getType()->getAs<TupleType>();
2071+
if (!argType || argType->getNumElements() != paramLabels.size())
2072+
return;
2073+
2074+
SmallVector<TupleTypeElt, 4> newElts;
2075+
size_t index = 0;
2076+
for (auto &oldElt : argType->getElements()) {
2077+
newElts.push_back(TupleTypeElt(oldElt.getType(), paramLabels[index++]));
2078+
}
2079+
arg->setType(TupleType::get(newElts, cs.getASTContext()));
2080+
}
2081+
20502082
Expr *visitObjectLiteralExpr(ObjectLiteralExpr *expr) {
20512083
if (expr->getType() && !expr->getType()->hasTypeVariable())
20522084
return expr;
@@ -2078,6 +2110,7 @@ namespace {
20782110

20792111
DeclName constrName(tc.getObjectLiteralConstructorName(expr));
20802112
Expr *arg = expr->getArg();
2113+
adjustObjectLiteralArgumentType(arg, constrName);
20812114
Expr *base = TypeExpr::createImplicitHack(expr->getLoc(), conformingType,
20822115
ctx);
20832116
Expr *semanticExpr = tc.callWitness(base, dc, proto, conformance,

lib/Sema/CSDiag.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4868,8 +4868,9 @@ bool FailureDiagnosis::visitObjectLiteralExpr(ObjectLiteralExpr *E) {
48684868
if (constrs.size() != 1 || !isa<ConstructorDecl>(constrs.front()))
48694869
return false;
48704870
auto *constr = cast<ConstructorDecl>(constrs.front());
4871+
auto paramType = TC.getObjectLiteralParameterType(E, constr);
48714872
if (!typeCheckChildIndependently(
4872-
E->getArg(), constr->getArgumentType(), CTP_CallArgument))
4873+
E->getArg(), paramType, CTP_CallArgument))
48734874
return true;
48744875

48754876
// Conditions for showing this diagnostic:

lib/Sema/CSGen.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,7 +1386,12 @@ namespace {
13861386
protocol->getDeclaredType(),
13871387
CS.getConstraintLocator(expr));
13881388

1389-
// Add constraint on args.
1389+
// The arguments are required to be argument-convertible to the
1390+
// idealized parameter type of the initializer, which generally
1391+
// simplifies the first label (e.g. "colorLiteralRed:") by stripping
1392+
// all the redundant stuff about literals (leaving e.g. "red:").
1393+
// Constraint application will quietly rewrite the type of 'args' to
1394+
// use the right labels before forming the call to the initializer.
13901395
DeclName constrName = tc.getObjectLiteralConstructorName(expr);
13911396
assert(constrName);
13921397
ArrayRef<ValueDecl *> constrs = protocol->lookupDirect(constrName);
@@ -1395,8 +1400,9 @@ namespace {
13951400
return nullptr;
13961401
}
13971402
auto *constr = cast<ConstructorDecl>(constrs.front());
1403+
auto constrParamType = tc.getObjectLiteralParameterType(expr, constr);
13981404
CS.addConstraint(ConstraintKind::ArgumentTupleConversion,
1399-
expr->getArg()->getType(), constr->getArgumentType(),
1405+
expr->getArg()->getType(), constrParamType,
14001406
CS.getConstraintLocator(expr, ConstraintLocator::ApplyArgument));
14011407

14021408
Type result = tv;

lib/Sema/TypeChecker.cpp

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -168,18 +168,56 @@ ProtocolDecl *TypeChecker::getLiteralProtocol(Expr *expr) {
168168

169169
DeclName TypeChecker::getObjectLiteralConstructorName(ObjectLiteralExpr *expr) {
170170
switch (expr->getLiteralKind()) {
171-
case ObjectLiteralExpr::colorLiteral: {
172-
return DeclName(Context, Context.Id_init,
173-
{ Context.getIdentifier("red"),
174-
Context.getIdentifier("green"),
175-
Context.getIdentifier("blue"),
176-
Context.getIdentifier("alpha") });
177-
}
178-
case ObjectLiteralExpr::imageLiteral:
179-
case ObjectLiteralExpr::fileLiteral: {
180-
return DeclName(Context, Context.Id_init,
181-
{ Context.getIdentifier("resourceName") });
182-
}
171+
case ObjectLiteralExpr::colorLiteral: {
172+
return DeclName(Context, Context.Id_init,
173+
{ Context.getIdentifier("colorLiteralRed"),
174+
Context.getIdentifier("green"),
175+
Context.getIdentifier("blue"),
176+
Context.getIdentifier("alpha") });
177+
}
178+
case ObjectLiteralExpr::imageLiteral: {
179+
return DeclName(Context, Context.Id_init,
180+
{ Context.getIdentifier("imageLiteralResourceName") });
181+
}
182+
case ObjectLiteralExpr::fileLiteral: {
183+
return DeclName(Context, Context.Id_init,
184+
{ Context.getIdentifier("fileReferenceLiteralResourceName") });
185+
}
186+
}
187+
llvm_unreachable("unknown literal constructor");
188+
}
189+
190+
/// Return an idealized form of the parameter type of the given
191+
/// object-literal initializer. This removes references to the protocol
192+
/// name from the first argument label, which would be otherwise be
193+
/// redundant when writing out the object-literal syntax:
194+
///
195+
/// #fileLiteral(fileReferenceLiteralResourceName: "hello.jpg")
196+
///
197+
/// Doing this allows us to preserve a nicer (and source-compatible)
198+
/// literal syntax while still giving the initializer a semantically
199+
/// unambiguous name.
200+
Type TypeChecker::getObjectLiteralParameterType(ObjectLiteralExpr *expr,
201+
ConstructorDecl *ctor) {
202+
Type argType = ctor->getArgumentType();
203+
auto argTuple = argType->getAs<TupleType>();
204+
if (!argTuple) return argType;
205+
206+
auto replace = [&](StringRef replacement) -> Type {
207+
SmallVector<TupleTypeElt, 4> elements;
208+
elements.append(argTuple->getElements().begin(),
209+
argTuple->getElements().end());
210+
elements[0] = TupleTypeElt(elements[0].getType(),
211+
Context.getIdentifier(replacement));
212+
return TupleType::get(elements, Context);
213+
};
214+
215+
switch (expr->getLiteralKind()) {
216+
case ObjectLiteralExpr::colorLiteral:
217+
return replace("red");
218+
case ObjectLiteralExpr::fileLiteral:
219+
case ObjectLiteralExpr::imageLiteral:
220+
return replace("resourceName");
183221
}
184222
llvm_unreachable("unknown literal constructor");
185223
}

lib/Sema/TypeChecker.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1661,6 +1661,9 @@ class TypeChecker final : public LazyResolver {
16611661

16621662
DeclName getObjectLiteralConstructorName(ObjectLiteralExpr *expr);
16631663

1664+
Type getObjectLiteralParameterType(ObjectLiteralExpr *expr,
1665+
ConstructorDecl *ctor);
1666+
16641667
/// Get the module appropriate for looking up standard library types.
16651668
///
16661669
/// This is "Swift", if that module is imported, or the current module if

stdlib/public/SDK/AppKit/AppKit.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public func NSApplicationMain(
6868
) -> Int32
6969

7070
extension NSColor : _ColorLiteralConvertible {
71-
public required convenience init(red: Float, green: Float,
71+
public required convenience init(colorLiteralRed red: Float, green: Float,
7272
blue: Float, alpha: Float) {
7373
self.init(srgbRed: CGFloat(red), green: CGFloat(green),
7474
blue: CGFloat(blue), alpha: CGFloat(alpha))
@@ -82,7 +82,7 @@ extension NSImage : _ImageLiteralConvertible {
8282
self.init(named: name)
8383
}
8484

85-
public required convenience init(resourceName name: String) {
85+
public required convenience init(imageLiteralResourceName name: String) {
8686
self.init(failableImageLiteral: name)
8787
}
8888
}

stdlib/public/SDK/Foundation/Foundation.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1338,7 +1338,8 @@ extension NSURL : _FileReferenceLiteralConvertible {
13381338
self.init(fileURLWithPath: fullPath)
13391339
}
13401340

1341-
public required convenience init(resourceName path: String) {
1341+
public required convenience
1342+
init(fileReferenceLiteralResourceName path: String) {
13421343
self.init(failableFileReferenceLiteral: path)
13431344
}
13441345
}

stdlib/public/SDK/UIKit/UIKit.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,8 @@ extension UIView : _DefaultCustomPlaygroundQuickLookable {
219219
#endif
220220

221221
extension UIColor : _ColorLiteralConvertible {
222-
@nonobjc public required convenience init(red: Float, green: Float,
222+
@nonobjc public required convenience init(colorLiteralRed red: Float,
223+
green: Float,
223224
blue: Float, alpha: Float) {
224225
self.init(red: CGFloat(red), green: CGFloat(green),
225226
blue: CGFloat(blue), alpha: CGFloat(alpha))
@@ -233,7 +234,7 @@ extension UIImage : _ImageLiteralConvertible {
233234
self.init(named: name)
234235
}
235236

236-
public required convenience init(resourceName name: String) {
237+
public required convenience init(imageLiteralResourceName name: String) {
237238
self.init(failableImageLiteral: name)
238239
}
239240
}

stdlib/public/core/CompilerProtocols.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -497,19 +497,19 @@ public protocol StringInterpolationConvertible {
497497
/// Conforming types can be initialized with color literals (e.g.
498498
/// `#colorLiteral(red: 1, green: 0, blue: 0, alpha: 1)`).
499499
public protocol _ColorLiteralConvertible {
500-
init(red: Float, green: Float, blue: Float, alpha: Float)
500+
init(colorLiteralRed red: Float, green: Float, blue: Float, alpha: Float)
501501
}
502502

503503
/// Conforming types can be initialized with image literals (e.g.
504504
/// `#imageLiteral(resourceName: "hi.png")`).
505505
public protocol _ImageLiteralConvertible {
506-
init(resourceName: String)
506+
init(imageLiteralResourceName path: String)
507507
}
508508

509509
/// Conforming types can be initialized with strings (e.g.
510510
/// `#fileLiteral(resourceName: "resource.txt")`).
511511
public protocol _FileReferenceLiteralConvertible {
512-
init(resourceName: String)
512+
init(fileReferenceLiteralResourceName path: String)
513513
}
514514

515515
/// A container is destructor safe if whether it may store to memory on

test/IDE/complete_value_literals.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ func testTuple2() {
201201
// TUPLE_2: Literal[Tuple]/None/TypeRelation[Identical]: ({#(values)#})[#(MyInt1, MyString1, MyDouble1)#];
202202

203203
struct MyColor1: _ColorLiteralConvertible {
204-
init(red: Float, green: Float, blue: Float, alpha: Float) {}
204+
init(colorLiteralRed: Float, green: Float, blue: Float, alpha: Float) {}
205205
}
206206
func testColor0() {
207207
let x: Int = #^COLOR_0^#
@@ -219,7 +219,7 @@ func testColor2() {
219219
// COLOR_2: Literal[_Color]/None/TypeRelation[Convertible]: #colorLiteral({#red: Float#}, {#green: Float#}, {#blue: Float#}, {#alpha: Float#})[#MyColor1#];
220220

221221
struct MyImage1: _ImageLiteralConvertible {
222-
init(resourceName: String) {}
222+
init(imageLiteralResourceName: String) {}
223223
}
224224
func testImage0() {
225225
let x: Int = #^IMAGE_0^#

0 commit comments

Comments
 (0)