mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Clean up the semantics of 'let' properties in a number of ways:
- We switch to a model where let properties may be "initialized", but never reassigned. Specifically, immutable properties in structs/classes may have an init value specified in their declaration (but can then never be reset in any init implementation) or not (in which case they must be initialized exactly once on all paths through every init. This makes a lot more sense for immutability, defines several problems away, and provides a path to supporting things like (rdar://16181314) - We now *never* default initialize an immutable property. Formerly we would default initialize optional let properties to nil, but this isn't actually useful, and allows an error of omission with let properties. This resolves: <rdar://problem/19035287> let properties should only be initializable, not reassignable and possibly other radars. Swift SVN r23779
This commit is contained in:
@@ -370,6 +370,8 @@ namespace {
|
||||
bool isInitializedAtUse(const DIMemoryUse &Use, bool *SuperInitDone = 0);
|
||||
|
||||
void handleStoreUse(unsigned UseID);
|
||||
void handleInOutUse(const DIMemoryUse &Use);
|
||||
|
||||
void handleLoadUseFailure(const DIMemoryUse &InstInfo,
|
||||
bool IsSuperInitComplete);
|
||||
void handleSuperInitUse(const DIMemoryUse &InstInfo);
|
||||
@@ -649,8 +651,7 @@ void LifetimeChecker::doIt() {
|
||||
}
|
||||
|
||||
case DIUseKind::InOutUse:
|
||||
if (!isInitializedAtUse(Use))
|
||||
diagnoseInitError(Use, diag::variable_inout_before_initialized);
|
||||
handleInOutUse(Use);
|
||||
break;
|
||||
|
||||
case DIUseKind::Escape:
|
||||
@@ -726,16 +727,7 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) {
|
||||
auto Liveness = getLivenessAtInst(InstInfo.Inst, InstInfo.FirstElement,
|
||||
InstInfo.NumElements);
|
||||
|
||||
// If this is a partial store into a struct and the whole struct hasn't been
|
||||
// initialized, diagnose this as an error.
|
||||
if (InstInfo.Kind == DIUseKind::PartialStore &&
|
||||
Liveness.get(InstInfo.FirstElement) != DIKind::Yes) {
|
||||
assert(InstInfo.NumElements == 1 && "partial stores are intra-element");
|
||||
diagnoseInitError(InstInfo, diag::struct_not_fully_initialized);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check to see if the store is either fully uninitialized or fully
|
||||
// Check to see if the stored location is either fully uninitialized or fully
|
||||
// initialized.
|
||||
bool isFullyInitialized = true;
|
||||
bool isFullyUninitialized = true;
|
||||
@@ -748,6 +740,38 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) {
|
||||
isFullyUninitialized = false;
|
||||
}
|
||||
|
||||
// If this is a partial store into a struct and the whole struct hasn't been
|
||||
// initialized, diagnose this as an error.
|
||||
if (InstInfo.Kind == DIUseKind::PartialStore && !isFullyInitialized) {
|
||||
assert(InstInfo.NumElements == 1 && "partial stores are intra-element");
|
||||
diagnoseInitError(InstInfo, diag::struct_not_fully_initialized);
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is a store to a 'let' property in an initializer, then we only
|
||||
// allow the assignment if the property was completely uninitialized.
|
||||
// Overwrites are not permitted.
|
||||
if (TheMemory.IsSelfOfNonDelegatingInitializer &&
|
||||
(InstInfo.Kind == DIUseKind::PartialStore || !isFullyUninitialized)) {
|
||||
|
||||
for (unsigned i = InstInfo.FirstElement, e = i+InstInfo.NumElements;
|
||||
i != e; ++i) {
|
||||
if (Liveness.get(i) == DIKind::No || !TheMemory.isElementLetProperty(i))
|
||||
continue;
|
||||
|
||||
std::string PropertyName = "self";
|
||||
auto *VD = TheMemory.getPathStringToElement(i, PropertyName);
|
||||
diagnose(Module, InstInfo.Inst->getLoc(),
|
||||
diag::immutable_property_already_initialized, PropertyName);
|
||||
if (auto *Var = dyn_cast<VarDecl>(VD))
|
||||
if (auto *InitPat = Var->getParentPattern())
|
||||
if (InitPat->hasInit())
|
||||
diagnose(Module, SILLocation(VD),
|
||||
diag::initial_value_provided_in_let_decl);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is an initialization or a normal assignment, upgrade the store to
|
||||
// an initialization or assign in the uses list so that clients know about it.
|
||||
if (isFullyUninitialized) {
|
||||
@@ -778,6 +802,33 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) {
|
||||
updateInstructionForInitState(InstInfo);
|
||||
}
|
||||
|
||||
void LifetimeChecker::handleInOutUse(const DIMemoryUse &Use) {
|
||||
// inout uses are generally straight-forward: the memory must be initialized
|
||||
// before the "address" is passed as an l-value.
|
||||
if (!isInitializedAtUse(Use)) {
|
||||
diagnoseInitError(Use, diag::variable_inout_before_initialized);
|
||||
return;
|
||||
}
|
||||
|
||||
// One additional check: 'let' properties may never be passed inout, because
|
||||
// they are only allowed to have their initial value set, not a subsequent
|
||||
// overwrite.
|
||||
if (TheMemory.IsSelfOfNonDelegatingInitializer) {
|
||||
for (unsigned i = Use.FirstElement, e = i+Use.NumElements;
|
||||
i != e; ++i) {
|
||||
if (!TheMemory.isElementLetProperty(i))
|
||||
continue;
|
||||
|
||||
std::string PropertyName = "self";
|
||||
(void)TheMemory.getPathStringToElement(i, PropertyName);
|
||||
diagnose(Module, Use.Inst->getLoc(),
|
||||
diag::immutable_property_passed_inout, PropertyName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// handleLoadUseFailure - Check and diagnose various failures when a load use
|
||||
/// is not fully initialized.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user