Skip to content

Commit 890f670

Browse files
authored
Merge pull request #32481 from xedin/rework-object-literal-typechecking
[ConstraintSystem] Rework handling of object literal expressions
2 parents 0da060e + 02bc2c6 commit 890f670

File tree

10 files changed

+99
-78
lines changed

10 files changed

+99
-78
lines changed

include/swift/AST/Types.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2885,6 +2885,10 @@ class AnyFunctionType : public TypeBase {
28852885

28862886
Param getWithoutLabel() const { return Param(Ty, Identifier(), Flags); }
28872887

2888+
Param withLabel(Identifier newLabel) const {
2889+
return Param(Ty, newLabel, Flags);
2890+
}
2891+
28882892
Param withType(Type newType) const { return Param(newType, Label, Flags); }
28892893

28902894
Param withFlags(ParameterTypeFlags flags) const {

lib/Sema/CSApply.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2533,14 +2533,15 @@ namespace {
25332533
if (cs.getType(expr) && !cs.getType(expr)->hasTypeVariable())
25342534
return expr;
25352535

2536-
auto &ctx = cs.getASTContext();
2537-
25382536
// Figure out the type we're converting to.
25392537
auto openedType = cs.getType(expr);
25402538
auto type = simplifyType(openedType);
25412539
cs.setType(expr, type);
25422540

2543-
if (type->is<UnresolvedType>()) return expr;
2541+
if (type->is<UnresolvedType>())
2542+
return expr;
2543+
2544+
auto &ctx = cs.getASTContext();
25442545

25452546
Type conformingType = type;
25462547
if (auto baseType = conformingType->getOptionalObjectType()) {
@@ -2550,7 +2551,7 @@ namespace {
25502551
}
25512552

25522553
// Find the appropriate object literal protocol.
2553-
auto proto = TypeChecker::getLiteralProtocol(cs.getASTContext(), expr);
2554+
auto proto = TypeChecker::getLiteralProtocol(ctx, expr);
25542555
assert(proto && "Missing object literal protocol?");
25552556
auto conformance =
25562557
TypeChecker::conformsToProtocol(conformingType, proto, cs.DC);
@@ -2560,10 +2561,24 @@ namespace {
25602561

25612562
ConcreteDeclRef witness = conformance.getWitnessByName(
25622563
conformingType->getRValueType(), constrName);
2563-
if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
2564+
2565+
auto selectedOverload = solution.getOverloadChoiceIfAvailable(
2566+
cs.getConstraintLocator(expr, ConstraintLocator::ConstructorMember));
2567+
2568+
if (!selectedOverload)
25642569
return nullptr;
2570+
2571+
auto fnType =
2572+
simplifyType(selectedOverload->openedType)->castTo<FunctionType>();
2573+
2574+
auto newArg = coerceCallArguments(
2575+
expr->getArg(), fnType, witness,
2576+
/*applyExpr=*/nullptr, expr->getArgumentLabels(),
2577+
expr->hasTrailingClosure(),
2578+
cs.getConstraintLocator(expr, ConstraintLocator::ApplyArgument));
2579+
25652580
expr->setInitializer(witness);
2566-
expr->setArg(cs.coerceToRValue(expr->getArg()));
2581+
expr->setArg(newArg);
25672582
return expr;
25682583
}
25692584

lib/Sema/CSBindings.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,8 +1110,7 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
11101110
fix = SpecifyClosureParameterType::create(cs, dstLocator);
11111111
} else if (TypeVar->getImpl().isClosureResultType()) {
11121112
fix = SpecifyClosureReturnType::create(cs, dstLocator);
1113-
} else if (srcLocator->getAnchor() &&
1114-
isExpr<ObjectLiteralExpr>(srcLocator->getAnchor())) {
1113+
} else if (srcLocator->directlyAt<ObjectLiteralExpr>()) {
11151114
fix = SpecifyObjectLiteralTypeImport::create(cs, dstLocator);
11161115
} else if (srcLocator->isKeyPathRoot()) {
11171116
fix = SpecifyKeyPathRootType::create(cs, dstLocator);

lib/Sema/CSGen.cpp

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,60 +1357,63 @@ namespace {
13571357
if (expr->getType())
13581358
return expr->getType();
13591359

1360-
auto &de = CS.getASTContext().Diags;
1361-
auto protocol = TypeChecker::getLiteralProtocol(CS.getASTContext(), expr);
1360+
auto &ctx = CS.getASTContext();
1361+
auto &de = ctx.Diags;
1362+
auto protocol = TypeChecker::getLiteralProtocol(ctx, expr);
13621363
if (!protocol) {
13631364
de.diagnose(expr->getLoc(), diag::use_unknown_object_literal_protocol,
13641365
expr->getLiteralKindPlainName());
13651366
return nullptr;
13661367
}
13671368

1368-
auto tv = CS.createTypeVariable(exprLoc,
1369-
TVO_PrefersSubtypeBinding |
1370-
TVO_CanBindToNoEscape |
1371-
TVO_CanBindToHole);
1372-
1373-
CS.addConstraint(ConstraintKind::LiteralConformsTo, tv,
1374-
protocol->getDeclaredType(),
1375-
exprLoc);
1369+
auto witnessType = CS.createTypeVariable(
1370+
exprLoc, TVO_PrefersSubtypeBinding | TVO_CanBindToNoEscape |
1371+
TVO_CanBindToHole);
1372+
1373+
CS.addConstraint(ConstraintKind::LiteralConformsTo, witnessType,
1374+
protocol->getDeclaredType(), exprLoc);
13761375

13771376
// The arguments are required to be argument-convertible to the
13781377
// idealized parameter type of the initializer, which generally
13791378
// simplifies the first label (e.g. "colorLiteralRed:") by stripping
13801379
// all the redundant stuff about literals (leaving e.g. "red:").
13811380
// Constraint application will quietly rewrite the type of 'args' to
13821381
// use the right labels before forming the call to the initializer.
1383-
auto constrName =
1384-
TypeChecker::getObjectLiteralConstructorName(CS.getASTContext(),
1385-
expr);
1382+
auto constrName = TypeChecker::getObjectLiteralConstructorName(ctx, expr);
13861383
assert(constrName);
13871384
auto *constr = dyn_cast_or_null<ConstructorDecl>(
13881385
protocol->getSingleRequirement(constrName));
13891386
if (!constr) {
13901387
de.diagnose(protocol, diag::object_literal_broken_proto);
13911388
return nullptr;
13921389
}
1393-
auto constrParamType =
1394-
TypeChecker::getObjectLiteralParameterType(expr, constr);
13951390

1396-
// Extract the arguments.
1391+
auto *memberLoc =
1392+
CS.getConstraintLocator(expr, ConstraintLocator::ConstructorMember);
1393+
1394+
auto *memberType =
1395+
CS.createTypeVariable(memberLoc, TVO_CanBindToNoEscape);
1396+
1397+
CS.addValueMemberConstraint(MetatypeType::get(witnessType, ctx),
1398+
DeclNameRef(constrName), memberType, CurDC,
1399+
FunctionRefKind::DoubleApply, {}, memberLoc);
1400+
13971401
SmallVector<AnyFunctionType::Param, 8> args;
13981402
AnyFunctionType::decomposeInput(CS.getType(expr->getArg()), args);
13991403

1400-
// Extract the parameters.
1401-
SmallVector<AnyFunctionType::Param, 8> params;
1402-
AnyFunctionType::decomposeInput(constrParamType, params);
1404+
auto resultType = CS.createTypeVariable(
1405+
CS.getConstraintLocator(expr, ConstraintLocator::FunctionResult),
1406+
TVO_CanBindToNoEscape);
14031407

1404-
auto funcType = constr->getMethodInterfaceType()->castTo<FunctionType>();
1405-
::matchCallArguments(
1406-
CS, funcType, args, params, ConstraintKind::ArgumentConversion,
1407-
CS.getConstraintLocator(expr, ConstraintLocator::ApplyArgument));
1408+
CS.addConstraint(
1409+
ConstraintKind::ApplicableFunction,
1410+
FunctionType::get(args, resultType), memberType,
1411+
CS.getConstraintLocator(expr, ConstraintLocator::ApplyFunction));
14081412

1409-
Type result = tv;
14101413
if (constr->isFailable())
1411-
result = OptionalType::get(result);
1414+
return OptionalType::get(witnessType);
14121415

1413-
return result;
1416+
return witnessType;
14141417
}
14151418

14161419
Type visitDeclRefExpr(DeclRefExpr *E) {

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6754,7 +6754,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
67546754
return SolutionKind::Solved;
67556755
} else if ((kind == ConstraintKind::ValueMember ||
67566756
kind == ConstraintKind::ValueWitness) &&
6757-
baseObjTy->isHole()) {
6757+
baseObjTy->getMetatypeInstanceType()->isHole()) {
67586758
// If base type is a "hole" there is no reason to record any
67596759
// more "member not found" fixes for chained member references.
67606760
markMemberTypeAsPotentialHole(memberTy);

lib/Sema/ConstraintSystem.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,9 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator(
581581
if (isExpr<MemberRefExpr>(anchor))
582582
return getConstraintLocator(anchor, ConstraintLocator::Member);
583583

584+
if (isExpr<ObjectLiteralExpr>(anchor))
585+
return getConstraintLocator(anchor, ConstraintLocator::ConstructorMember);
586+
584587
return getConstraintLocator(anchor);
585588
}
586589

@@ -1666,6 +1669,34 @@ ConstraintSystem::getTypeOfMemberReference(
16661669
});
16671670
}
16681671

1672+
// Construct an idealized parameter type of the initializer associated
1673+
// with object literal, which generally simplifies the first label
1674+
// (e.g. "colorLiteralRed:") by stripping all the redundant stuff about
1675+
// literals (leaving e.g. "red:").
1676+
{
1677+
auto anchor = locator.getAnchor();
1678+
if (auto *OLE = getAsExpr<ObjectLiteralExpr>(anchor)) {
1679+
auto fnType = type->castTo<FunctionType>();
1680+
1681+
SmallVector<AnyFunctionType::Param, 4> params(fnType->getParams().begin(),
1682+
fnType->getParams().end());
1683+
1684+
switch (OLE->getLiteralKind()) {
1685+
case ObjectLiteralExpr::colorLiteral:
1686+
params[0] = params[0].withLabel(Context.getIdentifier("red"));
1687+
break;
1688+
1689+
case ObjectLiteralExpr::fileLiteral:
1690+
case ObjectLiteralExpr::imageLiteral:
1691+
params[0] = params[0].withLabel(Context.getIdentifier("resourceName"));
1692+
break;
1693+
}
1694+
1695+
type =
1696+
FunctionType::get(params, fnType->getResult(), fnType->getExtInfo());
1697+
}
1698+
}
1699+
16691700
// If we opened up any type variables, record the replacements.
16701701
recordOpenedTypes(locator, replacements);
16711702

lib/Sema/TypeChecker.cpp

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -174,42 +174,6 @@ DeclName TypeChecker::getObjectLiteralConstructorName(ASTContext &Context,
174174
llvm_unreachable("unknown literal constructor");
175175
}
176176

177-
/// Return an idealized form of the parameter type of the given
178-
/// object-literal initializer. This removes references to the protocol
179-
/// name from the first argument label, which would be otherwise be
180-
/// redundant when writing out the object-literal syntax:
181-
///
182-
/// #fileLiteral(fileReferenceLiteralResourceName: "hello.jpg")
183-
///
184-
/// Doing this allows us to preserve a nicer (and source-compatible)
185-
/// literal syntax while still giving the initializer a semantically
186-
/// unambiguous name.
187-
Type TypeChecker::getObjectLiteralParameterType(ObjectLiteralExpr *expr,
188-
ConstructorDecl *ctor) {
189-
auto params = ctor->getMethodInterfaceType()
190-
->castTo<FunctionType>()->getParams();
191-
SmallVector<AnyFunctionType::Param, 8> newParams;
192-
newParams.append(params.begin(), params.end());
193-
194-
auto replace = [&](StringRef replacement) -> Type {
195-
auto &Context = ctor->getASTContext();
196-
newParams[0] = AnyFunctionType::Param(newParams[0].getPlainType(),
197-
Context.getIdentifier(replacement),
198-
newParams[0].getParameterFlags());
199-
return AnyFunctionType::composeInput(Context, newParams,
200-
/*canonicalVararg=*/false);
201-
};
202-
203-
switch (expr->getLiteralKind()) {
204-
case ObjectLiteralExpr::colorLiteral:
205-
return replace("red");
206-
case ObjectLiteralExpr::fileLiteral:
207-
case ObjectLiteralExpr::imageLiteral:
208-
return replace("resourceName");
209-
}
210-
llvm_unreachable("unknown literal constructor");
211-
}
212-
213177
ModuleDecl *TypeChecker::getStdlibModule(const DeclContext *dc) {
214178
if (auto *stdlib = dc->getASTContext().getStdlibModule()) {
215179
return stdlib;

lib/Sema/TypeChecker.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,9 +1057,6 @@ ProtocolDecl *getLiteralProtocol(ASTContext &ctx, Expr *expr);
10571057
DeclName getObjectLiteralConstructorName(ASTContext &ctx,
10581058
ObjectLiteralExpr *expr);
10591059

1060-
Type getObjectLiteralParameterType(ObjectLiteralExpr *expr,
1061-
ConstructorDecl *ctor);
1062-
10631060
/// Get the module appropriate for looking up standard library types.
10641061
///
10651062
/// This is "Swift", if that module is imported, or the current module if

test/Sema/object_literals_ios.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ struct S: _ExpressibleByColorLiteral {
88
let y: S = #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1)
99
let y2 = #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1) // expected-error{{could not infer type of color literal}}
1010
// expected-note@-1{{import UIKit to use 'UIColor' as the default color literal type}}
11-
let y3 = #colorLiteral(red: 1, bleen: 0, grue: 0, alpha: 1) // expected-error{{incorrect argument labels in call (have 'red:bleen:grue:alpha:', expected 'red:green:blue:alpha:')}}
11+
let y3 = #colorLiteral(red: 1, bleen: 0, grue: 0, alpha: 1)
1212
// expected-error@-1 {{could not infer type of color literal}}
1313
// expected-note@-2 {{import UIKit to use 'UIColor' as the default color literal type}}
1414

15+
let _: S = #colorLiteral(red: 1, bleen: 0, grue: 0, alpha: 1)
16+
// expected-error@-1{{incorrect argument labels in call (have 'red:bleen:grue:alpha:', expected 'red:green:blue:alpha:')}} {{34-39=green}} {{44-48=blue}}
17+
1518
struct I: _ExpressibleByImageLiteral {
1619
init(imageLiteralResourceName: String) {}
1720
}

test/Sema/object_literals_osx.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ struct S: _ExpressibleByColorLiteral {
88
let y: S = #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1)
99
let y2 = #colorLiteral(red: 1, green: 0, blue: 0, alpha: 1) // expected-error{{could not infer type of color literal}}
1010
// expected-note@-1{{import AppKit to use 'NSColor' as the default color literal type}}
11-
let y3 = #colorLiteral(red: 1, bleen: 0, grue: 0, alpha: 1) // expected-error{{incorrect argument labels in call (have 'red:bleen:grue:alpha:', expected 'red:green:blue:alpha:')}}
11+
let y3 = #colorLiteral(red: 1, bleen: 0, grue: 0, alpha: 1)
1212
// expected-error@-1{{could not infer type of color literal}}
1313
// expected-note@-2{{import AppKit to use 'NSColor' as the default color literal type}}
1414

15+
let _: S = #colorLiteral(red: 1, bleen: 0, grue: 0, alpha: 1)
16+
// expected-error@-1 {{incorrect argument labels in call (have 'red:bleen:grue:alpha:', expected 'red:green:blue:alpha:')}} {{34-39=green}} {{44-48=blue}}
17+
1518
struct I: _ExpressibleByImageLiteral {
1619
init(imageLiteralResourceName: String) {}
1720
}
@@ -21,7 +24,7 @@ let z2 = #imageLiteral(resourceName: "hello.png") // expected-error{{could not i
2124
// expected-note@-1{{import AppKit to use 'NSImage' as the default image literal type}}
2225

2326
struct Path: _ExpressibleByFileReferenceLiteral {
24-
init(fileReferenceLiteralResourceName: String) {}
27+
init(fileReferenceLiteralResourceName: String) {} // expected-note {{'init(fileReferenceLiteralResourceName:)' declared here}}
2528
}
2629

2730
let p1: Path = #fileLiteral(resourceName: "what.txt")
@@ -32,10 +35,12 @@ let text = #fileLiteral(resourceName: "TextFile.txt").relativeString! // expecte
3235
// expected-note@-1{{import Foundation to use 'URL' as the default file reference literal type}}
3336

3437
// rdar://problem/49861813
35-
#fileLiteral() // expected-error{{missing argument for parameter 'resourceName' in call}}
38+
#fileLiteral()
3639
// expected-error@-1{{could not infer type of file reference literal}}
3740
// expected-note@-2{{import Foundation to use 'URL' as the default file reference literal type}}
3841

42+
let _: Path = #fileLiteral() // expected-error {{missing argument for parameter 'resourceName' in call}}
43+
3944
// rdar://problem/62927467
4045
func test_literal_arguments_are_loaded() {
4146
var resource = "foo.txt" // expected-warning {{variable 'resource' was never mutated; consider changing to 'let' constant}}

0 commit comments

Comments
 (0)