@@ -56,44 +56,40 @@ static bool hasThrowingFunctionClosureParameter(CanType type) {
56
56
ProtocolRethrowsRequirementList
57
57
ProtocolRethrowsRequirementsRequest::evaluate (Evaluator &evaluator,
58
58
ProtocolDecl *decl) const {
59
- SmallVector<std::pair<Type, ValueDecl*>, 2 > found;
60
- llvm::DenseSet<ProtocolDecl*> checkedProtocols;
61
-
62
59
ASTContext &ctx = decl->getASTContext ();
63
60
64
61
// only allow rethrowing requirements to be determined from marked protocols
65
- if (!decl->getAttrs (). hasAttribute <swift::AtRethrowsAttr> ()) {
66
- return ProtocolRethrowsRequirementList (ctx. AllocateCopy (found) );
62
+ if (!decl->isRethrowingProtocol ()) {
63
+ return ProtocolRethrowsRequirementList ();
67
64
}
68
65
66
+ SmallVector<AbstractFunctionDecl *, 2 > requirements;
67
+ SmallVector<std::pair<Type, ProtocolDecl *>, 2 > conformances;
68
+
69
69
// check if immediate members of protocol are 'throws'
70
70
for (auto member : decl->getMembers ()) {
71
71
auto fnDecl = dyn_cast<AbstractFunctionDecl>(member);
72
72
if (!fnDecl || !fnDecl->hasThrows ())
73
73
continue ;
74
74
75
- found.push_back (
76
- std::pair<Type, ValueDecl*>(decl->getSelfInterfaceType (), fnDecl));
75
+ requirements.push_back (fnDecl);
77
76
}
78
- checkedProtocols.insert (decl);
79
77
80
78
// check associated conformances of associated types or inheritance
81
79
for (auto requirement : decl->getRequirementSignature ()) {
82
- if (requirement.getKind () != RequirementKind::Conformance) {
80
+ if (requirement.getKind () != RequirementKind::Conformance)
83
81
continue ;
84
- }
82
+
85
83
auto protoTy = requirement.getSecondType ()->castTo <ProtocolType>();
86
- auto proto = protoTy->getDecl ();
87
- if (checkedProtocols. count (proto) != 0 ) {
84
+ auto protoDecl = protoTy->getDecl ();
85
+ if (!protoDecl-> isRethrowingProtocol ())
88
86
continue ;
89
- }
90
- checkedProtocols.insert (proto);
91
- for (auto entry : proto->getRethrowingRequirements ()) {
92
- found.emplace_back (requirement.getFirstType (), entry.second );
93
- }
87
+
88
+ conformances.emplace_back (requirement.getFirstType (), protoDecl);
94
89
}
95
90
96
- return ProtocolRethrowsRequirementList (ctx.AllocateCopy (found));
91
+ return ProtocolRethrowsRequirementList (ctx.AllocateCopy (requirements),
92
+ ctx.AllocateCopy (conformances));
97
93
}
98
94
99
95
FunctionRethrowingKind
@@ -135,68 +131,90 @@ FunctionRethrowingKindRequest::evaluate(Evaluator &evaluator,
135
131
return FunctionRethrowingKind::None;
136
132
}
137
133
138
- static bool classifyRequirement (ModuleDecl *module ,
139
- ProtocolConformance *reqConformance,
140
- ValueDecl *requiredFn) {
141
- auto declRef = reqConformance->getWitnessDeclRef (requiredFn);
142
- auto witnessDecl = cast<AbstractFunctionDecl>(declRef.getDecl ());
134
+ static bool classifyWitness (ModuleDecl *module ,
135
+ ProtocolConformance *conformance,
136
+ AbstractFunctionDecl *req) {
137
+ auto declRef = conformance->getWitnessDeclRef (req);
138
+ if (!declRef) {
139
+ // Invalid conformance.
140
+ return true ;
141
+ }
142
+
143
+ auto witnessDecl = dyn_cast<AbstractFunctionDecl>(declRef.getDecl ());
144
+ if (!witnessDecl) {
145
+ // Enum element constructors never throw.
146
+ assert (isa<EnumElementDecl>(declRef.getDecl ()));
147
+ return false ;
148
+ }
149
+
143
150
switch (witnessDecl->getRethrowingKind ()) {
151
+ case FunctionRethrowingKind::None:
152
+ // Witness doesn't throw at all, so it contributes nothing.
153
+ return false ;
154
+
144
155
case FunctionRethrowingKind::ByConformance: {
145
- auto substitutions = reqConformance->getSubstitutions (module );
156
+ // Witness throws if the concrete type's @rethrows conformances
157
+ // recursively throw.
158
+ auto substitutions = conformance->getSubstitutions (module );
146
159
for (auto conformanceRef : substitutions.getConformances ()) {
147
160
if (conformanceRef.classifyAsThrows ()) {
148
161
return true ;
149
162
}
150
163
}
151
- break ;
164
+ return false ;
152
165
}
153
- case FunctionRethrowingKind::None:
154
- break ;
166
+
167
+ case FunctionRethrowingKind::ByClosure:
168
+ // Witness only throws if a closure argument throws, so it
169
+ // contributes nothng.
170
+ return false ;
171
+
155
172
case FunctionRethrowingKind::Throws:
173
+ // Witness always throws.
156
174
return true ;
157
- default :
175
+
176
+ case FunctionRethrowingKind::Invalid:
177
+ // If the code is invalid, just assume it throws.
158
178
return true ;
159
179
}
160
- return false ;
161
180
}
162
181
163
- // classify the type requirements of a given protocol type with a function
164
- // requirement as throws or not. This will detect if the signature of the
165
- // function is throwing or not depending on associated types.
166
- static bool classifyTypeRequirement (ModuleDecl *module , Type protoType,
167
- ValueDecl *requiredFn,
168
- ProtocolConformance *conformance,
169
- ProtocolDecl *requiredProtocol) {
170
- auto reqProtocol = cast<ProtocolDecl>(requiredFn->getDeclContext ());
171
- ProtocolConformance *reqConformance;
172
-
173
- if (protoType->isEqual (reqProtocol->getSelfInterfaceType ()) &&
174
- requiredProtocol == reqProtocol) {
175
- reqConformance = conformance;
176
- } else {
177
- auto reqConformanceRef =
178
- conformance->getAssociatedConformance (protoType, reqProtocol);
179
- if (!reqConformanceRef.isConcrete ()) {
180
- return true ;
182
+ bool
183
+ ProtocolConformanceClassifyAsThrowsRequest::evaluate (
184
+ Evaluator &evaluator, ProtocolConformance *conformance) const {
185
+ auto *module = conformance->getDeclContext ()->getParentModule ();
186
+
187
+ llvm::SmallDenseSet<ProtocolConformance *, 2 > visited;
188
+ SmallVector<ProtocolConformance *, 2 > worklist;
189
+
190
+ worklist.push_back (conformance);
191
+
192
+ while (!worklist.empty ()) {
193
+ auto *current = worklist.back ();
194
+ worklist.pop_back ();
195
+
196
+ if (!visited.insert (current).second )
197
+ continue ;
198
+
199
+ auto protoDecl = current->getProtocol ();
200
+
201
+ auto list = protoDecl->getRethrowingRequirements ();
202
+ for (auto req : list.getRequirements ()) {
203
+ if (classifyWitness (module , current, req))
204
+ return true ;
181
205
}
182
- reqConformance = reqConformanceRef.getConcrete ();
183
- }
184
206
185
- return classifyRequirement (module , reqConformance, requiredFn);
186
- }
207
+ for (auto pair : list.getConformances ()) {
208
+ auto assocConf =
209
+ current->getAssociatedConformance (
210
+ pair.first , pair.second );
211
+ if (!assocConf.isConcrete ())
212
+ return true ;
187
213
188
- bool
189
- ProtocolConformanceRefClassifyAsThrowsRequest::evaluate (
190
- Evaluator &evaluator, ProtocolConformanceRef conformanceRef) const {
191
- auto conformance = conformanceRef.getConcrete ();
192
- auto requiredProtocol = conformanceRef.getRequirement ();
193
- auto module = requiredProtocol->getModuleContext ();
194
- for (auto req : requiredProtocol->getRethrowingRequirements ()) {
195
- if (classifyTypeRequirement (module , req.first , req.second ,
196
- conformance, requiredProtocol)) {
197
- return true ;
214
+ worklist.push_back (assocConf.getConcrete ());
198
215
}
199
216
}
217
+
200
218
return false ;
201
219
}
202
220
0 commit comments