Skip to content

NCGenerics: fix partial backdeployment support #73328

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

Closed
wants to merge 4 commits into from
Closed
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 include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -6735,6 +6735,11 @@ ERROR(availability_isolated_any_only_version_newer, none,
"%0 %1 or newer",
(StringRef, llvm::VersionTuple))

WARNING(availability_noncopyable_generics_only_version_newer, none,
"runtime support for '~' suppressions on generic parameters is only available in "
"%0 %1 or newer",
(StringRef, llvm::VersionTuple))

ERROR(availability_variadic_type_only_version_newer, none,
"parameter packs in generic types are only available in %0 %1 or newer",
(StringRef, llvm::VersionTuple))
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/FeatureAvailability.def
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ FEATURE(SwiftExceptionPersonality, (6, 0))
// Metadata support for @isolated(any) function types
FEATURE(IsolatedAny, (6, 0))

FEATURE(NoncopyableGenerics, (6, 0))

FEATURE(TaskExecutor, FUTURE)
FEATURE(Differentiation, FUTURE)
FEATURE(InitRawStructMetadata, FUTURE)
Expand Down
27 changes: 27 additions & 0 deletions include/swift/Sema/CheckAvailability.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//===--- CheckAvailability.h - CheckAvailability ----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_SEMA_CHECKAVAILABILITY_H
#define SWIFT_SEMA_CHECKAVAILABILITY_H

namespace swift {
class NominalOrBoundGenericNominalType;

/// NoncopyableGenerics, which is the generics system for inverses, does not
/// always require the runtime metadata that is only availabile in Swift 6.
///
/// \returns true if the given type needs the newer runtime.
bool requiresNoncopyableGenericsAvailabilityCheck(
NominalOrBoundGenericNominalType *type);
}

#endif //SWIFT_SEMA_CheckAVAILABILITY_H
10 changes: 10 additions & 0 deletions lib/IRGen/GenReflection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "swift/IRGen/Linking.h"
#include "swift/RemoteInspection/MetadataSourceBuilder.h"
#include "swift/RemoteInspection/Records.h"
#include "swift/Sema/CheckAvailability.h"
#include "swift/SIL/SILModule.h"

#include "ConstantBuilder.h"
Expand Down Expand Up @@ -226,6 +227,14 @@ getRuntimeVersionThatSupportsDemanglingType(CanType type) {
// involving them.
}

// Any nominal type that has an inverse requirement in its generic signature
// uses NoncopyableGenerics. We can support some cases where the type has
// bound generic arguments that conform to all invertible protocols.
if (auto nominalTy = dyn_cast<NominalOrBoundGenericNominalType>(t)) {
if (requiresNoncopyableGenericsAvailabilityCheck(nominalTy))
return addRequirement(Swift_6_0);
}

return false;
});

Expand Down Expand Up @@ -358,6 +367,7 @@ getTypeRefByFunction(IRGenModule &IGM,
Address(bindingsBufPtr, IGM.Int8Ty, IGM.getPointerAlignment()),
MetadataState::Complete, subs);

substT = substT.getReferenceStorageReferent(); // FIXME: shot in the dark here
auto ret = IGF.emitTypeMetadataRef(substT);
IGF.Builder.CreateRet(ret);
}
Expand Down
70 changes: 70 additions & 0 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "swift/Parse/Lexer.h"
#include "swift/Parse/Parser.h"
#include "swift/Sema/IDETypeChecking.h"
#include "swift/Sema/CheckAvailability.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/SaveAndRestore.h"
Expand Down Expand Up @@ -3023,6 +3024,68 @@ static bool diagnoseIsolatedAnyAvailability(
ReferenceDC);
}

static bool diagnoseNoncopyableGenericsAvailability(
SourceRange ReferenceRange, const DeclContext *ReferenceDC) {
// Do not check availability in the stdlib.
// FIXME: would be better if we had an attribute on each decl that opt's out
// of availability checking in the stdlib, because it's been audited or
// automatically checked to ensure it doesn't use the metadata.
// if (ReferenceDC->getParentModule()->isStdlibModule())
// return false;

return TypeChecker::checkAvailability(
ReferenceRange,
ReferenceDC->getASTContext().getNoncopyableGenericsAvailability(),
diag::availability_noncopyable_generics_only_version_newer,
ReferenceDC);
}

static bool inverseGenericsOldRuntimeCompatable(BoundGenericType *boundTy) {
for (auto arg : boundTy->getGenericArgs()) {
if (arg->hasTypeParameter() || arg->hasUnboundGenericType())
return false;

// Make sure the argument conforms to all known invertible protocols.
for (auto ip : InvertibleProtocolSet::allKnown()) {
switch (ip) {
case InvertibleProtocolKind::Copyable:
if (arg->isNoncopyable())
return false;
break;
case InvertibleProtocolKind::Escapable:
if (!arg->isEscapable())
return false;
}
}
}
return true;
}

bool swift::requiresNoncopyableGenericsAvailabilityCheck(
NominalOrBoundGenericNominalType *nominalTy) {
auto *nom = nominalTy->getDecl();
if (auto sig = nom->getGenericSignature()) {
SmallVector<InverseRequirement, 2> inverses;
SmallVector<Requirement, 2> reqs;
sig->getRequirementsWithInverses(reqs, inverses);
if (!inverses.empty()) {
// If the nominal is a bound generic type, and all arguments are
// conform to all invertible protocols, then older runtimes that will
// not check for those conformances don't actually need to, since we
// have already checked it now.
if (auto boundTy = dyn_cast<BoundGenericType>(nominalTy)) {
if (!inverseGenericsOldRuntimeCompatable(boundTy))
return true;
} else {
// It's generic, not bound, and has an inverse on a generic
// parameter, so we need the appropriate runtime.
return true;
}
}
}
return false;
}

static bool checkTypeMetadataAvailabilityInternal(CanType type,
SourceRange refLoc,
const DeclContext *refDC) {
Expand All @@ -3033,6 +3096,13 @@ static bool checkTypeMetadataAvailabilityInternal(CanType type,
auto isolation = fnType->getIsolation();
if (isolation.isErased())
return diagnoseIsolatedAnyAvailability(refLoc, refDC);
// } else if (auto nominalTy = dyn_cast<NominalOrBoundGenericNominalType>(type)) {
// if (requiresNoncopyableGenericsAvailabilityCheck(nominalTy))
// return diagnoseNoncopyableGenericsAvailability(refLoc, refDC);
//// llvm_unreachable("did hit this case!");
} else if (auto archeType = dyn_cast<ArchetypeType>(type)) {
if (archeType->isNoncopyable())
return diagnoseNoncopyableGenericsAvailability(refLoc, refDC);
}
return false;
});
Expand Down
69 changes: 69 additions & 0 deletions test/Backdeploy/Noncopyable/mangling.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// RUN: %empty-directory(%t)
// RUN: %swift-frontend %s -swift-version 6 -module-name main -emit-ir -o %t/new.ir
// RUN: %FileCheck %s --check-prefix=NEW < %t/new.ir
// RUN: %target-swift-frontend %s -target %target-cpu-apple-macosx10.15 -module-name main -emit-ir -o %t/old.ir
// RUN: %FileCheck %s --check-prefix=OLD < %t/old.ir

// Check that we add extra type metadata accessors for types with generic
// parameters that have an inverse. These are used instead of using demangling
// cache variables since old runtimes cannot synthesize type metadata based on
// the new mangling.

// RUN: %target-build-swift -target %target-cpu-apple-macosx10.15 %s -o %t/test_mangling
// RUN: %target-run %t/test_mangling | %FileCheck %s

// REQUIRES: OS=macosx
// REQUIRES: executable_test


// This type's generic parameter is noncopyable, so older runtimes can't
// demangle the type's name to build the metadata.
struct Foo<T: ~Copyable>: ~Copyable {
mutating func bar() { print("Foo.bar") }
}

func test() {
var foo = Foo<Int>()
foo.bar()
}
test()
// CHECK: Foo.bar

// NEW: define hidden swiftcc void @"$s4main4testyyF"()
// NEW-NOT: %swift.metadata_response
// NEW: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s4main3FooVySiGMD")
// NEW-NOT: %swift.metadata_response
// NEW: }

// OLD: define hidden swiftcc void @"$s4main4testyyF"()
// OLD-NOT: __swift_instantiateConcreteTypeFromMangledName
// OLD: call swiftcc %swift.metadata_response @"$s4main3FooVySiGMa"(i64 0)
// OLD-NOT: __swift_instantiateConcreteTypeFromMangledName
// OLD: }


// This type does not need a Swift 6.0 runtime, despite being noncopyable,
// because it doesn't have a noncopyable generic parameter.
struct JustNoncopyable<T>: ~Copyable {
mutating func bar() { print("JustNoncopyable.bar") }
}

func testNonGeneric() {
var ng = JustNoncopyable<Int>()
ng.bar()
}
testNonGeneric()

// CHECK: JustNoncopyable.bar

// NEW: define hidden swiftcc void @"$s4main14testNonGenericyyF"()
// NEW-NOT: %swift.metadata_response
// NEW: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s4main15JustNoncopyableVySiGMD")
// NEW-NOT: %swift.metadata_response
// NEW: }

// OLD: define hidden swiftcc void @"$s4main14testNonGenericyyF"()
// OLD-NOT: %swift.metadata_response
// OLD: call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s4main15JustNoncopyableVySiGMD")
// OLD-NOT: %swift.metadata_response
// OLD: }