Skip to content

Commit b0c990a

Browse files
authored
Merge pull request #65131 from gottesmm/release/5.9/101651138
[5.9][move-only] Make it an error if we attempt to destructure/partially invalidate through a field with a deinit.
2 parents ec237da + c8d19ed commit b0c990a

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)