Skip to content

Fix type lowering of ~Copyable and ~Escapable generics. #72866

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 4 commits into from
Apr 10, 2024

Conversation

atrick
Copy link
Contributor

@atrick atrick commented Apr 5, 2024

Factor AbstractionPattern::conformsToKnownProtocol and lower ~Escapable using the same logic as ~Copyable.

Adds support for conditionally Escapable enums.

Correctly sets the SILType::isTrivial flags for conditionally escapable structs and enums in environments (extensions) that provide an Escapable conformance, such as:

struct NE<E: ~Escapable> : ~Escapable {}

extension NE: Escapable {
  func foo() -> Self {
    // Self is both Escapable and trivial here.
    self
  }
}

Fixes rdar://125950218 ([nonescapable] support conditionally escapable enums)

@atrick
Copy link
Contributor Author

atrick commented Apr 5, 2024

@swift-ci test

@slavapestov
Copy link
Contributor

slavapestov commented Apr 5, 2024

struct NE<E: ~Escapable> : ~Escapable {}

extension NE: Escapable {
  func foo() -> Self {
    // Self is both Escapable and trivial here.
    self
  }
}

I don't see a test case that covers this specific situation from your PR description -- how can we observe the triviality of self here?

@atrick
Copy link
Contributor Author

atrick commented Apr 5, 2024

struct NE<E: ~Escapable> : ~Escapable {}

extension NE: Escapable {
  func foo() -> Self {
    // Self is both Escapable and trivial here.
    self
  }
}

I don't see a test case that covers this specific situation from your PR description -- how can we observe the triviality of self here?

Joe already wrote tests to check triviality. I reused those by adding ~Escapable

struct MyStruct<T: ~Copyable & ~Escapable>: ~Copyable & ~Escapable
enum MyEnum<T: ~Copyable & ~Escapable>: ~Copyable & ~Escapable

// CHECK-LABEL: sil{{.*}} @{{.*}}13trivialStruct
func trivialStruct() -> Int {
    // CHECK: [[ALLOC:%.*]] = alloc_stack $MyStruct<Trivial>
    // CHECK-NOT: destroy_addr [[ALLOC]] :
    // CHECK: dealloc_stack [[ALLOC]] :
     return MemoryLayout.size(ofValue: MyStruct(x: Trivial.a))
}
// CHECK-LABEL: sil{{.*}} @{{.*}}11trivialEnum
func trivialEnum() -> Int {
    // CHECK: [[ALLOC:%.*]] = alloc_stack $MyEnum<Trivial>
    // CHECK-NOT: destroy_addr [[ALLOC]] :
    // CHECK: dealloc_stack [[ALLOC]] :
     return MemoryLayout.size(ofValue: MyEnum.x(Trivial.a))
}

struct MyAssortment {
    var a: MyStruct<Trivial>
    var b: MyEnum<Trivial>
}

// CHECK-LABEL: sil{{.*}} @{{.*}}4frob
func frob(x: MyAssortment) -> Int {
    // CHECK: [[ALLOC:%.*]] = alloc_stack $MyAssortment
    // CHECK-NOT: destroy_addr [[ALLOC]] :
    // CHECK: dealloc_stack [[ALLOC]] :
    return MemoryLayout.size(ofValue: x)
}

This raises a good point that I was planning to bring up. We have a fair amount of complexity in AbstractionPattern to recognize inverse protocols. But any of that logic could be wrong and we'll never find out about it, at least for ~Escapabe, because the only effect is to set SILType::isTrivial in more cases. If we fail to set isTrivial, codegen is likely still correct and we'll never notice. None of the diagnostics exercise this logic, so it could be inconsistent.

@atrick
Copy link
Contributor Author

atrick commented Apr 6, 2024

@nate-chandler I can't remove the remaining uses of canBeEscapable yet because TypeConverter::visitAggregateLeaves recursively evaluates interface types. This may be as simple as mapping them into a context, but I don't understand this well enough to do it. So, for example:

struct Boxx<each T : _BitwiseCopyable> {
  var ts: (repeat each T)
}

Here, TypeConverter::verifyTrivialLowering is asked to evaluate the type of the ts member, which is a pack type with interface types:

(pack_expansion_type
  (pattern=pack_archetype_type address=0x150962278 conforms_to="Swift.(file)._BitwiseCopyable" name="each T"
    (interface_type=generic_type_param_type depth=0 index=0 decl="testbitwisecopyable.(file).Boxx.T@./testbitwisecopyable.swift:15:18" pack))
  (count=pack_archetype_type address=0x150962278 conforms_to="Swift.(file)._BitwiseCopyable" name="each T"
    (interface_type=generic_type_param_type depth=0 index=0 decl="testbitwisecopyable.(file).Boxx.T@./testbitwisecopyable.swift:15:18" pack)))

That's not going to work with any type system queries, including AbstractionPattern::isNoncopyable or, in this PR, AbstractionPattern::conformsToKnownProtocol.

@nate-chandler
Copy link
Contributor

@atrick Okay, I will address this in a follow-up.

atrick added 4 commits April 7, 2024 20:31
This fixes TypeLowering for ~Copyable generics, such as:

struct S<T: ~Copyable>: ~Copyable {
  var x: T
}

extension S: Copyable where T: Copyable  {}

func foo<T>(s: S<T>) -> ()

Previously, TypeLowering would ignore the implicit Copyable
requirement on the archetype 'T'.
Factor AbstractionPattern::conformsToKnownProtocol and lower ~Escapable using the same logic as ~Copyable.

Adds support for conditionally Escapable enums.

Correctly sets the SILType::isTrivial flags for conditionally escapable structs and enums in environments (extensions)
that provide an Escapable conformance, such as:

    struct NE<E: ~Escapable> : ~Escapable {}

    extension NE: Escapable {
      func foo() -> Self {
        // Self is both Escapable and trivial here.
        self
      }
    }

Fixes rdar://125950218 ([nonescapable] support conditionally escapable enums)
@atrick atrick force-pushed the fix-nonescapable-lowering branch from 4bfceac to 4f0680c Compare April 8, 2024 06:08
@atrick atrick changed the title Fix type lowering of ~Escapable generics. Fix type lowering of ~Copyable and ~Escapable generics. Apr 8, 2024
@atrick
Copy link
Contributor Author

atrick commented Apr 8, 2024

@jckarter I included a fix for AbstractionPattern::isNoncopyable in this PR.

@nate-chandler I included several unit tests involving BitwiseCopyable which, ultimately, should result in trivial lowering. But it's not high enough priority to debug right now, so I left TODOs, which we may want to track in a bug report.

@atrick
Copy link
Contributor Author

atrick commented Apr 8, 2024

@swift-ci test

@nate-chandler
Copy link
Contributor

@atrick As a reminder, having conformance to BitwiseCopyable imply SIL trivial when appropriate (not always) is already tracked by rdar://123661042 and is not a correctness issue.

@atrick
Copy link
Contributor Author

atrick commented Apr 8, 2024

This is currently blocked on a segfault in stdlib/ArrayTraps.swift.gyb on iphonesimulator-x86_64 which I haven't been able to reproduce locally

@atrick
Copy link
Contributor Author

atrick commented Apr 9, 2024

This passes for me locally with the same build-script invocation as CI:
lit ./validation-test-iphonesimulator-x86_64 --filter stdlib/ArrayTraps.swift.gyb -a

@atrick
Copy link
Contributor Author

atrick commented Apr 9, 2024

@swift-ci test

@atrick
Copy link
Contributor Author

atrick commented Apr 9, 2024

@swift-ci smoke test windows

@atrick atrick merged commit 38b78ed into swiftlang:main Apr 10, 2024
@atrick atrick deleted the fix-nonescapable-lowering branch April 10, 2024 04:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants