Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

☣️[Sema] Transform initializers into coercions for conforming numeric literals #15311

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
54 changes: 54 additions & 0 deletions lib/Sema/TypeCheckConstraints.cpp
Expand Up @@ -787,6 +787,10 @@ namespace {
/// Simplify a key path expression into a canonical form.
void resolveKeyPathExpr(KeyPathExpr *KPE);

/// Simplify constructs like `UInt32(1)` into `1 as UInt32` if
/// the type conforms to the expected literal protocol.
Expr *simplifyInitWithLiteral(Expr *E);

public:
PreCheckExpression(TypeChecker &tc, DeclContext *dc) : TC(tc), DC(dc) { }

Expand Down Expand Up @@ -989,6 +993,9 @@ namespace {
return KPE;
}

if (auto *simplified = simplifyInitWithLiteral(expr))
return simplified;

return expr;
}

Expand Down Expand Up @@ -1547,6 +1554,53 @@ void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) {
KPE->resolveComponents(TC.Context, components);
}

Expr *PreCheckExpression::simplifyInitWithLiteral(Expr *E) {
auto *call = dyn_cast<CallExpr>(E);
if (!call || call->getNumArguments() != 1)
return nullptr;

auto *typeExpr = dyn_cast<TypeExpr>(call->getFn());
if (!typeExpr)
return nullptr;

auto *argExpr = call->getArg()->getSemanticsProvidingExpr();
auto *number = dyn_cast<NumberLiteralExpr>(argExpr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this apply to all literal kinds? For example, Set([1, 2, 3])?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I just tried with NumberLiteralExpr because I ran into problem with failable initializer in swiftpm clashing with ExpressibleByStringLiteral but still wanted to get some performance numbers, and NumberLiteralExpr since to be the biggest pain point for stdlib folks anyway.

if (!number)
return nullptr;

auto *protocol = TC.getLiteralProtocol(number);
if (!protocol)
return nullptr;

Type type;
if (auto *rep = typeExpr->getTypeRepr()) {
TypeResolutionOptions options;
options |= TypeResolutionFlags::AllowUnboundGenerics;
options |= TypeResolutionFlags::InExpression;
type = TC.resolveType(rep, DC, options);
} else {
type = typeExpr->getTypeLoc().getType();
}

if (!type)
return nullptr;

ConformanceCheckOptions options;
options |= ConformanceCheckFlags::InExpression;
options |= ConformanceCheckFlags::SuppressDependencyTracking;
options |= ConformanceCheckFlags::SkipConditionalRequirements;

auto result = TC.conformsToProtocol(type, protocol, DC, options);
if (result) {
auto *expr =
new (TC.Context) CoerceExpr(argExpr, {}, typeExpr->getTypeRepr());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're losing a bunch of the source information in doing this. Should we try to make this a constraint-system change and have CSApply fix up the result, rather than turning it into a CoerceExpr?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's on my agenda to do next.

expr->setImplicit();
return expr;
}

return nullptr;
}

/// \brief Clean up the given ill-formed expression, removing any references
/// to type variables and setting error types on erroneous expression nodes.
void CleanupIllFormedExpressionRAII::doIt(Expr *expr, ASTContext &Context) {
Expand Down