|
32 | 32 | using namespace swift;
|
33 | 33 | using namespace swift::constraints;
|
34 | 34 |
|
| 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 | + |
35 | 241 | /// \brief Find the declaration directly referenced by this expression.
|
36 | 242 | static std::pair<ValueDecl *, FunctionRefKind>
|
37 | 243 | findReferencedDecl(Expr *expr, DeclNameLoc &loc) {
|
|
0 commit comments