Skip to content

Commit b5c091a

Browse files
committed
[WIP] Reimplement @dynamicCallable resolution.
Reimplement dynamic call resolution using a disjunction constraint of `ApplicableFunction` constraints.
1 parent ecb1955 commit b5c091a

File tree

7 files changed

+516
-40
lines changed

7 files changed

+516
-40
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ namespace swift {
155155
///
156156
/// This option enables verbose debugging output from the constraint
157157
/// solver.
158-
bool DebugConstraintSolver = false;
158+
bool DebugConstraintSolver = true;
159159

160160
/// \brief Specific solution attempt for which the constraint
161161
/// solver should be debugged.

lib/Sema/CSApply.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7119,6 +7119,9 @@ static Expr *finishApplyDynamicCallable(ConstraintSystem &cs, ApplyExpr *apply,
71197119
}
71207120
}
71217121

7122+
llvm::errs() << "CS APPLY\n";
7123+
cs.getType(fn)->dump();
7124+
71227125
auto method = useKwargsMethod
71237126
? *methods.keywordArgumentsMethods.begin()
71247127
: *methods.argumentsMethods.begin();

lib/Sema/CSGen.cpp

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,212 @@
3232
using namespace swift;
3333
using namespace swift::constraints;
3434

35+
// START DYNAMIC CALLABLE
36+
/// Returns the function declaration corresponding to a @dynamicCallable
37+
/// attribute required method (if it exists) implemented by a type. Otherwise,
38+
/// return nullptr.
39+
// static FuncDecl *
40+
static llvm::SmallVector<FuncDecl *, 4>
41+
lookupDynamicCallableMethods(Type type, ConstraintSystem &CS,
42+
const ConstraintLocatorBuilder &locator,
43+
Identifier argumentName, bool hasKeywordArgs,
44+
bool &error) {
45+
auto &ctx = CS.getASTContext();
46+
auto decl = type->getAnyNominal();
47+
auto methodName = DeclName(ctx, ctx.Id_dynamicallyCall, { argumentName });
48+
auto matches = CS.performMemberLookup(ConstraintKind::ValueMember,
49+
methodName, type,
50+
FunctionRefKind::SingleApply,
51+
CS.getConstraintLocator(locator),
52+
/*includeInaccessibleMembers*/ false);
53+
// Filter valid candidates.
54+
auto candidates = matches.ViableCandidates;
55+
auto filter = [&](OverloadChoice choice) {
56+
auto cand = cast<FuncDecl>(choice.getDecl());
57+
return !isValidDynamicCallableMethod(cand, decl, CS.TC, hasKeywordArgs);
58+
};
59+
candidates.erase(std::remove_if(candidates.begin(), candidates.end(), filter),
60+
candidates.end());
61+
62+
SmallVector<FuncDecl *, 4> methods;
63+
for (auto candidate : candidates) {
64+
auto funcDecl = dyn_cast_or_null<FuncDecl>(candidate.getDecl());
65+
if (!funcDecl) continue;
66+
methods.push_back(funcDecl);
67+
}
68+
return methods;
69+
/*
70+
// If there is one candidate, return it. Otherwise, return nullptr.
71+
auto size = candidates.size();
72+
if (size == 1) return cast<FuncDecl>(candidates.front().getDecl());
73+
// If there are >1 candidates, it is an overload error.
74+
else if (size > 1) error = true;
75+
return nullptr;
76+
*/
77+
}
78+
79+
/// Looks up and returns the @dynamicCallable required methods (if they exist)
80+
/// implemented by a type. This function should not be called directly: instead,
81+
/// call `getDynamicCallableMethods` which performs caching.
82+
static DynamicCallableMethods
83+
lookupDynamicCallableMethods(Type type, ConstraintSystem &CS,
84+
const ConstraintLocatorBuilder &locator,
85+
bool &error) {
86+
auto &ctx = CS.getASTContext();
87+
DynamicCallableMethods methods;
88+
// methods.argumentsMethod =
89+
// lookupDynamicCallableMethod(type, CS, locator, ctx.Id_withArguments,
90+
// /*hasKeywordArgs*/ false, error);
91+
// methods.keywordArgumentsMethod =
92+
// lookupDynamicCallableMethod(type, CS, locator, ctx.Id_withKeywordArguments,
93+
// /*hasKeywordArgs*/ true, error);
94+
95+
auto argMethods =
96+
lookupDynamicCallableMethods(type, CS, locator, ctx.Id_withArguments,
97+
/*hasKeywordArgs*/ false, error);
98+
auto kwargMethods =
99+
lookupDynamicCallableMethods(type, CS, locator, ctx.Id_withKeywordArguments,
100+
/*hasKeywordArgs*/ true, error);
101+
// methods.argumentsMethods.insert(argMethods.begin(), argMethods.end());
102+
// methods.keywordArgumentsMethods.insert(kwargMethods.begin(),
103+
// kwargMethods.end());
104+
methods.argumentsMethods.append(argMethods.begin(), argMethods.end());
105+
methods.keywordArgumentsMethods.append(kwargMethods.begin(),
106+
kwargMethods.end());
107+
// methods.addArgumentsMethod(
108+
// lookupDynamicCallableMethod(type, CS, locator, ctx.Id_withArguments,
109+
// /*hasKeywordArgs*/ false, error));
110+
// methods.addKeywordArgumentsMethod(
111+
// lookupDynamicCallableMethod(type, CS, locator, ctx.Id_withKeywordArguments,
112+
// /*hasKeywordArgs*/ true, error));
113+
return methods;
114+
}
115+
116+
/// Returns the @dynamicCallable required methods (if they exist) implemented by
117+
/// a type.
118+
/// This function may be slow for deep class hierarchies and multiple protocol
119+
/// conformances, but it is invoked only after other constraint simplification
120+
/// rules fail.
121+
static DynamicCallableMethods
122+
getDynamicCallableMethods(Type type, ConstraintSystem &CS,
123+
const ConstraintLocatorBuilder &locator,
124+
bool &error) {
125+
auto canType = type->getCanonicalType();
126+
auto it = CS.DynamicCallableCache.find(canType);
127+
if (it != CS.DynamicCallableCache.end()) return it->second;
128+
129+
// Calculate @dynamicCallable methods for composite types with multiple
130+
// components (protocol composition types and archetypes).
131+
auto calculateForComponentTypes =
132+
[&](ArrayRef<Type> componentTypes) -> DynamicCallableMethods {
133+
DynamicCallableMethods methods;
134+
for (auto componentType : componentTypes) {
135+
auto tmp = getDynamicCallableMethods(componentType, CS, locator, error);
136+
if (error) return methods;
137+
for (auto method : tmp.argumentsMethods) {
138+
bool foundMethod = false;
139+
for (auto m : methods.argumentsMethods) {
140+
if (method == m) {
141+
foundMethod = true;
142+
break;
143+
}
144+
}
145+
if (!foundMethod)
146+
methods.addArgumentsMethod(method);
147+
// if (!methods.argumentsMethods.count(method))
148+
// methods.addArgumentsMethod(method);
149+
}
150+
for (auto method : tmp.keywordArgumentsMethods) {
151+
bool foundMethod = false;
152+
for (auto m : methods.keywordArgumentsMethods) {
153+
if (method == m) {
154+
foundMethod = true;
155+
break;
156+
}
157+
}
158+
if (!foundMethod)
159+
methods.addKeywordArgumentsMethod(method);
160+
// if (!methods.keywordArgumentsMethods.count(method))
161+
// methods.addKeywordArgumentsMethod(method);
162+
}
163+
/*
164+
if (tmp.argumentsMethod) {
165+
if (methods.argumentsMethod &&
166+
methods.argumentsMethod != tmp.argumentsMethod) {
167+
error = true;
168+
return methods;
169+
}
170+
methods.argumentsMethod = tmp.argumentsMethod;
171+
}
172+
if (tmp.keywordArgumentsMethod) {
173+
if (methods.keywordArgumentsMethod &&
174+
methods.keywordArgumentsMethod != tmp.keywordArgumentsMethod) {
175+
error = true;
176+
return methods;
177+
}
178+
methods.keywordArgumentsMethod = tmp.keywordArgumentsMethod;
179+
}
180+
*/
181+
}
182+
return methods;
183+
};
184+
185+
// Calculate @dynamicCallable methods.
186+
auto calculate = [&]() -> DynamicCallableMethods {
187+
// If this is an archetype type, check if any types it conforms to
188+
// (superclass or protocols) have the attribute.
189+
if (auto archetype = dyn_cast<ArchetypeType>(canType)) {
190+
SmallVector<Type, 2> componentTypes;
191+
for (auto protocolDecl : archetype->getConformsTo())
192+
componentTypes.push_back(protocolDecl->getDeclaredType());
193+
if (auto superclass = archetype->getSuperclass())
194+
componentTypes.push_back(superclass);
195+
return calculateForComponentTypes(componentTypes);
196+
}
197+
198+
// If this is a protocol composition, check if any of its members have the
199+
// attribute.
200+
if (auto protocolComp = dyn_cast<ProtocolCompositionType>(canType))
201+
return calculateForComponentTypes(protocolComp->getMembers());
202+
203+
// Otherwise, this must be a nominal type.
204+
// Dynamic calling doesn't work for tuples, etc.
205+
auto nominal = canType->getAnyNominal();
206+
if (!nominal) return DynamicCallableMethods();
207+
208+
// If this type conforms to a protocol which has the attribute, then
209+
// look up the methods.
210+
for (auto p : nominal->getAllProtocols())
211+
if (p->getAttrs().hasAttribute<DynamicCallableAttr>())
212+
return lookupDynamicCallableMethods(type, CS, locator, error);
213+
214+
// Walk superclasses, if present.
215+
llvm::SmallPtrSet<const NominalTypeDecl*, 8> visitedDecls;
216+
while (1) {
217+
// If we found a circular parent class chain, reject this.
218+
if (!visitedDecls.insert(nominal).second)
219+
return DynamicCallableMethods();
220+
221+
// If this type has the attribute on it, then look up the methods.
222+
if (nominal->getAttrs().hasAttribute<DynamicCallableAttr>())
223+
return lookupDynamicCallableMethods(type, CS, locator, error);
224+
225+
// If this type is a class with a superclass, check superclasses.
226+
if (auto *cd = dyn_cast<ClassDecl>(nominal)) {
227+
if (auto superClass = cd->getSuperclassDecl()) {
228+
nominal = superClass;
229+
continue;
230+
}
231+
}
232+
233+
return DynamicCallableMethods();
234+
}
235+
};
236+
237+
return CS.DynamicCallableCache[canType] = calculate();
238+
}
239+
// END DYNAMIC CALLABLE
240+
35241
/// \brief Find the declaration directly referenced by this expression.
36242
static std::pair<ValueDecl *, FunctionRefKind>
37243
findReferencedDecl(Expr *expr, DeclNameLoc &loc) {

0 commit comments

Comments
 (0)