mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
fix <rdar://problem/19259730> Using mutating methods in a struct initializer with a let property is rejected
while we're at it, improve the QoI for actually-invalid mutating method calls in struct initalizers. Swift SVN r24030
This commit is contained in:
@@ -92,6 +92,10 @@ ERROR(variable_escape_before_initialized,sil_analysis,none,
|
||||
ERROR(self_use_before_init_in_delegatinginit,sil_analysis,none,
|
||||
"use of '%0' in delegating initializer before self.init is called",
|
||||
(StringRef))
|
||||
ERROR(self_use_before_init_in_structinit,sil_analysis,none,
|
||||
"use of '%0' in delegating initializer before self.init is called",
|
||||
(StringRef))
|
||||
|
||||
ERROR(variable_addrtaken_before_initialized,sil_analysis,none,
|
||||
"address of variable '%0' taken before it is initialized",
|
||||
(StringRef))
|
||||
|
||||
@@ -662,10 +662,19 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) {
|
||||
continue;
|
||||
|
||||
// If this is an @inout parameter, it is like both a load and store.
|
||||
case ParameterConvention::Indirect_Inout:
|
||||
addElementUses(BaseEltNo, PointeeType, User, DIUseKind::InOutUse);
|
||||
case ParameterConvention::Indirect_Inout: {
|
||||
// If we're in the initializer for a struct, and this is a call to a
|
||||
// mutating method, we model that as an escape of self. If an
|
||||
// individual sub-member is passed as inout, then we model that as an
|
||||
// inout use.
|
||||
auto Kind = DIUseKind::InOutUse;
|
||||
if (TheMemory.isStructInitSelf() && Pointer == TheMemory.getAddress())
|
||||
Kind = DIUseKind::Escape;
|
||||
|
||||
addElementUses(BaseEltNo, PointeeType, User, Kind);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
llvm_unreachable("bad parameter convention");
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,14 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
// True if the memory object is the 'self' argument of a struct initializer.
|
||||
bool isStructInitSelf() const {
|
||||
if (auto *MUI = dyn_cast<MarkUninitializedInst>(MemoryInst))
|
||||
if (MUI->isRootSelf() && isa<StructDecl>(getType()->getAnyNominal()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// True if the memory object is the 'self' argument of a class initializer.
|
||||
bool isClassInitSelf() const {
|
||||
if (isAnyInitSelf() && isa<ClassDecl>(getType()->getAnyNominal()))
|
||||
|
||||
@@ -673,7 +673,13 @@ void LifetimeChecker::doIt() {
|
||||
diag::return_from_init_without_self_init);
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (isa<ApplyInst>(Inst) && TheMemory.isStructInitSelf()) {
|
||||
if (shouldEmitError(Inst)) {
|
||||
diagnose(Module, Inst->getLoc(),
|
||||
diag::use_of_self_before_fully_init);
|
||||
noteUninitializedMembers(Use);
|
||||
}
|
||||
break;
|
||||
} else if (isa<MarkFunctionEscapeInst>(Inst))
|
||||
DiagMessage = diag::global_variable_function_use_uninit;
|
||||
else if (isa<AddressToPointerInst>(Inst))
|
||||
|
||||
@@ -957,3 +957,32 @@ class r19254812Derived: r19254812Base{
|
||||
println(pi) // ok, no diagnostic expected.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// <rdar://problem/19259730> Using mutating methods in a struct initializer with a let property is rejected
|
||||
struct StructMutatingMethodTest {
|
||||
let a, b: String // expected-note 2 {{'self.b' not initialized}}
|
||||
|
||||
init(x: String, y: String) {
|
||||
a = x
|
||||
b = y
|
||||
mutate() // ok
|
||||
}
|
||||
|
||||
init(x: String) {
|
||||
a = x
|
||||
mutate() // expected-error {{'self' used before all stored properties are initialized}}
|
||||
b = x
|
||||
}
|
||||
|
||||
init() {
|
||||
a = ""
|
||||
nonmutate() // expected-error {{'self' used before all stored properties are initialized}}
|
||||
b = ""
|
||||
}
|
||||
|
||||
mutating func mutate() {}
|
||||
func nonmutate() {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user