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,
|
ERROR(self_use_before_init_in_delegatinginit,sil_analysis,none,
|
||||||
"use of '%0' in delegating initializer before self.init is called",
|
"use of '%0' in delegating initializer before self.init is called",
|
||||||
(StringRef))
|
(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,
|
ERROR(variable_addrtaken_before_initialized,sil_analysis,none,
|
||||||
"address of variable '%0' taken before it is initialized",
|
"address of variable '%0' taken before it is initialized",
|
||||||
(StringRef))
|
(StringRef))
|
||||||
|
|||||||
@@ -662,10 +662,19 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If this is an @inout parameter, it is like both a load and store.
|
// If this is an @inout parameter, it is like both a load and store.
|
||||||
case ParameterConvention::Indirect_Inout:
|
case ParameterConvention::Indirect_Inout: {
|
||||||
addElementUses(BaseEltNo, PointeeType, User, DIUseKind::InOutUse);
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
llvm_unreachable("bad parameter convention");
|
llvm_unreachable("bad parameter convention");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,14 @@ public:
|
|||||||
return false;
|
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.
|
// True if the memory object is the 'self' argument of a class initializer.
|
||||||
bool isClassInitSelf() const {
|
bool isClassInitSelf() const {
|
||||||
if (isAnyInitSelf() && isa<ClassDecl>(getType()->getAnyNominal()))
|
if (isAnyInitSelf() && isa<ClassDecl>(getType()->getAnyNominal()))
|
||||||
|
|||||||
@@ -673,7 +673,13 @@ void LifetimeChecker::doIt() {
|
|||||||
diag::return_from_init_without_self_init);
|
diag::return_from_init_without_self_init);
|
||||||
break;
|
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))
|
} else if (isa<MarkFunctionEscapeInst>(Inst))
|
||||||
DiagMessage = diag::global_variable_function_use_uninit;
|
DiagMessage = diag::global_variable_function_use_uninit;
|
||||||
else if (isa<AddressToPointerInst>(Inst))
|
else if (isa<AddressToPointerInst>(Inst))
|
||||||
|
|||||||
@@ -957,3 +957,32 @@ class r19254812Derived: r19254812Base{
|
|||||||
println(pi) // ok, no diagnostic expected.
|
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