Skip to content

[5.1] Opaque types require a newer Swift runtime. #24821

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
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
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4052,6 +4052,10 @@ ERROR(availability_decl_only_version_newer, none,
"%0 is only available in %1 %2 or newer",
(DeclName, StringRef, llvm::VersionTuple))

ERROR(availability_opaque_types_only_version_newer, none,
"'some' return types are only available in %0 %1 or newer",
(StringRef, llvm::VersionTuple))

NOTE(availability_guard_with_version_check, none,
"add 'if #available' version check", ())

Expand Down
2 changes: 1 addition & 1 deletion include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ namespace swift {

/// Enable the experimental opaque result types feature.
bool EnableOpaqueResultTypes = true;

/// To mimic existing system, set to false.
/// To experiment with including file-private and private dependency info,
/// set to true.
Expand Down
26 changes: 26 additions & 0 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1326,6 +1326,32 @@ static void fixAvailability(SourceRange ReferenceRange,
}
}

void TypeChecker::diagnosePotentialOpaqueTypeUnavailability(
SourceRange ReferenceRange, const DeclContext *ReferenceDC,
const UnavailabilityReason &Reason) {
// We only emit diagnostics for API unavailability, not for explicitly
// weak-linked symbols.
if (Reason.getReasonKind() !=
UnavailabilityReason::Kind::RequiresOSVersionRange) {
return;
}

auto RequiredRange = Reason.getRequiredOSVersionRange();
{
auto Err =
diagnose(ReferenceRange.Start, diag::availability_opaque_types_only_version_newer,
prettyPlatformString(targetPlatform(Context.LangOpts)),
Reason.getRequiredOSVersionRange().getLowerEndpoint());

// Direct a fixit to the error if an existing guard is nearly-correct
if (fixAvailabilityByNarrowingNearbyVersionCheck(ReferenceRange,
ReferenceDC,
RequiredRange, *this, Err))
return;
}
fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, *this);
}

void TypeChecker::diagnosePotentialUnavailability(
const Decl *D, DeclName Name, SourceRange ReferenceRange,
const DeclContext *ReferenceDC, const UnavailabilityReason &Reason) {
Expand Down
33 changes: 30 additions & 3 deletions lib/Sema/TypeCheckGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,26 @@ static void revertDependentTypeLoc(TypeLoc &tl) {
tl.setType(Type());
}

///
/// Generic functions
///
//
// Generic functions
//

static AvailabilityContext getOpaqueTypeAvailability(ASTContext &Context) {
if (Context.LangOpts.DisableAvailabilityChecking)
return AvailabilityContext::alwaysAvailable();

auto target = Context.LangOpts.Target;

if (target.isMacOSX()
|| target.isiOS()
|| target.isWatchOS())
// TODO: Update with OS versions that ship with runtime support
return AvailabilityContext(
VersionRange::allGTE(llvm::VersionTuple(9999,0,0)));


return AvailabilityContext::alwaysAvailable();
}

/// Get the opaque type representing the return type of a declaration, or
/// create it if it does not yet exist.
Expand All @@ -181,6 +198,16 @@ Type TypeChecker::getOrCreateOpaqueResultType(TypeResolution resolution,
return existingDecl->getDeclaredInterfaceType();
}

// Check the availability of the opaque type runtime support.
auto runningOS = overApproximateAvailabilityAtLocation(repr->getLoc(),
originatingDecl->getInnermostDeclContext());
auto availability = getOpaqueTypeAvailability(Context);
if (!runningOS.isContainedIn(availability)) {
diagnosePotentialOpaqueTypeUnavailability(repr->getSourceRange(),
originatingDecl->getInnermostDeclContext(),
UnavailabilityReason::requiresVersionRange(availability.getOSVersion()));
}

// Try to resolve the constraint repr. It should be some kind of existential
// type.
TypeResolutionOptions options(TypeResolverContext::GenericRequirement);
Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -2008,6 +2008,10 @@ class TypeChecker final : public LazyResolver {
const DeclContext *ReferenceDC,
const UnavailabilityReason &Reason);

void diagnosePotentialOpaqueTypeUnavailability(SourceRange ReferenceRange,
const DeclContext *ReferenceDC,
const UnavailabilityReason &Reason);

/// Emits a diagnostic for a reference to a storage accessor that is
/// potentially unavailable.
void diagnosePotentialAccessorUnavailability(
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/dynamic_replaceable_opaque_return.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend -module-name A -swift-version 5 -primary-file %s -emit-ir | %FileCheck %s
// RUN: %target-swift-frontend -disable-availability-checking -module-name A -swift-version 5 -primary-file %s -emit-ir | %FileCheck %s

// REQUIRES: objc_interop

Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/lazy_opaque_result_type.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=OpaqueArchetypeSpecializer -parse-as-library -module-name=test -O -primary-file %s -emit-ir > %t.ll
// RUN: %target-swift-frontend -disable-availability-checking -Xllvm -sil-disable-pass=OpaqueArchetypeSpecializer -parse-as-library -module-name=test -O -primary-file %s -emit-ir > %t.ll
// RUN: %FileCheck %s < %t.ll

protocol P { }
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/opaque_result_type.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %empty-directory(%t)
// RUN: %{python} %utils/chex.py < %s > %t/opaque_result_type.swift
// RUN: %target-swift-frontend -emit-ir %t/opaque_result_type.swift | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-NODEBUG %t/opaque_result_type.swift
// RUN: %target-swift-frontend -disable-availability-checking -emit-ir %t/opaque_result_type.swift | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-NODEBUG %t/opaque_result_type.swift

public protocol O {
func bar()
Expand Down
4 changes: 2 additions & 2 deletions test/IRGen/opaque_result_type_access_path.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -module-name=test %s -o %t/a.out
// RUN: %target-build-swift -Xfrontend -disable-availability-checking -module-name=test %s -o %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s
// REQUIRES: executable_test
// REQUIRES: CPU=arm64 || CPU=x86_64

// Check that the IRGenMangler does not crashq when mangling a conformance
// Check that the IRGenMangler does not crash when mangling a conformance
// access path with an opaque result type as root.
// As a bonus, also do a runtime test to check that there is no miscompile.

Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/opaque_result_type_debug.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend -g -emit-ir -enable-anonymous-context-mangled-names %s | %FileCheck %s
// RUN: %target-swift-frontend -disable-availability-checking -g -emit-ir -enable-anonymous-context-mangled-names %s | %FileCheck %s

public protocol P {}
extension Int: P {}
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/opaque_result_type_global.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend -emit-ir -verify %s
// RUN: %target-swift-frontend -disable-availability-checking -emit-ir -verify %s

// rdar://problem/49818962
func foo() -> some Collection {
Expand Down
4 changes: 4 additions & 0 deletions test/Interpreter/Inputs/dynamic_replacement_opaque1.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ extension Int: P {

}

@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
func bar(_ x: Int) -> some P {
return x
}

struct Container {
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
func bar(_ x: Int) -> some P {
return x
}

@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
var computedProperty : some P {
get {
return 2
Expand All @@ -27,6 +30,7 @@ struct Container {
}
}

@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
subscript(_ x: Int) -> some P {
get {
return 2
Expand Down
5 changes: 5 additions & 0 deletions test/Interpreter/Inputs/dynamic_replacement_opaque2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,20 @@ struct Pair : P {
}
}

@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
@_dynamicReplacement(for:bar(_:))
func _replacement_bar(y x: Int) -> some P {
return Pair()
}

extension Container {
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
@_dynamicReplacement(for:bar(_:))
func _replacement_bar(y x: Int) -> some P {
return Pair()
}

@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
@_dynamicReplacement(for: computedProperty)
var _replacement_computedProperty : some P {
get {
Expand All @@ -28,6 +31,8 @@ extension Container {
print("replacement \(newValue)")
}
}

@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
@_dynamicReplacement(for: subscript(_:))
subscript(y x: Int) -> some P {
get {
Expand Down
13 changes: 11 additions & 2 deletions test/Interpreter/dynamic_replacement_opaque_result.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ private func target_library_name(_ name: String) -> String {
#endif
}

@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
func test() {
print(MemoryLayout.size(ofValue: bar(5)))
print(MemoryLayout.size(ofValue: Container().bar(5)))
Expand All @@ -51,7 +52,11 @@ func test() {
// CHECK: 2
// CHECK: 8
// CHECK: 2
test()
if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) {
test()
} else {
print("8 8 5 5 8 2 8 2")
}

var executablePath = CommandLine.arguments[0]
executablePath.removeLast(4)
Expand All @@ -72,4 +77,8 @@ executablePath.removeLast(4)
// CHECK: 1
// CHECK: 16
// CHECK: 1
test()
if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) {
test()
} else {
print("16 16 1 1 16 1 16 1")
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func getAssocSubscriptType<T: AssocTypeInference>(_ x: T) -> T.AssocSubscript {
struct MyFoo: Foo {}
struct YourFoo: Foo {}

@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
func someTypeIsTheSame() {
var a = foo(0)
a = foo(0)
Expand Down
Loading