Language support for deprecation

According to a quick search of the zig repo, there are 27 files with /// Deprecated in them, most from the standard library.

Given that zig aims to enable maintainable software, what do you think about language features regarding deprecation?

An obvious non-language-level utility would be a third-party linter looking for Deprecated in doc comments.

What existing features of zig aid the maintainability of libraries and user code through deprecation cycles?

Some existing semi-related maintainability features I have found nice:

  1. User code (and library code when refactoring) sometimes doesn’t need to change very much when types change from pointers to values, since fields of struct pointers can be directly accessed with ., like my_struct_pointer.my_field, which contrasts with C requiring the -> (arrow) operator.
  2. An enum type can be changed to a tagged union typically without changes to user code in switches.
3 Likes

Doesn’t need to be a language feature I think? Spitballing, the user code can do

pub const std_options = .{
    .deprecation_warnings = true,
};

and then the library could do

pub fn a_deprecated_function() {
    comptime if (@import("root").std_options.deperecation_warnings) {
        @compileLog("'a_deprecated_function' is deprecated, use 'a_better_function' instead")
    }
}

Would love for std to do this actually, it should ease Zig upgrades quite a bit!

28 Likes

of course, status quo zig cannot do this bc std.Options does not have a deprecation_warnings field.

user code can do something like this today though:

const root = @import("root");
pub const aDeprecatedFunction = if (@hasDecl(root, "deprecation_warnings") and root.deprecation_warnings) {
    @compileError("'aDeprecatedFunction() is deprecated, use aBetterName() instead!")
} else aBetterName;

pub fn aBetterName() { ... }

ETA – could also make “deprecation_warnings” an options module in one’s build.zig so that users of a library could opt into them as part of the build process.

I think for user code a build option is the way to go. If deprecation_warnings does become a std option, then it might make sense to use an enum instead of a bool:

const DeprecationWarning = enum {
    ignore,
    compile_log,
    use_std_option,
};

Then default to .use_std_option if the user doesn’t pass in anything.

Although, I’m not sure when you would want to see only a subset of the deprecations.

1 Like

There is now a proposal for a @deprecated() builtin.

3 Likes

As long as depreciation marking includes zig fmt automation when feasible!

I wonder if instead of another builtin just to mark something deprecated it wouldn’t be better to add a builtin to attach an attribute, and the “deprecated” can just be an attribute value then.

I wonder if instead of another builtin just to mark something deprecated it wouldn’t be better to add a builtin to attach an attribute, and the “deprecated” can just be an attribute value then.

That sounds similar to various pragma/attribute issues, all of which I believe are rejected or closed.

Here’s one such issue, which has deprecation as one of the examples: Proposal: Pragmas · Issue #5239 · ziglang/zig · GitHub

@deprecated() has been merged, looking forward to trying it out! (… though not saying I look forward to writing code that I later have to remove, slowly and painfully, from my non-existent users…)

5 Likes

Do you have a reference as to where or when it was merged?

I can’t find deprec in the 0.15.2 nor the 0.16.0-dev documentation.

i think it was reversed due to some issues and hasnt been fixed since

2 Likes

My (uninformed) guess is that it’s related to the same issues with lazy evaluation semantics that led to the std.Options split between fields and decls.

What happened is that I made an initial implementation based on the semantics in my original proposal. Andrew then suggested some changes which improved the utility of the feature, but had the side effect of making more complicated to implement correctly. Initially I didn’t realize the implications behind certain changes and so we merged an implementation that was not correct, which then got reverted.

12 Likes

I’d like to revive this topic as I’ve become increasingly annoyed to the fact that deprecations are very hard to follow unless you are 100% up-to-date every tiny zig std development.

It’s all thanks to zls that I sometimes catch up to them:

6 Likes

I sense an opportunity for a third-party project here — “The Big Zig Migrator” tool, a cli that by hook or by crook helps migrate real world codebases, going beyond zig fmt. Doing things like spotting deprecations, rewriting the code to be compatible before updating Zig version, maybe supporting custom user-defined rewrite rules a-al ast-grep.

4 Likes

Apparently should be able to detect deprecations (at least in std?)
I wish https://github.com/ziglang/zig/issues/21525 was solved though, so I don’t have to do zig build -Dallow-lint-dep=true lint so it can be fetched only when required.

EDIT: I’ve been enjoying zlinter now. Very nice project!

1 Like

This sounds a lot like //go:fix inline to me, which is a concept that I would welcome in Zig if there was such a thing as zig fix.

This is tooling that can be easily made and added to your build.