Skip to content

[SILGen/DI] InitAccessors: Fix handling of nonmutating set when type has other stored properties #68875

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
Oct 2, 2023

Conversation

xedin
Copy link
Contributor

@xedin xedin commented Sep 29, 2023

The original change supported only the case where all stored properties were controlled
by an init accessor but when other fields were involved SILGen would emitted copies of
loaded "self" for each setter application which is redundant and due to the fact that the
setter was escaping.

These changes make partial application of the setter non-escaping which means that
"self" can be borrowed instead of copied.

Resolves: #67827

The closure with applied base is not escaping and gets applied
only once (when self is fully initialized). Let's make sure that
the partial application results in on-stack closure that borrows
"self" instead of copying it.
…sign_by_wrapper`

For cases where init accessor field has a nonmutating set we need
ignore copies and borrows associated with load of "self" because
they are going to be erased together with the setter application
by DI.
@xedin xedin requested a review from jckarter September 29, 2023 20:25
@xedin
Copy link
Contributor Author

xedin commented Sep 29, 2023

@swift-ci please test

@xedin
Copy link
Contributor Author

xedin commented Sep 29, 2023

@swift-ci please test source compatibility

@xedin
Copy link
Contributor Author

xedin commented Sep 29, 2023

@swift-ci please build toolchain macOS

@xedin
Copy link
Contributor Author

xedin commented Sep 30, 2023

@swift-ci please test source compatibility release

@xedin
Copy link
Contributor Author

xedin commented Sep 30, 2023

@swift-ci please test source compatibility debug

@xedin
Copy link
Contributor Author

xedin commented Sep 30, 2023

This failure in distributed actors looks unrelated but I am going to wait for main branch suite run to complete.

@xedin xedin merged commit 829694f into swiftlang:main Oct 2, 2023
@mansbernhardt
Copy link

Thanks @xedin for fixing this. Would it be possible to get this fix into the 5.9.x branch/release as well?

@xedin
Copy link
Contributor Author

xedin commented Oct 3, 2023

Unfortunately I think it might be too risky of a change for 5.9.x but I'll see what I can do.

@mansbernhardt
Copy link

Hello @xedin, I tried your fix out in the latest Xcode 15.3 beta (15E5178i), and it seem to work fine for the examples I reported, but if I change them slightly to include e.g. let constant it fails to compile again Variable 'self._count' used before being initialized:

struct Test {
    let id: UUID
    private var _count: Int

    var count: Int
    {
        @storageRestrictions(initializes: _count)
        init {
            _count = newValue
        }
        get {
            _count
        }
        nonmutating set {
            // Update store
        }
    }

    init(id: UUID, count: Int) {
        self.id = id
        self.count = count // << Variable 'self._count' used before being initialized
    }
}

@xedin
Copy link
Contributor Author

xedin commented Jan 29, 2024

I will take a look!

@xedin
Copy link
Contributor Author

xedin commented Jan 29, 2024

Hm, var id doesn't work either, var and let result in the same SIL so it must be something in DI that I haven't accounted for.

@xedin
Copy link
Contributor Author

xedin commented Jan 29, 2024

This appears to be related to the UUID, if I change the type of id to String both let and var succeed.

@mansatgigset
Copy link

Interesting. Switching UUID to Date also fails.

@xedin
Copy link
Contributor Author

xedin commented Jan 29, 2024

Right, I think both Date and UUID are big enough to force indirect access to the Test, this is something we didn't account for because for that a copy of Test has to be allocated.

@xedin
Copy link
Contributor Author

xedin commented Jan 30, 2024

I think I figured it out, the problem is not actually SILGen, it's Sema because it generates a load from "self" of count though it shouldn't be doing that. There is a special handling of that for property wrappers but not for init accessors, I should be able to fix this tomorrow.

@xedin
Copy link
Contributor Author

xedin commented Jan 30, 2024

Here is the fix - #71248

@mansbernhardt
Copy link

mansbernhardt commented Jun 26, 2024

@xedin I tested the above with the latest Xcode beta, and it seems to work, but if I modify it slightly it still fails with the same error (the below is a closer match to how my macro is generating its code).

struct Test {
  let id: UUID
  private var _count: Int

  var count: Int
  {
    @storageRestrictions(initializes: _count)
    init {
      _count = newValue
    }
    _read {
      yield _count
    }
    nonmutating _modify {
      var val = _count
      yield &val
    }
  }

  init(id: UUID, count: Int) {
    self.id = id
    self.count = count // << Variable 'self._count' used before being initialized
  } // << Return from initializer without initializing all stored properties
}

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.

Init accessor with non mutating set results in compiler error
4 participants