Skip to content

Commit 76e9382

Browse files
committed
[move-only] Make it an error if we attempt to destructure/partially invalidate through a field with a deinit.
If one has a type with a deinit, if one were to partially invalidate the value, the checker will clean up the remaining parts of the value but not the actual underlying value. This then would cause the deinit of the actual type to be called. rdar://101651138
1 parent 6b3a17b commit 76e9382

9 files changed

+851
-362
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,12 @@ ERROR(sil_moveonlychecker_notconsumable_but_assignable_was_consumed_escaping_var
765765
(StringRef))
766766
ERROR(sil_moveonlychecker_let_capture_consumed, none,
767767
"'%0' was consumed but it is illegal to consume a noncopyable immutable capture of an escaping closure. One can only read from it", (StringRef))
768+
ERROR(sil_moveonlychecker_cannot_destructure_deinit_nominal_type_self, none,
769+
"Cannot partially consume '%0' since it has a user defined deinit",
770+
(StringRef))
771+
ERROR(sil_moveonlychecker_cannot_destructure_deinit_nominal_type_field, none,
772+
"Cannot partially consume '%0' since it contains field '%1.%2' whose type %3 has a user defined deinit",
773+
(StringRef, StringRef, StringRef, DeclBaseName))
768774

769775
NOTE(sil_moveonlychecker_moveonly_field_consumed_here, none,
770776
"move only field consumed here", ())
@@ -784,6 +790,8 @@ NOTE(sil_moveonlychecker_nonconsuming_use_here, none,
784790
"non-consuming use here", ())
785791
NOTE(sil_movekillscopyablevalue_value_cyclic_consumed_in_loop_here, none,
786792
"consuming in loop use here", ())
793+
NOTE(sil_moveonlychecker_deinit_here, none,
794+
"deinit declared here", ())
787795

788796
ERROR(sil_moveonlychecker_not_understand_consumable_and_assignable, none,
789797
"Usage of @noImplicitCopy that the move checker does not know how to "

lib/SILOptimizer/Mandatory/CMakeLists.txt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,18 @@ target_sources(swiftSILOptimizer PRIVATE
2323
LexicalLifetimeEliminator.cpp
2424
LowerHopToActor.cpp
2525
MandatoryInlining.cpp
26-
MovedAsyncVarDebugInfoPropagator.cpp
27-
MoveOnlyAddressCheckerUtils.cpp
2826
MoveOnlyAddressCheckerTester.cpp
29-
MoveOnlyBorrowToDestructureUtils.cpp
27+
MoveOnlyAddressCheckerUtils.cpp
3028
MoveOnlyBorrowToDestructureTester.cpp
29+
MoveOnlyBorrowToDestructureUtils.cpp
30+
MoveOnlyChecker.cpp
3131
MoveOnlyDeinitInsertion.cpp
3232
MoveOnlyDiagnostics.cpp
33-
MoveOnlyObjectCheckerUtils.cpp
3433
MoveOnlyObjectCheckerTester.cpp
35-
MoveOnlyChecker.cpp
34+
MoveOnlyObjectCheckerUtils.cpp
35+
MoveOnlyTypeUtils.cpp
3636
MoveOnlyUtils.cpp
37+
MovedAsyncVarDebugInfoPropagator.cpp
3738
NestedSemanticFunctionCheck.cpp
3839
OptimizeHopToExecutor.cpp
3940
PerformanceDiagnostics.cpp

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

Lines changed: 255 additions & 127 deletions
Large diffs are not rendered by default.

lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureUtils.cpp

Lines changed: 1 addition & 230 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "MoveOnlyBorrowToDestructureUtils.h"
2929
#include "MoveOnlyDiagnostics.h"
3030
#include "MoveOnlyObjectCheckerUtils.h"
31+
#include "MoveOnlyTypeUtils.h"
3132

3233
#include "swift/Basic/BlotSetVector.h"
3334
#include "swift/Basic/Defer.h"
@@ -544,236 +545,6 @@ void Implementation::checkDestructureUsesOnBoundary() const {
544545
}
545546
}
546547

547-
static StructDecl *getFullyReferenceableStruct(SILType ktypeTy) {
548-
auto structDecl = ktypeTy.getStructOrBoundGenericStruct();
549-
if (!structDecl || structDecl->hasUnreferenceableStorage())
550-
return nullptr;
551-
return structDecl;
552-
}
553-
554-
namespace {
555-
556-
struct TypeOffsetSizePair {
557-
SubElementOffset startOffset = 0;
558-
TypeSubElementCount size = 0;
559-
560-
TypeOffsetSizePair() : startOffset(0), size(0) {}
561-
TypeOffsetSizePair(SILType baseType, SILFunction *fn)
562-
: startOffset(0), size(baseType, fn) {}
563-
TypeOffsetSizePair(SubElementOffset offset, TypeSubElementCount size)
564-
: startOffset(offset), size(size) {}
565-
TypeOffsetSizePair(SILValue projection, SILValue base)
566-
: startOffset(*SubElementOffset::compute(projection, base)),
567-
size(TypeSubElementCount(projection)) {}
568-
569-
IntRange<unsigned> getRange() const {
570-
return range(startOffset, getEndOffset());
571-
}
572-
573-
SubElementOffset getEndOffset() const {
574-
return SubElementOffset(startOffset + size);
575-
}
576-
577-
bool operator==(const TypeOffsetSizePair &other) const {
578-
return startOffset == other.startOffset && size == other.size;
579-
}
580-
581-
bool operator!=(const TypeOffsetSizePair &other) const {
582-
return !(*this == other);
583-
}
584-
585-
/// Given an ancestor offset \p ancestorOffset and a type called \p
586-
/// ancestorType, walk one level towards this current type which is assumed to
587-
/// be a child type of \p ancestorType.
588-
Optional<std::pair<TypeOffsetSizePair, SILType>>
589-
walkOneLevelTowardsChild(TypeOffsetSizePair ancestorOffsetSize,
590-
SILType ancestorType, SILFunction *fn) const {
591-
assert(ancestorOffsetSize.size >= size &&
592-
"Too large to be a child of ancestorType");
593-
assert((ancestorOffsetSize.startOffset <= startOffset &&
594-
startOffset <
595-
(ancestorOffsetSize.startOffset + ancestorOffsetSize.size)) &&
596-
"Not within the offset range of ancestor");
597-
598-
if (auto tupleType = ancestorType.getAs<TupleType>()) {
599-
// Before we do anything, see if we have a single element tuple. If we do,
600-
// just return that.
601-
if (tupleType->getNumElements() == 1) {
602-
return {{ancestorOffsetSize, ancestorType.getTupleElementType(0)}};
603-
}
604-
605-
assert(ancestorOffsetSize.size > size &&
606-
"Too large to be a child of ancestorType");
607-
608-
unsigned childOffset = ancestorOffsetSize.startOffset;
609-
610-
for (auto index : indices(tupleType->getElementTypes())) {
611-
SILType newType = ancestorType.getTupleElementType(index);
612-
unsigned newSize = TypeSubElementCount(newType, fn);
613-
614-
// childOffset + size(tupleChild) is the offset of the next tuple
615-
// element. If our target offset is less than that, then we know that
616-
// the target type must be a descendent of this tuple element type.
617-
if (childOffset + newSize > startOffset) {
618-
return {{{childOffset, newSize}, newType}};
619-
}
620-
621-
// Otherwise, add the new size of this field to iterOffset so we visit
622-
// our sibling type next.
623-
childOffset += newSize;
624-
}
625-
626-
// At this point, we know that our type is not a subtype of this
627-
// type. Some sort of logic error occurred.
628-
llvm_unreachable("Not a child of this type?!");
629-
}
630-
631-
if (auto *structDecl = getFullyReferenceableStruct(ancestorType)) {
632-
// Before we do anything, see if we have a single element struct. If we
633-
// do, just return that.
634-
auto storedProperties = structDecl->getStoredProperties();
635-
if (storedProperties.size() == 1) {
636-
return {{ancestorOffsetSize,
637-
ancestorType.getFieldType(storedProperties[0], fn)}};
638-
}
639-
640-
assert(ancestorOffsetSize.size > size &&
641-
"Too large to be a child of ancestorType");
642-
643-
unsigned childOffset = ancestorOffsetSize.startOffset;
644-
for (auto *fieldDecl : storedProperties) {
645-
SILType newType = ancestorType.getFieldType(fieldDecl, fn);
646-
unsigned newSize = TypeSubElementCount(newType, fn);
647-
648-
// iterOffset + size(tupleChild) is the offset of the next tuple
649-
// element. If our target offset is less than that, then we know that
650-
// the target type must be a child of this tuple element type.
651-
if (childOffset + newSize > startOffset) {
652-
return {{{childOffset, newSize}, newType}};
653-
}
654-
655-
// Otherwise, add the new size of this field to iterOffset so we visit
656-
// our sibling type next.
657-
childOffset += newSize;
658-
}
659-
660-
// At this point, we know that our type is not a subtype of this
661-
// type. Some sort of logic error occurred.
662-
llvm_unreachable("Not a child of this type?!");
663-
}
664-
665-
if (auto *enumDecl = ancestorType.getEnumOrBoundGenericEnum()) {
666-
llvm_unreachable("Cannot find child type of enum!\n");
667-
}
668-
669-
llvm_unreachable("Hit a leaf type?! Should have handled it earlier");
670-
}
671-
672-
/// Given an ancestor offset \p ancestorOffset and a type called \p
673-
/// ancestorType, walk one level towards this current type inserting on value,
674-
/// the relevant projection.
675-
Optional<std::pair<TypeOffsetSizePair, SILValue>>
676-
walkOneLevelTowardsChild(SILBuilderWithScope &builder, SILLocation loc,
677-
TypeOffsetSizePair ancestorOffsetSize,
678-
SILValue ancestorValue) const {
679-
auto *fn = ancestorValue->getFunction();
680-
SILType ancestorType = ancestorValue->getType();
681-
682-
assert(ancestorOffsetSize.size >= size &&
683-
"Too large to be a child of ancestorType");
684-
assert((ancestorOffsetSize.startOffset <= startOffset &&
685-
startOffset <
686-
(ancestorOffsetSize.startOffset + ancestorOffsetSize.size)) &&
687-
"Not within the offset range of ancestor");
688-
if (auto tupleType = ancestorType.getAs<TupleType>()) {
689-
// Before we do anything, see if we have a single element tuple. If we do,
690-
// just return that.
691-
if (tupleType->getNumElements() == 1) {
692-
auto *newValue = builder.createTupleExtract(loc, ancestorValue, 0);
693-
return {{ancestorOffsetSize, newValue}};
694-
}
695-
696-
assert(ancestorOffsetSize.size > size &&
697-
"Too large to be a child of ancestorType");
698-
699-
unsigned childOffset = ancestorOffsetSize.startOffset;
700-
701-
for (auto index : indices(tupleType->getElementTypes())) {
702-
SILType newType = ancestorType.getTupleElementType(index);
703-
unsigned newSize = TypeSubElementCount(newType, fn);
704-
705-
// childOffset + size(tupleChild) is the offset of the next tuple
706-
// element. If our target offset is less than that, then we know that
707-
// the target type must be a descendent of this tuple element type.
708-
if (childOffset + newSize > startOffset) {
709-
auto *newValue =
710-
builder.createTupleExtract(loc, ancestorValue, index);
711-
return {{{childOffset, newSize}, newValue}};
712-
}
713-
714-
// Otherwise, add the new size of this field to iterOffset so we visit
715-
// our sibling type next.
716-
childOffset += newSize;
717-
}
718-
719-
// At this point, we know that our type is not a subtype of this
720-
// type. Some sort of logic error occurred.
721-
llvm_unreachable("Not a child of this type?!");
722-
}
723-
724-
if (auto *structDecl = getFullyReferenceableStruct(ancestorType)) {
725-
// Before we do anything, see if we have a single element struct. If we
726-
// do, just return that.
727-
auto storedProperties = structDecl->getStoredProperties();
728-
if (storedProperties.size() == 1) {
729-
auto *newValue = builder.createStructExtract(loc, ancestorValue,
730-
storedProperties[0]);
731-
return {{ancestorOffsetSize, newValue}};
732-
}
733-
734-
assert(ancestorOffsetSize.size > size &&
735-
"Too large to be a child of ancestorType");
736-
737-
unsigned childOffset = ancestorOffsetSize.startOffset;
738-
for (auto *fieldDecl : structDecl->getStoredProperties()) {
739-
SILType newType = ancestorType.getFieldType(fieldDecl, fn);
740-
unsigned newSize = TypeSubElementCount(newType, fn);
741-
742-
// iterOffset + size(tupleChild) is the offset of the next tuple
743-
// element. If our target offset is less than that, then we know that
744-
// the target type must be a child of this tuple element type.
745-
if (childOffset + newSize > startOffset) {
746-
auto *newValue =
747-
builder.createStructExtract(loc, ancestorValue, fieldDecl);
748-
return {{{childOffset, newSize}, newValue}};
749-
}
750-
751-
// Otherwise, add the new size of this field to iterOffset so we visit
752-
// our sibling type next.
753-
childOffset += newSize;
754-
}
755-
756-
// At this point, we know that our type is not a subtype of this
757-
// type. Some sort of logic error occurred.
758-
llvm_unreachable("Not a child of this type?!");
759-
}
760-
761-
if (auto *enumDecl = ancestorType.getEnumOrBoundGenericEnum()) {
762-
llvm_unreachable("Cannot find child type of enum!\n");
763-
}
764-
765-
llvm_unreachable("Hit a leaf type?! Should have handled it earlier");
766-
}
767-
};
768-
769-
llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
770-
const TypeOffsetSizePair &other) {
771-
return os << "(startOffset: " << other.startOffset << ", size: " << other.size
772-
<< ")";
773-
}
774-
775-
} // anonymous namespace
776-
777548
#ifndef NDEBUG
778549
static void dumpSmallestTypeAvailable(
779550
SmallVectorImpl<Optional<std::pair<TypeOffsetSizePair, SILType>>>

lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,3 +750,28 @@ void DiagnosticEmitter::emitPromotedBoxArgumentError(
750750
diagnose(astContext, user, diag::sil_moveonlychecker_consuming_use_here);
751751
}
752752
}
753+
754+
void DiagnosticEmitter::emitCannotDestructureDeinitNominalError(
755+
MarkMustCheckInst *markedValue, StringRef pathString,
756+
NominalTypeDecl *deinitedNominal, SILInstruction *consumingUser) {
757+
auto &astContext = fn->getASTContext();
758+
SmallString<64> varName;
759+
getVariableNameForValue(markedValue, varName);
760+
761+
registerDiagnosticEmitted(markedValue);
762+
763+
if (pathString.empty()) {
764+
diagnose(
765+
astContext, markedValue,
766+
diag::sil_moveonlychecker_cannot_destructure_deinit_nominal_type_self,
767+
varName);
768+
} else {
769+
diagnose(
770+
astContext, markedValue,
771+
diag::sil_moveonlychecker_cannot_destructure_deinit_nominal_type_field,
772+
varName, varName, pathString.drop_front(),
773+
deinitedNominal->getBaseName());
774+
}
775+
diagnose(astContext, consumingUser,
776+
diag::sil_moveonlychecker_consuming_use_here);
777+
}

lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ class DiagnosticEmitter {
111111
void emitPromotedBoxArgumentError(MarkMustCheckInst *markedValue,
112112
SILFunctionArgument *arg);
113113

114+
void emitCannotDestructureDeinitNominalError(MarkMustCheckInst *markedValue,
115+
StringRef pathString,
116+
NominalTypeDecl *deinitedNominal,
117+
SILInstruction *consumingUser);
118+
114119
private:
115120
/// Emit diagnostics for the final consuming uses and consuming uses needing
116121
/// copy. If filter is non-null, allow for the caller to pre-process operands

0 commit comments

Comments
 (0)