Skip to content

Make Error and CodingKey conform to ConcurrentValue. #36070

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 3 commits into from
Feb 22, 2021
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
24 changes: 23 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4320,20 +4320,38 @@ ERROR(non_concurrent_type_member,none,
"%select{stored property %1|associated value %1}0 of "
"'ConcurrentValue'-conforming %2 %3 has non-concurrent-value type %4",
(bool, DeclName, DescriptiveDeclKind, DeclName, Type))
WARNING(non_concurrent_type_member_warn,none,
"%select{stored property %1|associated value %1}0 of "
"'ConcurrentValue'-conforming %2 %3 has non-concurrent-value type %4",
(bool, DeclName, DescriptiveDeclKind, DeclName, Type))
ERROR(concurrent_value_class_mutable_property,none,
"stored property %0 of 'ConcurrentValue'-conforming %1 %2 is mutable",
(DeclName, DescriptiveDeclKind, DeclName))
WARNING(concurrent_value_class_mutable_property_warn,none,
"stored property %0 of 'ConcurrentValue'-conforming %1 %2 is mutable",
(DeclName, DescriptiveDeclKind, DeclName))
ERROR(concurrent_value_outside_source_file,none,
"conformance 'ConcurrentValue' must occur in the same source file as "
"conformance to 'ConcurrentValue' must occur in the same source file as "
"%0 %1; use 'UnsafeConcurrentValue' for retroactive conformance",
(DescriptiveDeclKind, DeclName))
WARNING(concurrent_value_outside_source_file_warn,none,
"conformance to 'ConcurrentValue' must occur in the same source file as "
"%0 %1; use 'UnsafeConcurrentValue' for retroactive conformance",
(DescriptiveDeclKind, DeclName))
ERROR(concurrent_value_open_class,none,
"open class %0 cannot conform to `ConcurrentValue`; "
"use `UnsafeConcurrentValue`", (DeclName))
WARNING(concurrent_value_open_class_warn,none,
"open class %0 cannot conform to `ConcurrentValue`; "
"use `UnsafeConcurrentValue`", (DeclName))
ERROR(concurrent_value_inherit,none,
"`ConcurrentValue` class %1 cannot inherit from another class"
"%select{| other than 'NSObject'}0",
(bool, DeclName))
WARNING(concurrent_value_inherit_warn,none,
"`ConcurrentValue` class %1 cannot inherit from another class"
"%select{| other than 'NSObject'}0",
(bool, DeclName))

ERROR(actorindependent_let,none,
"'@actorIndependent' is meaningless on 'let' declarations because "
Expand Down Expand Up @@ -5624,6 +5642,10 @@ ERROR(marker_protocol_inherit_nonmarker, none,
(DeclName, DeclName))
ERROR(marker_protocol_cast,none,
"marker protocol %0 cannot be used in a conditional cast", (DeclName))
ERROR(marker_protocol_conditional_conformance,none,
"conditional conformance to non-marker protocol %0 cannot depend on "
"conformance of %1 to non-marker protocol %2",
(Identifier, Type, Identifier))

//------------------------------------------------------------------------------
// MARK: differentiable programming diagnostics
Expand Down
33 changes: 24 additions & 9 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2308,7 +2308,8 @@ static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) {
return false;
}

void swift::checkConcurrentValueConformance(ProtocolConformance *conformance) {
void swift::checkConcurrentValueConformance(
ProtocolConformance *conformance, bool asWarning) {
auto conformanceDC = conformance->getDeclContext();
auto nominal = conformance->getType()->getAnyNominal();
if (!nominal)
Expand All @@ -2326,7 +2327,9 @@ void swift::checkConcurrentValueConformance(ProtocolConformance *conformance) {
if (!conformanceDC->getParentSourceFile() ||
conformanceDC->getParentSourceFile() != nominal->getParentSourceFile()) {
conformanceDecl->diagnose(
diag::concurrent_value_outside_source_file,
asWarning
? diag::concurrent_value_outside_source_file_warn
: diag::concurrent_value_outside_source_file,
nominal->getDescriptiveKind(), nominal->getName());
return;
}
Expand All @@ -2335,8 +2338,11 @@ void swift::checkConcurrentValueConformance(ProtocolConformance *conformance) {
// An open class cannot conform to `ConcurrentValue`.
if (classDecl->getFormalAccess() == AccessLevel::Open) {
classDecl->diagnose(
diag::concurrent_value_open_class, classDecl->getName());
return;
asWarning ? diag::concurrent_value_open_class_warn
: diag::concurrent_value_open_class,
classDecl->getName());
if (!asWarning)
return;
}

// A 'ConcurrentValue' class cannot inherit from another class, although
Expand All @@ -2345,10 +2351,12 @@ void swift::checkConcurrentValueConformance(ProtocolConformance *conformance) {
if (auto superclassDecl = classDecl->getSuperclassDecl()) {
if (!superclassDecl->isNSObject()) {
classDecl->diagnose(
diag::concurrent_value_inherit,
asWarning ? diag::concurrent_value_inherit_warn
: diag::concurrent_value_inherit,
nominal->getASTContext().LangOpts.EnableObjCInterop,
classDecl->getName());
return;
if (!asWarning)
return;
}
}
}
Expand All @@ -2359,7 +2367,10 @@ void swift::checkConcurrentValueConformance(ProtocolConformance *conformance) {
if (isa<StructDecl>(nominal) || classDecl) {
for (auto property : nominal->getStoredProperties()) {
if (classDecl && property->supportsMutation()) {
property->diagnose(diag::concurrent_value_class_mutable_property, property->getName(), nominal->getDescriptiveKind(),
property->diagnose(
asWarning ? diag::concurrent_value_class_mutable_property_warn
: diag::concurrent_value_class_mutable_property,
property->getName(), nominal->getDescriptiveKind(),
nominal->getName());
continue;
}
Expand All @@ -2368,7 +2379,9 @@ void swift::checkConcurrentValueConformance(ProtocolConformance *conformance) {
conformanceDC->mapTypeIntoContext(property->getInterfaceType());
if (!isConcurrentValueType(conformanceDC, propertyType)) {
property->diagnose(
diag::non_concurrent_type_member, false, property->getName(),
asWarning ? diag::non_concurrent_type_member_warn
: diag::non_concurrent_type_member,
false, property->getName(),
nominal->getDescriptiveKind(), nominal->getName(), propertyType);
continue;
}
Expand All @@ -2389,7 +2402,9 @@ void swift::checkConcurrentValueConformance(ProtocolConformance *conformance) {
element->getArgumentInterfaceType());
if (!isConcurrentValueType(conformanceDC, elementType)) {
element->diagnose(
diag::non_concurrent_type_member, true, element->getName(),
asWarning ? diag::non_concurrent_type_member_warn
: diag::non_concurrent_type_member,
true, element->getName(),
nominal->getDescriptiveKind(), nominal->getName(), elementType);
continue;
}
Expand Down
3 changes: 2 additions & 1 deletion lib/Sema/TypeCheckConcurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@ bool diagnoseNonConcurrentTypesInReference(
ConcurrentReferenceKind refKind);

/// Check the correctness of the given ConcurrentValue conformance.
void checkConcurrentValueConformance(ProtocolConformance *conformance);
void checkConcurrentValueConformance(
ProtocolConformance *conformance, bool asWarning);

} // end namespace swift

Expand Down
32 changes: 29 additions & 3 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1935,6 +1935,23 @@ checkIndividualConformance(NormalProtocolConformance *conformance,

nestedType = nestedType.getNominalParent();
}

// If the protocol to which we are conditionally conforming is not a marker
// protocol, the conditional requirements must not involve conformance to a
// marker protocol. We cannot evaluate such a conformance at runtime.
if (!Proto->isMarkerProtocol()) {
for (const auto &req : *conditionalReqs) {
if (req.getKind() == RequirementKind::Conformance &&
req.getSecondType()->castTo<ProtocolType>()->getDecl()
->isMarkerProtocol()) {
C.Diags.diagnose(
ComplainLoc, diag::marker_protocol_conditional_conformance,
Proto->getName(), req.getFirstType(),
req.getSecondType()->castTo<ProtocolType>()->getDecl()->getName());
conformance->setInvalid();
}
}
}
}

// If the protocol contains missing requirements, it can't be conformed to
Expand Down Expand Up @@ -1962,7 +1979,8 @@ checkIndividualConformance(NormalProtocolConformance *conformance,
}

bool impliedDisablesMissingWitnessFixits = false;
if (conformance->getSourceKind() == ConformanceEntryKind::Implied) {
if (conformance->getSourceKind() == ConformanceEntryKind::Implied &&
!Proto->isMarkerProtocol()) {
// We've got something like:
//
// protocol Foo : Proto {}
Expand Down Expand Up @@ -5642,6 +5660,8 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {

ProtocolConformance *concurrentValueConformance = nullptr;
ProtocolConformance *unsafeConcurrentValueConformance = nullptr;
ProtocolConformance *errorConformance = nullptr;
ProtocolConformance *codingKeyConformance = nullptr;
bool anyInvalid = false;
for (auto conformance : conformances) {
// Check and record normal conformances.
Expand Down Expand Up @@ -5673,12 +5693,18 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
} else if (proto->isSpecificProtocol(
KnownProtocolKind::UnsafeConcurrentValue)) {
unsafeConcurrentValueConformance = conformance;
} else if (proto->isSpecificProtocol(KnownProtocolKind::Error)) {
errorConformance = conformance;
} else if (proto->isSpecificProtocol(KnownProtocolKind::CodingKey)) {
codingKeyConformance = conformance;
}
}

// Check constraints of ConcurrentValue.
if (concurrentValueConformance && !unsafeConcurrentValueConformance)
checkConcurrentValueConformance(concurrentValueConformance);
if (concurrentValueConformance && !unsafeConcurrentValueConformance) {
bool asWarning = errorConformance || codingKeyConformance;
checkConcurrentValueConformance(concurrentValueConformance, asWarning);
}

// Check all conformances.
groupChecker.checkAllConformances();
Expand Down
7 changes: 4 additions & 3 deletions stdlib/public/core/Codable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ public typealias Codable = Encodable & Decodable
//===----------------------------------------------------------------------===//

/// A type that can be used as a key for encoding and decoding.
public protocol CodingKey: CustomStringConvertible,
public protocol CodingKey: ConcurrentValue,
CustomStringConvertible,
CustomDebugStringConvertible {
/// The string to use in a named collection (e.g. a string-keyed dictionary).
var stringValue: String { get }
Expand Down Expand Up @@ -3179,7 +3180,7 @@ public struct CodingUserInfoKey: RawRepresentable, Equatable, Hashable, Concurre
/// An error that occurs during the encoding of a value.
public enum EncodingError: Error {
/// The context in which the error occurred.
public struct Context {
public struct Context: ConcurrentValue {
/// The path of coding keys taken to get to the point of the failing encode
/// call.
public let codingPath: [CodingKey]
Expand Down Expand Up @@ -3262,7 +3263,7 @@ public enum EncodingError: Error {
/// An error that occurs during the decoding of a value.
public enum DecodingError: Error {
/// The context in which the error occurred.
public struct Context {
public struct Context: ConcurrentValue {
/// The path of coding keys taken to get to the point of the failing decode
/// call.
public let codingPath: [CodingKey]
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/ErrorType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ import SwiftShims
/// print("Other error: \(error)")
/// }
/// // Prints "Parsing error: mismatchedTag [19:5]"
public protocol Error {
public protocol Error: ConcurrentValue {
var _domain: String { get }
var _code: Int { get }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public struct URL : _ObjectiveCBridgeable {
}
}

extension NSError : Error {
extension NSError : Error, UnsafeConcurrentValue {
public var _domain: String { return domain }
public var _code: Int { return code }
}
Expand Down
4 changes: 2 additions & 2 deletions test/SILGen/errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ class Cat {}
enum HomeworkError : Error {
case TooHard
case TooMuch
case CatAteIt(Cat)
case CatHidIt(Cat)
case CatAteIt(Cat) // expected-warning{{associated value 'CatAteIt' of 'ConcurrentValue'-conforming enum 'HomeworkError' has non-concurrent-value type 'Cat'}}
case CatHidIt(Cat) // expected-warning{{associated value 'CatHidIt' of 'ConcurrentValue'-conforming enum 'HomeworkError' has non-concurrent-value type 'Cat'}}
}

func someValidPointer<T>() -> UnsafePointer<T> { fatalError() }
Expand Down
2 changes: 1 addition & 1 deletion test/Sema/existential_nested_type.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ protocol HasAssoc {
}

enum MyError : Error {
case bad(Any)
case bad(Any) // expected-warning{{associated value 'bad' of 'ConcurrentValue'-conforming enum 'MyError' has non-concurrent-value type 'Any'}}
}

func checkIt(_ js: Any) throws {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Protocol CodingKey has added inherited protocol ConcurrentValue
Protocol CodingKey has generic signature change from <Self : Swift.CustomDebugStringConvertible, Self : Swift.CustomStringConvertible> to <Self : Swift.ConcurrentValue, Self : Swift.CustomDebugStringConvertible, Self : Swift.CustomStringConvertible>
Protocol Error has added inherited protocol ConcurrentValue
Protocol Error has generic signature change from to <Self : Swift.ConcurrentValue>
4 changes: 4 additions & 0 deletions test/api-digester/stability-stdlib-abi-with-asserts.test
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,8 @@ Func _measureRuntimeFunctionCountersDiffs(objects:_:) is a new API without @avai
Protocol _RuntimeFunctionCountersStats is a new API without @available attribute
Struct _GlobalRuntimeFunctionCountersState is a new API without @available attribute
Struct _ObjectRuntimeFunctionCountersState is a new API without @available attribute
Protocol CodingKey has added inherited protocol ConcurrentValue
Protocol CodingKey has generic signature change from <Self : Swift.CustomDebugStringConvertible, Self : Swift.CustomStringConvertible> to <Self : Swift.ConcurrentValue, Self : Swift.CustomDebugStringConvertible, Self : Swift.CustomStringConvertible>
Protocol Error has added inherited protocol ConcurrentValue
Protocol Error has generic signature change from to <Self : Swift.ConcurrentValue>
Struct _RuntimeFunctionCounters is a new API without @available attribute
12 changes: 12 additions & 0 deletions test/attr/attr_marker_protocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,15 @@ func testNotOkay(a: Any) {
func inner(p3: P3) { }
}


@_marker protocol P8 { }
protocol P9: P8 { }

// Implied conditional conformance to P8 is okay because P8 is a marker
// protocol.
extension Array: P9 where Element: P9 { }

protocol P10 { }

extension Array: P10 where Element: P10, Element: P8 { }
// expected-error@-1{{conditional conformance to non-marker protocol 'P10' cannot depend on conformance of 'Element' to non-marker protocol 'P8'}}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
extension NoRawTypeKey : CodingKey {}
extension StringKey : CodingKey {}
extension IntKey : CodingKey {}
extension NoRawTypeKey : CodingKey, UnsafeConcurrentValue {}
extension StringKey : CodingKey, UnsafeConcurrentValue {}
extension IntKey : CodingKey, UnsafeConcurrentValue {}