Skip to content

Commit 5aca779

Browse files
authored
Merge pull request #25145 from slavapestov/protocol-requirement-accessor-crash
Sema: Fix crash-on-invalid with 'let' property in protocol
2 parents 36ce8ff + 5e39f03 commit 5aca779

File tree

3 files changed

+17
-114
lines changed

3 files changed

+17
-114
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 8 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -209,112 +209,6 @@ getTypesToCompare(ValueDecl *reqt, Type reqtType, bool reqtTypeIsIUO,
209209
return std::make_tuple(reqtType, witnessType, optAdjustment);
210210
}
211211

212-
// Verify that the mutating bit is correct between a protocol requirement and a
213-
// witness. This returns true on invalid.
214-
static bool checkMutating(FuncDecl *requirement, FuncDecl *witness,
215-
ValueDecl *witnessDecl) {
216-
// Witnesses in classes never have mutating conflicts.
217-
if (auto contextType =
218-
witnessDecl->getDeclContext()->getDeclaredInterfaceType())
219-
if (contextType->hasReferenceSemantics())
220-
return false;
221-
222-
// Determine whether the witness will be mutating or not. If the witness is
223-
// stored property accessor, it may not be synthesized yet.
224-
bool witnessMutating;
225-
if (witness)
226-
witnessMutating = (requirement->isInstanceMember() &&
227-
witness->isMutating());
228-
else {
229-
auto reqtAsAccessor = cast<AccessorDecl>(requirement);
230-
auto storage = cast<AbstractStorageDecl>(witnessDecl);
231-
232-
auto isReadMutating = [&] {
233-
switch (storage->getReadImpl()) {
234-
case ReadImplKind::Stored:
235-
return false;
236-
case ReadImplKind::Address:
237-
return storage->getAddressor()->isMutating();
238-
case ReadImplKind::Read:
239-
return storage->getReadCoroutine()->isMutating();
240-
case ReadImplKind::Inherited:
241-
case ReadImplKind::Get:
242-
llvm_unreachable("should have a getter");
243-
}
244-
llvm_unreachable("unhandled kind");
245-
};
246-
247-
auto isStoredSetterMutating = [&] {
248-
// A stored property on a value type will have a mutating setter
249-
// and a non-mutating getter.
250-
return reqtAsAccessor->isInstanceMember();
251-
};
252-
253-
auto isWriteMutating = [&] {
254-
switch (storage->getWriteImpl()) {
255-
case WriteImplKind::Stored:
256-
return isStoredSetterMutating();
257-
case WriteImplKind::MutableAddress:
258-
return storage->getMutableAddressor()->isMutating();
259-
case WriteImplKind::Modify:
260-
return storage->getModifyCoroutine()->isMutating();
261-
case WriteImplKind::Immutable:
262-
llvm_unreachable("asking for setter for immutable storage");
263-
case WriteImplKind::Set:
264-
case WriteImplKind::StoredWithObservers:
265-
case WriteImplKind::InheritedWithObservers:
266-
llvm_unreachable("should have a setter");
267-
}
268-
llvm_unreachable("unhandled kind");
269-
};
270-
271-
auto isReadWriteMutating = [&] {
272-
switch (storage->getReadWriteImpl()) {
273-
case ReadWriteImplKind::Stored:
274-
return isStoredSetterMutating();
275-
case ReadWriteImplKind::MutableAddress:
276-
return storage->getMutableAddressor()->isMutating();
277-
case ReadWriteImplKind::Modify:
278-
return storage->getModifyCoroutine()->isMutating();
279-
case ReadWriteImplKind::MaterializeToTemporary:
280-
return isReadMutating() || isWriteMutating();
281-
case ReadWriteImplKind::Immutable:
282-
llvm_unreachable("asking for setter for immutable storage");
283-
}
284-
llvm_unreachable("unhandled kind");
285-
};
286-
287-
switch (reqtAsAccessor->getAccessorKind()) {
288-
case AccessorKind::Get:
289-
case AccessorKind::Read:
290-
witnessMutating = isReadMutating();
291-
break;
292-
293-
case AccessorKind::Set:
294-
witnessMutating = isWriteMutating();
295-
break;
296-
297-
case AccessorKind::Modify:
298-
witnessMutating = isReadWriteMutating();
299-
break;
300-
301-
#define OPAQUE_ACCESSOR(ID, KEYWORD)
302-
#define ACCESSOR(ID) \
303-
case AccessorKind::ID:
304-
#include "swift/AST/AccessorKinds.def"
305-
llvm_unreachable("unexpected accessor requirement");
306-
}
307-
}
308-
309-
// Requirements in class-bound protocols never 'mutate' self.
310-
auto *proto = cast<ProtocolDecl>(requirement->getDeclContext());
311-
bool requirementMutating = (requirement->isMutating() &&
312-
!proto->requiresClass());
313-
314-
// The witness must not be more mutating than the requirement.
315-
return !requirementMutating && witnessMutating;
316-
}
317-
318212
/// Check that the Objective-C method(s) provided by the witness have
319213
/// the same selectors as those required by the requirement.
320214
static bool checkObjCWitnessSelector(TypeChecker &tc, ValueDecl *req,
@@ -484,7 +378,7 @@ swift::matchWitness(
484378
return RequirementMatch(witness, MatchKind::PostfixNonPostfixConflict);
485379

486380
// Check that the mutating bit is ok.
487-
if (checkMutating(funcReq, funcWitness, funcWitness))
381+
if (!funcReq->isMutating() && funcWitness->isMutating())
488382
return RequirementMatch(witness, MatchKind::MutatingConflict);
489383

490384
// If the requirement is rethrows, the witness must either be
@@ -509,15 +403,15 @@ swift::matchWitness(
509403
return RequirementMatch(witness, MatchKind::SettableConflict);
510404

511405
// Validate that the 'mutating' bit lines up for getters and setters.
512-
if (checkMutating(reqASD->getGetter(), witnessASD->getGetter(),
513-
witnessASD))
406+
if (!reqASD->isGetterMutating() && witnessASD->isGetterMutating())
514407
return RequirementMatch(getStandinForAccessor(witnessASD, AccessorKind::Get),
515408
MatchKind::MutatingConflict);
516-
517-
if (req->isSettable(req->getDeclContext()) &&
518-
checkMutating(reqASD->getSetter(), witnessASD->getSetter(), witnessASD))
519-
return RequirementMatch(getStandinForAccessor(witnessASD, AccessorKind::Set),
520-
MatchKind::MutatingConflict);
409+
410+
if (req->isSettable(req->getDeclContext())) {
411+
if (!reqASD->isSetterMutating() && witnessASD->isSetterMutating())
412+
return RequirementMatch(getStandinForAccessor(witnessASD, AccessorKind::Set),
413+
MatchKind::MutatingConflict);
414+
}
521415

522416
// Decompose the parameters for subscript declarations.
523417
decomposeFunctionType = isa<SubscriptDecl>(req);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public protocol P {
2+
let x: Int
3+
// expected-error@-1 {{immutable property requirement must be declared as 'var' with a '{ get }' specifier}}
4+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// RUN: %target-swift-frontend -typecheck -verify -primary-file %s %S/Inputs/protocol-conformance-let-other.swift
2+
3+
public struct S : P {
4+
public var x: Int
5+
}

0 commit comments

Comments
 (0)