@@ -122,6 +122,10 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
122
122
return F.getModule ().getOptions ().EnableSILOwnership ;
123
123
}
124
124
125
+ bool areCOWExistentialsEnabled () const {
126
+ return F.getModule ().getOptions ().UseCOWExistentials ;
127
+ }
128
+
125
129
void _require (bool condition, const Twine &complaint,
126
130
const std::function<void ()> &extraContext = nullptr) {
127
131
if (condition) return ;
@@ -2178,6 +2182,90 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
2178
2182
require (OpenedArchetypes.getOpenedArchetypeDef (archetype) == OEI,
2179
2183
" Archetype opened by open_existential_addr should be registered in "
2180
2184
" SILFunction" );
2185
+
2186
+ if (!areCOWExistentialsEnabled ())
2187
+ return ;
2188
+
2189
+ // Check all the uses. Consuming or mutating uses must have mutable access
2190
+ // to the opened value.
2191
+ auto allowedAccessKind = OEI->getAccessKind ();
2192
+ if (allowedAccessKind == OpenedExistentialAccess::Mutable)
2193
+ return ;
2194
+
2195
+ auto isConsumingOrMutatingApplyUse = [](Operand *use) -> bool {
2196
+ ApplySite apply (use->getUser ());
2197
+ assert (apply && " Not an apply instruction kind" );
2198
+ auto conv = apply.getArgumentConvention (use->getOperandNumber () - 1 );
2199
+ switch (conv) {
2200
+ case SILArgumentConvention::Indirect_In_Guaranteed:
2201
+ return false ;
2202
+
2203
+ case SILArgumentConvention::Indirect_Out:
2204
+ case SILArgumentConvention::Indirect_In:
2205
+ case SILArgumentConvention::Indirect_Inout:
2206
+ case SILArgumentConvention::Indirect_InoutAliasable:
2207
+ return true ;
2208
+
2209
+ case SILArgumentConvention::Direct_Unowned:
2210
+ case SILArgumentConvention::Direct_Guaranteed:
2211
+ case SILArgumentConvention::Direct_Owned:
2212
+ case SILArgumentConvention::Direct_Deallocating:
2213
+ assert (conv.isIndirectConvention () && " Expect an indirect convention" );
2214
+ return true ; // return something "conservative".
2215
+ }
2216
+ llvm_unreachable (" covered switch isn't covered?!" );
2217
+ };
2218
+
2219
+ // A "copy_addr %src [take] to *" is consuming on "%src".
2220
+ // A "copy_addr * to * %dst" is mutating on "%dst".
2221
+ auto isConsumingOrMutatingCopyAddrUse = [](Operand *use) -> bool {
2222
+ auto *copyAddr = cast<CopyAddrInst>(use->getUser ());
2223
+ if (copyAddr->getDest () == use->get ())
2224
+ return true ;
2225
+ if (copyAddr->getSrc () == use->get () && copyAddr->isTakeOfSrc () == IsTake)
2226
+ return true ;
2227
+ return false ;
2228
+ };
2229
+
2230
+ auto isMutatingOrConsuming = [=](OpenExistentialAddrInst *OEI) -> bool {
2231
+ for (auto *use : OEI->getUses ()) {
2232
+ auto *inst = use->getUser ();
2233
+ if (inst->isTypeDependentOperand (*use))
2234
+ continue ;
2235
+ switch (inst->getKind ()) {
2236
+ case ValueKind::ApplyInst:
2237
+ case ValueKind::TryApplyInst:
2238
+ case ValueKind::PartialApplyInst:
2239
+ if (isConsumingOrMutatingApplyUse (use))
2240
+ return true ;
2241
+ else
2242
+ break ;
2243
+ case ValueKind::CopyAddrInst:
2244
+ if (isConsumingOrMutatingCopyAddrUse (use))
2245
+ return true ;
2246
+ else
2247
+ break ;
2248
+ case ValueKind::DestroyAddrInst:
2249
+ return true ;
2250
+ case ValueKind::UncheckedAddrCastInst:
2251
+ // Escaping use lets be conservative here.
2252
+ return true ;
2253
+ case ValueKind::CheckedCastAddrBranchInst:
2254
+ if (cast<CheckedCastAddrBranchInst>(inst)->getConsumptionKind () !=
2255
+ CastConsumptionKind::CopyOnSuccess)
2256
+ return true ;
2257
+ break ;
2258
+ default :
2259
+ assert (false && " Unhandled unexpected instruction" );
2260
+ break ;
2261
+ }
2262
+ }
2263
+ return false ;
2264
+ };
2265
+ require (!isMutatingOrConsuming (OEI) ||
2266
+ allowedAccessKind == OpenedExistentialAccess::Mutable,
2267
+ " open_existential_addr uses that consumes or mutates but is not "
2268
+ " opened for mutation" );
2181
2269
}
2182
2270
2183
2271
void checkOpenExistentialRefInst (OpenExistentialRefInst *OEI) {
0 commit comments