Skip to content

Sema: Fix enum case witness crash when the requirement does not specify accessors #35860

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5663,6 +5663,11 @@ bool VarDecl::isSettable(const DeclContext *UseDC,
if (!isLet())
return supportsMutation();

// Static 'let's are always immutable.
if (isStatic()) {
return false;
}

//
// All the remaining logic handles the special cases where you can
// assign a 'let'.
Expand Down
18 changes: 12 additions & 6 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -648,17 +648,23 @@ swift::matchWitness(
} else if (isa<ConstructorDecl>(witness)) {
decomposeFunctionType = true;
ignoreReturnType = true;
} else if (isa<EnumElementDecl>(witness)) {
auto enumCase = cast<EnumElementDecl>(witness);
} else if (auto *enumCase = dyn_cast<EnumElementDecl>(witness)) {
// An enum case with associated values can satisfy only a
// method requirement.
if (enumCase->hasAssociatedValues() && isa<VarDecl>(req))
return RequirementMatch(witness, MatchKind::EnumCaseWithAssociatedValues);
auto isValid = isa<VarDecl>(req) || isa<FuncDecl>(req);
if (!isValid)

// An enum case can satisfy only a method or property requirement.
if (!isa<VarDecl>(req) && !isa<FuncDecl>(req))
return RequirementMatch(witness, MatchKind::KindConflict);
if (!cast<ValueDecl>(req)->isStatic())

// An enum case can satisfy only a static requirement.
if (!req->isStatic())
return RequirementMatch(witness, MatchKind::StaticNonStaticConflict);

// An enum case cannot satisfy a settable property requirement.
if (isa<VarDecl>(req) &&
cast<VarDecl>(req)->getParsedAccessor(AccessorKind::Set))
cast<VarDecl>(req)->isSettable(req->getDeclContext()))
return RequirementMatch(witness, MatchKind::SettableConflict);

decomposeFunctionType = enumCase->hasAssociatedValues();
Expand Down
21 changes: 21 additions & 0 deletions lib/Sema/TypeCheckStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3252,6 +3252,27 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator,
}
}

// Handle protocol requirements specially.
if (isa<ProtocolDecl>(storage->getDeclContext())) {
ReadImplKind readImpl = ReadImplKind::Stored;
// By default, treat the requirement as not having a setter.
WriteImplKind writeImpl = WriteImplKind::Immutable;
ReadWriteImplKind readWriteImpl = ReadWriteImplKind::Immutable;

if (storage->getParsedAccessor(AccessorKind::Set)) {
readImpl = ReadImplKind::Get;
writeImpl = WriteImplKind::Set;
readWriteImpl = ReadWriteImplKind::MaterializeToTemporary;
} else if (storage->getParsedAccessor(AccessorKind::Get)) {
readImpl = ReadImplKind::Get;
}

StorageImplInfo info(readImpl, writeImpl, readWriteImpl);
finishStorageImplInfo(storage, info);

return info;
}

bool hasWillSet = storage->getParsedAccessor(AccessorKind::WillSet);
bool hasDidSet = storage->getParsedAccessor(AccessorKind::DidSet);
bool hasSetter = storage->getParsedAccessor(AccessorKind::Set);
Expand Down
14 changes: 14 additions & 0 deletions test/decl/protocol/protocol_enum_witness.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,17 @@ protocol ThrowingFactory {
enum HorseFactory : ThrowingFactory {
case horse(Int)
}

protocol MissingAccessorsVar {
static var bar: Self // expected-error {{property in protocol must have explicit { get } or { get set } specifier}}
}
enum Bar14: MissingAccessorsVar { // OK
case bar
}

protocol MissingAccessorsLet {
static let bar: Self // expected-error {{protocols cannot require properties to be immutable; declare read-only properties by using 'var' with a '{ get }' specifier}}
}
enum Bar15: MissingAccessorsLet { // OK
case bar
}