Skip to content

[Proposal] Stat for Swift System #257

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft

Conversation

jrflat
Copy link
Contributor

@jrflat jrflat commented Aug 12, 2025

This proposal introduces a Swift-native Stat type to the System library, providing comprehensive access to file metadata on Unix-like platforms through type-safe, platform-aware APIs that wrap the C stat types and system calls.

Read full proposal...

}
```

These initializers use a typed `throws(Errno)` and require Swift 6.0 or later.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's typically not recommended to use typed throws in public API (or ABI, which impacts Darwin). Are you sure this is the right choice always and forever?

/// The corresponding C constant is `S_IFSOCK`.
public static var socket: FileType { get }

#if SYSTEM_PACKAGE_DARWIN || os(FreeBSD)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reasoning for using ifdef vs @available(OpenBSD, unavailable), etc., for the platforms where it isn't available? Might provide a nicer developer experience.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we're able to do that since the constant itself won't be available on those platforms, so the implementation will fail to build unless we ifdef the constant out. Maybe we could define the constant to be some bogus value on platforms where it doesn't exist, and then mark the public API unavailable? If I'm missing a common pattern for this please let me know and I'd be happy to look into it!

It would also help if there were a @available(Darwin, unavailable) to avoid doing it for each platform I think.

/// Time of last access, given as a `UTCClock.Instant`
///
/// The corresponding C property is `st_atim` (or `st_atimespec` on Darwin).
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should probably list visionOS availability for completeness?

Copy link
Contributor Author

@jrflat jrflat Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah thanks yeah I'll make sure to do that in the future. I'm actually planning to remove these particular availability annotations since they're accidentally left over from when I used Duration here. I'll need to update it with the appropriate UTCClock availability when that proposal is settled.

Combining `Stat` and `StatFS` (separate proposal) into a single type was considered but rejected because file and file system information serve different purposes and are typically needed in different contexts. Storing and/or initializing both `stat` and `statfs` structs unnecessarily reduces performance when one isn't needed.

### Making `Stat` available on Windows
It's possible to make `Stat` available on Windows and use either the non-native `_stat` functions from CRT or populate the information via a separate `GetFileInformation` call. However, many of the `stat` fields are not- or less-applicable on Windows and are treated as such by `_stat`. For instance, `st_uid` is always zero on Windows, `st_ino` has no meaning in FAT, HPFS, or NTFS file systems, and `st_mode` can only specify a regular file, directory, or character special, with the executable bit depending entirely on the file's path extension.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree with the rationale about many of the fields being not applicable or less applicable on Windows. Windows clearly provides and meticulously documents the POSIX-like wrappers like _stat, _wstat, and so on, to make porting existing code easier, so why not use them? I don't think it's an either/or thing -- we could make Stat available on Windows and create Windows-specific types that give developers full access to platform-native file metadata.

I think whether third party developers want to use the POSIX-ish family or functions or the Win32 functions is a choice they should decide for themselves, not one Swift System should effectively make for them.

Letting the Stat type wrap the POSIX functions on Windows provides the best of both worlds if we also provide a Win32-level abstraction later.

The Win32 API surface is also pretty large, and I wonder if general Win32 abstractions would be better handled by apinotes that we distribute with the Swift for Windows installer to project those APIs into more idiomatic Swift, rather than wrapper APIs in a package like this one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that Windows's documentation of _stat et al. is complete, but it's not particularly intuitive. I think using _stat for Windows would then put the onus on System for properly documenting and explaining all the (possibly unexpected) differences. I sympathize with giving third party developers the ability to choose, but I worry this would inherently make the Stat type more confusing. It also clashes a bit with System's "no abstraction" philosophy.

We could always re-visit this in the future if there's a demonstrated desire for supporting the POSIX-like wrappers on Windows. I'm also happy to hear ideas on how we could make the documentation around that more clear.

Using apinotes for the vast array of Win32 APIs is definitely an interesting idea I'd like to learn more about. I wonder if some of the more basic ones like GetFileInformationByHandle could still be useful to have in System, though.

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.

2 participants