Skip to content

Commit a9660c7

Browse files
authored
Merge pull request #17860 from xedin/SE-0213
[TypeChecker] SE-0213: Implement literal init via coercion
2 parents 86c26a9 + 29bbc99 commit a9660c7

File tree

14 files changed

+173
-20
lines changed

14 files changed

+173
-20
lines changed

include/swift/AST/Expr.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4306,6 +4306,11 @@ class IsExpr : public CheckedCastExpr {
43064306
///
43074307
/// Spelled 'a as T' and produces a value of type 'T'.
43084308
class CoerceExpr : public ExplicitCastExpr {
4309+
/// Since there is already `asLoc` location,
4310+
/// we use it to store `start` of the initializer
4311+
/// call source range to save some storage.
4312+
SourceLoc InitRangeEnd;
4313+
43094314
public:
43104315
CoerceExpr(Expr *sub, SourceLoc asLoc, TypeLoc type)
43114316
: ExplicitCastExpr(ExprKind::Coerce, sub, asLoc, type, type.getType())
@@ -4315,6 +4320,29 @@ class CoerceExpr : public ExplicitCastExpr {
43154320
: CoerceExpr(nullptr, asLoc, type)
43164321
{ }
43174322

4323+
private:
4324+
CoerceExpr(SourceRange initRange, Expr *literal, TypeLoc type)
4325+
: ExplicitCastExpr(ExprKind::Coerce, literal, initRange.Start,
4326+
type, type.getType()), InitRangeEnd(initRange.End)
4327+
{ setImplicit(); }
4328+
4329+
public:
4330+
/// Create an implicit coercion expression for literal initialization
4331+
/// preserving original source information, this way original call
4332+
/// could be recreated if needed.
4333+
static CoerceExpr *forLiteralInit(ASTContext &ctx, Expr *literal,
4334+
SourceRange range, TypeLoc literalType) {
4335+
return new (ctx) CoerceExpr(range, literal, literalType);
4336+
}
4337+
4338+
bool isLiteralInit() const { return InitRangeEnd.isValid(); }
4339+
4340+
SourceRange getSourceRange() const {
4341+
return isLiteralInit()
4342+
? SourceRange(getAsLoc(), InitRangeEnd)
4343+
: ExplicitCastExpr::getSourceRange();
4344+
}
4345+
43184346
static bool classof(const Expr *E) {
43194347
return E->getKind() == ExprKind::Coerce;
43204348
}

lib/Sema/CSApply.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3621,6 +3621,36 @@ namespace {
36213621

36223622
auto &tc = cs.getTypeChecker();
36233623

3624+
// Since this is literal initialization, we don't
3625+
// really need to keep wrapping coercion around.
3626+
if (expr->isLiteralInit()) {
3627+
auto *literalInit = expr->getSubExpr();
3628+
// If literal got converted into constructor call
3629+
// lets put proper source information in place.
3630+
if (auto *call = dyn_cast<CallExpr>(literalInit)) {
3631+
call->getFn()->forEachChildExpr([&](Expr *subExpr) -> Expr * {
3632+
auto *TE = dyn_cast<TypeExpr>(subExpr);
3633+
if (!TE)
3634+
return subExpr;
3635+
3636+
auto type = TE->getInstanceType(
3637+
[&](const Expr *expr) { return cs.hasType(expr); },
3638+
[&](const Expr *expr) { return cs.getType(expr); });
3639+
3640+
assert(!type->hasError());
3641+
3642+
if (!type->isEqual(toType))
3643+
return subExpr;
3644+
3645+
return cs.cacheType(new (tc.Context)
3646+
TypeExpr(expr->getCastTypeLoc()));
3647+
});
3648+
}
3649+
3650+
literalInit->setImplicit(false);
3651+
return literalInit;
3652+
}
3653+
36243654
// Turn the subexpression into an rvalue.
36253655
auto rvalueSub = cs.coerceToRValue(expr->getSubExpr());
36263656
expr->setSubExpr(rvalueSub);

lib/Sema/CSDiag.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2814,7 +2814,10 @@ static bool tryIntegerCastFixIts(InFlightDiagnostic &diag, ConstraintSystem &CS,
28142814
if (!isIntegerType(fromType, CS) || !isIntegerType(toType, CS))
28152815
return false;
28162816

2817-
auto getInnerCastedExpr = [&]() -> Expr* {
2817+
auto getInnerCastedExpr = [&]() -> Expr * {
2818+
if (auto *CE = dyn_cast<CoerceExpr>(expr))
2819+
return CE->getSubExpr();
2820+
28182821
auto *CE = dyn_cast<CallExpr>(expr);
28192822
if (!CE)
28202823
return nullptr;

lib/Sema/CSGen.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,11 +1394,12 @@ namespace {
13941394
Type visitTypeExpr(TypeExpr *E) {
13951395
Type type;
13961396
// If this is an implicit TypeExpr, don't validate its contents.
1397-
if (auto *rep = E->getTypeRepr()) {
1398-
type = resolveTypeReferenceInExpression(rep);
1399-
} else {
1397+
if (E->getTypeLoc().wasValidated()) {
14001398
type = E->getTypeLoc().getType();
1399+
} else if (auto *rep = E->getTypeRepr()) {
1400+
type = resolveTypeReferenceInExpression(rep);
14011401
}
1402+
14021403
if (!type || type->hasError()) return Type();
14031404

14041405
auto locator = CS.getConstraintLocator(E);

lib/Sema/CSSolver.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1004,7 +1004,7 @@ void ConstraintSystem::shrink(Expr *expr) {
10041004

10051005
// Counts the number of overload sets present in the tree so far.
10061006
// Note that the traversal is depth-first.
1007-
llvm::SmallVector<std::pair<ApplyExpr *, unsigned>, 4> ApplyExprs;
1007+
llvm::SmallVector<std::pair<Expr *, unsigned>, 4> ApplyExprs;
10081008

10091009
// A collection of original domains of all of the expressions,
10101010
// so they can be restored in case of failure.
@@ -1031,6 +1031,8 @@ void ConstraintSystem::shrink(Expr *expr) {
10311031
}
10321032

10331033
if (auto coerceExpr = dyn_cast<CoerceExpr>(expr)) {
1034+
if (coerceExpr->isLiteralInit())
1035+
ApplyExprs.push_back({coerceExpr, 1});
10341036
visitCoerceExpr(coerceExpr);
10351037
return {false, expr};
10361038
}

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,10 @@ namespace {
974974
/// Simplify a key path expression into a canonical form.
975975
void resolveKeyPathExpr(KeyPathExpr *KPE);
976976

977+
/// Simplify constructs like `UInt32(1)` into `1 as UInt32` if
978+
/// the type conforms to the expected literal protocol.
979+
Expr *simplifyTypeConstructionWithLiteralArg(Expr *E);
980+
977981
public:
978982
PreCheckExpression(TypeChecker &tc, DeclContext *dc, Expr *parent)
979983
: TC(tc), DC(dc), ParentExpr(parent) {}
@@ -1217,6 +1221,9 @@ namespace {
12171221
return KPE;
12181222
}
12191223

1224+
if (auto *simplified = simplifyTypeConstructionWithLiteralArg(expr))
1225+
return simplified;
1226+
12201227
return expr;
12211228
}
12221229

@@ -1824,6 +1831,66 @@ void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) {
18241831
KPE->resolveComponents(TC.Context, components);
18251832
}
18261833

1834+
Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) {
1835+
// If constructor call is expected to produce an optional let's not attempt
1836+
// this optimization because literal initializers aren't failable.
1837+
if (!TC.getLangOpts().isSwiftVersionAtLeast(5)) {
1838+
if (!ExprStack.empty()) {
1839+
auto *parent = ExprStack.back();
1840+
if (isa<BindOptionalExpr>(parent) || isa<ForceValueExpr>(parent))
1841+
return nullptr;
1842+
}
1843+
}
1844+
1845+
auto *call = dyn_cast<CallExpr>(E);
1846+
if (!call || call->getNumArguments() != 1)
1847+
return nullptr;
1848+
1849+
auto *typeExpr = dyn_cast<TypeExpr>(call->getFn());
1850+
if (!typeExpr)
1851+
return nullptr;
1852+
1853+
auto *argExpr = call->getArg()->getSemanticsProvidingExpr();
1854+
auto *literal = dyn_cast<LiteralExpr>(argExpr);
1855+
if (!literal)
1856+
return nullptr;
1857+
1858+
auto *protocol = TC.getLiteralProtocol(literal);
1859+
if (!protocol)
1860+
return nullptr;
1861+
1862+
Type type;
1863+
if (typeExpr->getTypeLoc().wasValidated()) {
1864+
type = typeExpr->getTypeLoc().getType();
1865+
} else if (auto *rep = typeExpr->getTypeRepr()) {
1866+
TypeResolutionOptions options;
1867+
options |= TypeResolutionFlags::AllowUnboundGenerics;
1868+
options |= TypeResolutionFlags::InExpression;
1869+
type = TC.resolveType(rep, DC, options);
1870+
typeExpr->getTypeLoc().setType(type);
1871+
}
1872+
1873+
if (!type)
1874+
return nullptr;
1875+
1876+
// Don't bother to convert deprecated selector syntax.
1877+
if (auto selectorTy = TC.getObjCSelectorType(DC)) {
1878+
if (type->isEqual(selectorTy))
1879+
return nullptr;
1880+
}
1881+
1882+
ConformanceCheckOptions options;
1883+
options |= ConformanceCheckFlags::InExpression;
1884+
options |= ConformanceCheckFlags::SkipConditionalRequirements;
1885+
1886+
auto result = TC.conformsToProtocol(type, protocol, DC, options);
1887+
if (!result || !result->isConcrete())
1888+
return nullptr;
1889+
1890+
return CoerceExpr::forLiteralInit(TC.Context, argExpr, call->getSourceRange(),
1891+
typeExpr->getTypeLoc());
1892+
}
1893+
18271894
/// \brief Clean up the given ill-formed expression, removing any references
18281895
/// to type variables and setting error types on erroneous expression nodes.
18291896
void CleanupIllFormedExpressionRAII::doIt(Expr *expr) {

stdlib/public/core/Integers.swift.gyb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3592,10 +3592,12 @@ ${assignmentOperatorComment(x.operator, True)}
35923592
/// to find an absolute value. In addition, because `abs(_:)` always returns
35933593
/// a value of the same type, even in a generic context, using the function
35943594
/// instead of the `magnitude` property is encouraged.
3595-
@_transparent
35963595
public var magnitude: U${Self} {
3597-
let base = U${Self}(_value)
3598-
return self < (0 as ${Self}) ? ~base + 1 : base
3596+
@inline(__always)
3597+
get {
3598+
let base = U${Self}(_value)
3599+
return self < (0 as ${Self}) ? ~base + 1 : base
3600+
}
35993601
}
36003602
% end
36013603

test/Constraints/diagnostics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -951,7 +951,7 @@ func SR_6272_a() {
951951
case bar
952952
}
953953

954-
// expected-error@+2 {{binary operator '*' cannot be applied to operands of type 'Int' and 'Float'}} {{35-41=}} {{42-43=}}
954+
// expected-error@+2 {{binary operator '*' cannot be applied to operands of type 'Int' and 'Float'}} {{35-35=Int(}} {{42-42=)}}
955955
// expected-note@+1 {{expected an argument list of type '(Int, Int)'}}
956956
let _: Int = Foo.bar.rawValue * Float(0)
957957

test/Constraints/optional.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,3 +272,23 @@ class Bar {
272272
func rdar37508855(_ e1: X?, _ e2: X?) -> [X] {
273273
return [e1, e2].filter { $0 == nil }.map { $0! }
274274
}
275+
276+
func se0213() {
277+
struct Q: ExpressibleByStringLiteral {
278+
typealias StringLiteralType = String
279+
280+
var foo: String
281+
282+
init?(_ possibleQ: StringLiteralType) {
283+
return nil
284+
}
285+
286+
init(stringLiteral str: StringLiteralType) {
287+
self.foo = str
288+
}
289+
}
290+
291+
_ = Q("why")?.foo // Ok
292+
_ = Q("who")!.foo // Ok
293+
_ = Q?("how") // Ok
294+
}

test/SILOptimizer/diagnostic_constant_propagation-swift4.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@
44
// These are specific to Swift 4.
55

66
func testArithmeticOverflowSwift4() {
7-
var _ = Int8(126) + (1 + 1) // FIXME: Should expect an integer overflow
8-
// error but none happens now (see <rdar://problem/39120081>)
7+
var _ = Int8(126) + (1 + 1) // expected-error {{arithmetic operation '126 + 2' (on type 'Int8') results in an overflow}}
98
}

test/SILOptimizer/diagnostic_constant_propagation_floats_x86.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,5 @@ func testIntToFloatConversion() {
109109
let e2: Float80 = 18_446_744_073_709_551_617 // expected-warning {{'18446744073709551617' is not exactly representable as 'Float80'; it becomes '18446744073709551616'}}
110110
_blackHole(e2)
111111

112-
// No warnings are emitted for conversion through explicit constructor calls.
113-
// Note that the error here is because of an implicit conversion of the input
114-
// literal to 'Int'.
115-
_blackHole(Float80(18_446_744_073_709_551_617)) // expected-error {{integer literal '18446744073709551617' overflows when stored into 'Int'}}
112+
_blackHole(Float80(18_446_744_073_709_551_617)) // Ok
116113
}

test/SILOptimizer/diagnostic_constant_propagation_int_arch32.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,5 @@ func testArithmeticOverflow_UInt_32bit() {
234234
}
235235

236236
func testIntToFloatConversion() {
237-
// No warnings are emitted for conversion through explicit constructor calls.
238-
// Note that the error here is because of an implicit conversion of the input
239-
// literal to 'Int', which is 32 bits in arch32.
240-
_blackHole(Double(9_007_199_254_740_993)) // expected-error {{integer literal '9007199254740993' overflows when stored into 'Int'}}
237+
_blackHole(Double(9_007_199_254_740_993)) // Ok
241238
}

test/expr/expressions.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,3 +910,10 @@ Sr3439: do {
910910
let obj = C();
911911
_ = obj[#column] // Ok.
912912
}
913+
914+
// rdar://problem/23672697 - No way to express literal integers larger than Int without using type ascription
915+
let _: Int64 = 0xFFF_FFFF_FFFF_FFFF
916+
let _: Int64 = Int64(0xFFF_FFFF_FFFF_FFFF)
917+
let _: Int64 = 0xFFF_FFFF_FFFF_FFFF as Int64
918+
let _ = Int64(0xFFF_FFFF_FFFF_FFFF)
919+
let _ = 0xFFF_FFFF_FFFF_FFFF as Int64

validation-test/Sema/type_checker_perf/slow/rdar18699199.swift.gyb renamed to validation-test/Sema/type_checker_perf/fast/rdar18699199.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 incrementScopeCounter %s
1+
// RUN: %scale-test --begin 1 --end 10 --step 1 --select incrementScopeCounter %s
22
// REQUIRES: OS=macosx
33
// REQUIRES: asserts
44
public enum E

0 commit comments

Comments
 (0)