@@ -410,6 +410,130 @@ void verifyKeyPathComponent(SILModule &M,
410
410
baseTy = componentTy;
411
411
}
412
412
413
+ // / Check if according to the SIL language model this memory /must only/ be used
414
+ // / immutably. Today this is only applied to in_guaranteed arguments and
415
+ // / open_existential_addr. We should expand it as needed.
416
+ struct ImmutableAddressUseVerifier {
417
+ SmallVector<Operand *, 32 > worklist;
418
+
419
+ bool isConsumingOrMutatingApplyUse (Operand *use) {
420
+ ApplySite apply (use->getUser ());
421
+ assert (apply && " Not an apply instruction kind" );
422
+ auto conv = apply.getArgumentConvention (*use);
423
+ switch (conv) {
424
+ case SILArgumentConvention::Indirect_In_Guaranteed:
425
+ return false ;
426
+
427
+ case SILArgumentConvention::Indirect_InoutAliasable:
428
+ // DISCUSSION: We do not consider inout_aliasable to be "truly mutating"
429
+ // since today it is just used as a way to mark a captured argument and
430
+ // not that something truly has mutating semantics. The reason why this
431
+ // is safe is that the typechecker guarantees that if our value was
432
+ // immutable, then the use in the closure must be immutable as well.
433
+ //
434
+ // TODO: Remove this in favor of using Inout and In_Guaranteed.
435
+ return false ;
436
+
437
+ case SILArgumentConvention::Indirect_Out:
438
+ case SILArgumentConvention::Indirect_In:
439
+ case SILArgumentConvention::Indirect_In_Constant:
440
+ case SILArgumentConvention::Indirect_Inout:
441
+ return true ;
442
+
443
+ case SILArgumentConvention::Direct_Unowned:
444
+ case SILArgumentConvention::Direct_Guaranteed:
445
+ case SILArgumentConvention::Direct_Owned:
446
+ case SILArgumentConvention::Direct_Deallocating:
447
+ assert (conv.isIndirectConvention () && " Expect an indirect convention" );
448
+ return true ; // return something "conservative".
449
+ }
450
+ llvm_unreachable (" covered switch isn't covered?!" );
451
+ };
452
+
453
+ // / A "copy_addr %src [take] to *" is consuming on "%src".
454
+ // / A "copy_addr * to * %dst" is mutating on "%dst".
455
+ bool isConsumingOrMutatingCopyAddrUse (Operand *use) {
456
+ auto *copyAddr = cast<CopyAddrInst>(use->getUser ());
457
+ if (copyAddr->getDest () == use->get ())
458
+ return true ;
459
+ if (copyAddr->getSrc () == use->get () && copyAddr->isTakeOfSrc () == IsTake)
460
+ return true ;
461
+ return false ;
462
+ }
463
+
464
+ bool isCastToNonConsuming (UncheckedAddrCastInst *i) {
465
+ for (auto *use : i->getUses ()) {
466
+ auto *inst = use->getUser ();
467
+ switch (inst->getKind ()) {
468
+ case SILInstructionKind::ApplyInst:
469
+ case SILInstructionKind::TryApplyInst:
470
+ case SILInstructionKind::PartialApplyInst:
471
+ if (isConsumingOrMutatingApplyUse (use))
472
+ return false ;
473
+ continue ;
474
+ default :
475
+ continue ;
476
+ }
477
+ }
478
+ return true ;
479
+ }
480
+
481
+ bool isMutatingOrConsuming (SILValue address) {
482
+ for (auto *use : address->getUses ()) {
483
+ auto *inst = use->getUser ();
484
+ if (inst->isTypeDependentOperand (*use))
485
+ continue ;
486
+ switch (inst->getKind ()) {
487
+ case SILInstructionKind::MarkDependenceInst:
488
+ break ;
489
+ case SILInstructionKind::ApplyInst:
490
+ case SILInstructionKind::TryApplyInst:
491
+ case SILInstructionKind::PartialApplyInst:
492
+ if (isConsumingOrMutatingApplyUse (use))
493
+ return true ;
494
+ else
495
+ break ;
496
+ case SILInstructionKind::CopyAddrInst:
497
+ if (isConsumingOrMutatingCopyAddrUse (use))
498
+ return true ;
499
+ else
500
+ break ;
501
+ case SILInstructionKind::DestroyAddrInst:
502
+ return true ;
503
+ case SILInstructionKind::UncheckedAddrCastInst: {
504
+ if (isCastToNonConsuming (cast<UncheckedAddrCastInst>(inst))) {
505
+ break ;
506
+ }
507
+ return true ;
508
+ }
509
+ case SILInstructionKind::CheckedCastAddrBranchInst:
510
+ switch (cast<CheckedCastAddrBranchInst>(inst)->getConsumptionKind ()) {
511
+ case CastConsumptionKind::BorrowAlways:
512
+ llvm_unreachable (" checked_cast_addr_br cannot have BorrowAlways" );
513
+ case CastConsumptionKind::CopyOnSuccess:
514
+ break ;
515
+ case CastConsumptionKind::TakeAlways:
516
+ case CastConsumptionKind::TakeOnSuccess:
517
+ return true ;
518
+ }
519
+ break ;
520
+ case SILInstructionKind::LoadInst:
521
+ // A 'non-taking' value load is harmless.
522
+ return cast<LoadInst>(inst)->getOwnershipQualifier () ==
523
+ LoadOwnershipQualifier::Take;
524
+ break ;
525
+ case SILInstructionKind::DebugValueAddrInst:
526
+ // Harmless use.
527
+ break ;
528
+ default :
529
+ llvm_unreachable (" Unhandled unexpected instruction" );
530
+ break ;
531
+ }
532
+ }
533
+ return false ;
534
+ }
535
+ };
536
+
413
537
// / The SIL verifier walks over a SIL function / basic block / instruction,
414
538
// / checking and enforcing its invariants.
415
539
class SILVerifier : public SILVerifierBase <SILVerifier> {
@@ -2878,123 +3002,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
2878
3002
if (allowedAccessKind == OpenedExistentialAccess::Mutable)
2879
3003
return ;
2880
3004
2881
- auto isConsumingOrMutatingApplyUse = [](Operand *use) -> bool {
2882
- ApplySite apply (use->getUser ());
2883
- assert (apply && " Not an apply instruction kind" );
2884
- auto conv = apply.getArgumentConvention (*use);
2885
- switch (conv) {
2886
- case SILArgumentConvention::Indirect_In_Guaranteed:
2887
- return false ;
2888
-
2889
- case SILArgumentConvention::Indirect_InoutAliasable:
2890
- // DISCUSSION: We do not consider inout_aliasable to be "truly mutating"
2891
- // since today it is just used as a way to mark a captured argument and
2892
- // not that something truly has mutating semantics. The reason why this
2893
- // is safe is that the typechecker guarantees that if our value was
2894
- // immutable, then the use in the closure must be immutable as well.
2895
- //
2896
- // TODO: Remove this in favor of using Inout and In_Guaranteed.
2897
- return false ;
2898
-
2899
- case SILArgumentConvention::Indirect_Out:
2900
- case SILArgumentConvention::Indirect_In:
2901
- case SILArgumentConvention::Indirect_In_Constant:
2902
- case SILArgumentConvention::Indirect_Inout:
2903
- return true ;
2904
-
2905
- case SILArgumentConvention::Direct_Unowned:
2906
- case SILArgumentConvention::Direct_Guaranteed:
2907
- case SILArgumentConvention::Direct_Owned:
2908
- case SILArgumentConvention::Direct_Deallocating:
2909
- assert (conv.isIndirectConvention () && " Expect an indirect convention" );
2910
- return true ; // return something "conservative".
2911
- }
2912
- llvm_unreachable (" covered switch isn't covered?!" );
2913
- };
2914
-
2915
- // A "copy_addr %src [take] to *" is consuming on "%src".
2916
- // A "copy_addr * to * %dst" is mutating on "%dst".
2917
- auto isConsumingOrMutatingCopyAddrUse = [](Operand *use) -> bool {
2918
- auto *copyAddr = cast<CopyAddrInst>(use->getUser ());
2919
- if (copyAddr->getDest () == use->get ())
2920
- return true ;
2921
- if (copyAddr->getSrc () == use->get () && copyAddr->isTakeOfSrc () == IsTake)
2922
- return true ;
2923
- return false ;
2924
- };
2925
-
2926
- auto isMutatingOrConsuming = [=](OpenExistentialAddrInst *OEI) -> bool {
2927
- for (auto *use : OEI->getUses ()) {
2928
- auto *inst = use->getUser ();
2929
- if (inst->isTypeDependentOperand (*use))
2930
- continue ;
2931
- switch (inst->getKind ()) {
2932
- case SILInstructionKind::MarkDependenceInst:
2933
- break ;
2934
- case SILInstructionKind::ApplyInst:
2935
- case SILInstructionKind::TryApplyInst:
2936
- case SILInstructionKind::PartialApplyInst:
2937
- if (isConsumingOrMutatingApplyUse (use))
2938
- return true ;
2939
- else
2940
- break ;
2941
- case SILInstructionKind::CopyAddrInst:
2942
- if (isConsumingOrMutatingCopyAddrUse (use))
2943
- return true ;
2944
- else
2945
- break ;
2946
- case SILInstructionKind::DestroyAddrInst:
2947
- return true ;
2948
- case SILInstructionKind::UncheckedAddrCastInst: {
2949
- auto isCastToNonConsuming = [=](UncheckedAddrCastInst *I) -> bool {
2950
- for (auto *use : I->getUses ()) {
2951
- auto *inst = use->getUser ();
2952
- switch (inst->getKind ()) {
2953
- case SILInstructionKind::ApplyInst:
2954
- case SILInstructionKind::TryApplyInst:
2955
- case SILInstructionKind::PartialApplyInst:
2956
- if (isConsumingOrMutatingApplyUse (use))
2957
- return false ;
2958
- continue ;
2959
- default :
2960
- continue ;
2961
- }
2962
- }
2963
- return true ;
2964
- };
2965
- if (isCastToNonConsuming (cast<UncheckedAddrCastInst>(inst))) {
2966
- break ;
2967
- }
2968
- return true ;
2969
- }
2970
- case SILInstructionKind::CheckedCastAddrBranchInst:
2971
- switch (cast<CheckedCastAddrBranchInst>(inst)->getConsumptionKind ()) {
2972
- case CastConsumptionKind::BorrowAlways:
2973
- llvm_unreachable (" checked_cast_addr_br cannot have BorrowAlways" );
2974
- case CastConsumptionKind::CopyOnSuccess:
2975
- break ;
2976
- case CastConsumptionKind::TakeAlways:
2977
- case CastConsumptionKind::TakeOnSuccess:
2978
- return true ;
2979
- }
2980
- break ;
2981
- case SILInstructionKind::LoadInst:
2982
- // A 'non-taking' value load is harmless.
2983
- return cast<LoadInst>(inst)->getOwnershipQualifier () ==
2984
- LoadOwnershipQualifier::Take;
2985
- break ;
2986
- case SILInstructionKind::DebugValueAddrInst:
2987
- // Harmless use.
2988
- break ;
2989
- default :
2990
- llvm_unreachable (" Unhandled unexpected instruction" );
2991
- break ;
2992
- }
2993
- }
2994
- return false ;
2995
- };
2996
- require (allowedAccessKind == OpenedExistentialAccess::Mutable
2997
- || !isMutatingOrConsuming (OEI),
3005
+ require (allowedAccessKind == OpenedExistentialAccess::Mutable ||
3006
+ !ImmutableAddressUseVerifier ().isMutatingOrConsuming (OEI),
2998
3007
" open_existential_addr uses that consumes or mutates but is not "
2999
3008
" opened for mutation" );
3000
3009
}
0 commit comments