Skip to content

[WIP] Implement dynamically callable types (@dynamicCallable). #16980

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

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
adb7648
Implement dynamically callable types (`@dynamicCallable`).
dan-zheng Jun 4, 2018
40e3fb9
Remove SWIFT_ENABLE_TENSORFLOW comments.
dan-zheng Jun 4, 2018
c4c773b
Add @dynamicCallable SIL gen test.
dan-zheng Jun 4, 2018
3625710
Add comments to @dynamicCallable SIL gen test.
dan-zheng Jun 4, 2018
f978c30
[@dynamicCallable] Minor clean up.
dan-zheng Jun 4, 2018
e1b36c7
[NFC] Edit formatting/comments for @dynamicCallable.
dan-zheng Jun 13, 2018
3cd4265
Remove "non-generic" requirement from @dynamicCallable diagnostic.
dan-zheng Jun 14, 2018
a122b40
Merge remote-tracking branch 'origin/master' into dynamic-callable
dan-zheng Jun 28, 2018
8c17ba8
Minor cleanup.
dan-zheng Jun 30, 2018
fe91f19
Add @dynamicCallable tests.
dan-zheng Jun 30, 2018
0044ed2
Add @dynamicCallable tests: protocol constrained/refined archetypes.
dan-zheng Jun 30, 2018
1329ba1
Minor edits.
dan-zheng Jul 15, 2018
6075027
Merge branch 'master' into dynamic-callable
dan-zheng Jul 15, 2018
a6de931
Support @dynamicCallable archetypes.
dan-zheng Jul 15, 2018
4ca99c6
Rename/edit @dynamicCallable SILGen test.
dan-zheng Jul 15, 2018
a982340
[@dynamicCallable] Minor edit.
dan-zheng Jul 18, 2018
adaf500
[@dynamicCallable] Minor edit.
dan-zheng Jul 19, 2018
a525424
Merge branch 'master' into dynamic-callable
dan-zheng Jul 19, 2018
2e39545
Merge branch 'master' into dynamic-callable
dan-zheng Aug 13, 2018
ecb1955
Merge branch 'master' into dynamic-callable
dan-zheng Sep 3, 2018
4438cb8
[WIP] Reimplement @dynamicCallable resolution.
dan-zheng Sep 5, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(optional, Optional,
OnConstructor | OnFunc | OnAccessor | OnVar | OnSubscript |
DeclModifier,
5)
// NOTE: 6 is unused
SIMPLE_DECL_ATTR(dynamicCallable, DynamicCallable,
OnNominalType,
6)
SIMPLE_DECL_ATTR(noreturn, NoReturn,
OnFunc | OnAccessor,
7)
Expand Down
12 changes: 12 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,18 @@ NOTE(archetype_declared_in_type,none,
NOTE(unbound_generic_parameter_explicit_fix,none,
"explicitly specify the generic arguments to fix this issue", ())

ERROR(invalid_dynamic_callable_type,none,
"@dynamicCallable attribute requires %0 to have either a valid "
"'dynamicallyCall(withArguments:)' method or "
"'dynamicallyCall(withKeywordArguments:)' method", (Type))
ERROR(missing_dynamic_callable_kwargs_method,none,
"@dynamicCallable type %0 cannot be applied with keyword arguments; "
"missing `dynamicCall(withKeywordArguments:)` method",
(Type))
ERROR(ambiguous_dynamic_callable_method,none,
"@dynamicCallable attribute does not support ambiguous method %0",
(DeclName))

ERROR(type_invalid_dml,none,
"@dynamicMemberLookup attribute requires %0 to have a "
"'subscript(dynamicMember:)' member with a string index", (Type))
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/KnownIdentifiers.def
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ IDENTIFIER(decode)
IDENTIFIER(decodeIfPresent)
IDENTIFIER(Decoder)
IDENTIFIER(decoder)
IDENTIFIER(dynamicallyCall)
IDENTIFIER(dynamicMember)
IDENTIFIER(Element)
IDENTIFIER(Encodable)
Expand Down Expand Up @@ -114,6 +115,8 @@ IDENTIFIER(Value)
IDENTIFIER(value)
IDENTIFIER_WITH_NAME(value_, "_value")
IDENTIFIER(with)
IDENTIFIER(withArguments)
IDENTIFIER(withKeywordArguments)

// Kinds of layout constraints
IDENTIFIER_WITH_NAME(UnknownLayout, "_UnknownLayout")
Expand Down
215 changes: 157 additions & 58 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7076,6 +7076,100 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal,
return literal;
}

// Resolve @dynamicCallable applications.
static Expr *finishApplyDynamicCallable(ConstraintSystem &cs, ApplyExpr *apply,
ConstraintLocatorBuilder locator) {
auto fn = apply->getFn();

auto isDynamicCallable = [&](Expr *base) {
auto type = cs.getType(base);
return cs.DynamicCallableCache.count(type->getCanonicalType());
};
assert(isDynamicCallable(fn) && "Expected a valid @dynamicCallable type");

auto &ctx = cs.getASTContext();
auto methods = cs.DynamicCallableCache[cs.getType(fn)->getCanonicalType()];
assert(methods.isValid() && "Expected a valid @dynamicCallable type");

TupleExpr *arg = dyn_cast<TupleExpr>(apply->getArg());
if (auto parenExpr = dyn_cast<ParenExpr>(apply->getArg())) {
arg = TupleExpr::createImplicit(ctx, parenExpr->getSubExpr(), {});
}

// Determine whether to call the positional arguments method or the
// keyword arguments method.
/*
bool useKwargsMethod = methods.argumentsMethod == nullptr;
if (!useKwargsMethod) {
for (auto name : arg->getElementNames()) {
if (!name.empty()) {
useKwargsMethod = true;
break;
}
}
}
*/
bool useKwargsMethod = methods.argumentsMethods.empty();
if (!useKwargsMethod) {
for (auto name : arg->getElementNames()) {
if (!name.empty()) {
useKwargsMethod = true;
break;
}
}
}

auto method = useKwargsMethod
? *methods.keywordArgumentsMethods.begin()
: *methods.argumentsMethods.begin();
assert(method && "Dynamic call method should exist");

auto memberType =
cs.getTypeOfMemberReference(cs.getType(fn), method, cs.DC,
/*isDynamicResult*/ false,
FunctionRefKind::DoubleApply,
locator).second;
auto methodType = memberType->castTo<AnyFunctionType>();

// Construct expression referencing the `dynamicallyCall` method.
Expr *member =
new (ctx) MemberRefExpr(fn, fn->getEndLoc(), ConcreteDeclRef(method),
DeclNameLoc(method->getNameLoc()),
/*Implicit*/ true);

// Construct argument to the method (either an array or dictionary
// expression).
Expr *argument = nullptr;
if (!useKwargsMethod) {
argument = ArrayExpr::create(ctx, SourceLoc(), arg->getElements(),
{}, SourceLoc());
} else {
SmallVector<Identifier, 4> names;
SmallVector<Expr *, 4> dictElements;
for (unsigned i = 0, n = arg->getNumElements(); i < n; i++) {
Expr *labelExpr =
new (ctx) StringLiteralExpr(arg->getElementName(i).get(),
arg->getElementNameLoc(i),
/*Implicit*/ true);
Expr *pair =
TupleExpr::createImplicit(ctx, { labelExpr, arg->getElement(i) },
{});
dictElements.push_back(pair);
}
argument = DictionaryExpr::create(ctx, SourceLoc(), dictElements, {},
SourceLoc());
}
argument->setImplicit();

// Construct call to the `dynamicallyCall` method.
auto argumentName = methodType->getParams()[0].getLabel();
Expr *result = CallExpr::createImplicit(ctx, member, argument,
{ argumentName });
cs.TC.typeCheckExpression(result, cs.DC);
cs.cacheExprTypes(result);
return result;
}

Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
ConstraintLocatorBuilder locator) {
TypeChecker &tc = cs.getTypeChecker();
Expand Down Expand Up @@ -7367,67 +7461,72 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
}

// We have a type constructor.
auto metaTy = cs.getType(fn)->castTo<AnyMetatypeType>();
auto ty = metaTy->getInstanceType();

if (!cs.isTypeReference(fn)) {
bool isExistentialType = false;
// If this is an attempt to initialize existential type.
if (auto metaType = cs.getType(fn)->getAs<MetatypeType>()) {
auto instanceType = metaType->getInstanceType();
isExistentialType = instanceType->isExistentialType();
}

if (!isExistentialType) {
// If the metatype value isn't a type expression,
// the user should reference '.init' explicitly, for clarity.
cs.TC
.diagnose(apply->getArg()->getStartLoc(),
diag::missing_init_on_metatype_initialization)
.fixItInsert(apply->getArg()->getStartLoc(), ".init");
}
}

// If we're "constructing" a tuple type, it's simply a conversion.
if (auto tupleTy = ty->getAs<TupleType>()) {
// FIXME: Need an AST to represent this properly.
return coerceToType(apply->getArg(), tupleTy, locator);
}
if (auto metaTy = cs.getType(fn)->getAs<AnyMetatypeType>()) {
auto ty = metaTy->getInstanceType();

if (!cs.isTypeReference(fn)) {
bool isExistentialType = false;
// If this is an attempt to initialize existential type.
if (auto metaType = cs.getType(fn)->getAs<MetatypeType>()) {
auto instanceType = metaType->getInstanceType();
isExistentialType = instanceType->isExistentialType();
}

if (!isExistentialType) {
// If the metatype value isn't a type expression,
// the user should reference '.init' explicitly, for clarity.
cs.TC
.diagnose(apply->getArg()->getStartLoc(),
diag::missing_init_on_metatype_initialization)
.fixItInsert(apply->getArg()->getStartLoc(), ".init");
}
}

// If we're "constructing" a tuple type, it's simply a conversion.
if (auto tupleTy = ty->getAs<TupleType>()) {
// FIXME: Need an AST to represent this properly.
return coerceToType(apply->getArg(), tupleTy, locator);
}

// We're constructing a value of nominal type. Look for the constructor or
// enum element to use.
auto ctorLocator = cs.getConstraintLocator(
locator.withPathElement(ConstraintLocator::ApplyFunction)
.withPathElement(ConstraintLocator::ConstructorMember));
auto selected = solution.getOverloadChoiceIfAvailable(ctorLocator);
if (!selected) {
assert(ty->hasError() || ty->hasUnresolvedType());
cs.setType(apply, ty);
return apply;
}

assert(ty->getNominalOrBoundGenericNominal() || ty->is<DynamicSelfType>() ||
ty->isExistentialType() || ty->is<ArchetypeType>());

// We have the constructor.
auto choice = selected->choice;

// Consider the constructor decl reference expr 'implicit', but the
// constructor call expr itself has the apply's 'implicitness'.
bool isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic;
Expr *declRef = buildMemberRef(fn, selected->openedFullType,
/*dotLoc=*/SourceLoc(), choice,
DeclNameLoc(fn->getEndLoc()),
selected->openedType, locator, ctorLocator,
/*Implicit=*/true, choice.getFunctionRefKind(),
AccessSemantics::Ordinary, isDynamic);
if (!declRef)
return nullptr;
declRef->setImplicit(apply->isImplicit());
apply->setFn(declRef);

// We're constructing a value of nominal type. Look for the constructor or
// enum element to use.
auto ctorLocator = cs.getConstraintLocator(
locator.withPathElement(ConstraintLocator::ApplyFunction)
.withPathElement(ConstraintLocator::ConstructorMember));
auto selected = solution.getOverloadChoiceIfAvailable(ctorLocator);
if (!selected) {
assert(ty->hasError() || ty->hasUnresolvedType());
cs.setType(apply, ty);
return apply;
// Tail-recur to actually call the constructor.
return finishApply(apply, openedType, locator);
}

assert(ty->getNominalOrBoundGenericNominal() || ty->is<DynamicSelfType>() ||
ty->isExistentialType() || ty->is<ArchetypeType>());

// We have the constructor.
auto choice = selected->choice;

// Consider the constructor decl reference expr 'implicit', but the
// constructor call expr itself has the apply's 'implicitness'.
bool isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic;
Expr *declRef = buildMemberRef(fn, selected->openedFullType,
/*dotLoc=*/SourceLoc(), choice,
DeclNameLoc(fn->getEndLoc()),
selected->openedType, locator, ctorLocator,
/*Implicit=*/true, choice.getFunctionRefKind(),
AccessSemantics::Ordinary, isDynamic);
if (!declRef)
return nullptr;
declRef->setImplicit(apply->isImplicit());
apply->setFn(declRef);

// Tail-recur to actually call the constructor.
return finishApply(apply, openedType, locator);
// Handle @dynamicCallable applications.
// At this point, all other AppyExpr cases have been handled.
return finishApplyDynamicCallable(cs, apply, locator);
}


Expand Down
6 changes: 4 additions & 2 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5332,9 +5332,11 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
}

// If we resolved a concrete expression for the callee, and it has
// non-function/non-metatype type, then we cannot call it!
// non-function/non-metatype/non-@dynamicCallable type, then we cannot call
// it!
if (!isUnresolvedOrTypeVarType(fnType) &&
!fnType->is<AnyFunctionType>() && !fnType->is<MetatypeType>()) {
!fnType->is<AnyFunctionType>() && !fnType->is<MetatypeType>()
&& !CS.DynamicCallableCache[fnType->getCanonicalType()].isValid()) {

auto arg = callExpr->getArg();

Expand Down
Loading