Skip to content

Conversation

@WalterBright
Copy link
Member

@dlang-bot
Copy link
Contributor

Thanks for your pull request, @WalterBright!

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "master + dmd#22171"

{
final int i = 3;
const ref r = i;
const(int)*p = &i;
Copy link
Contributor

Choose a reason for hiding this comment

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

static assert(is(typeof(&i)) == const(int)*));

Copy link
Contributor

Choose a reason for hiding this comment

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

DIP says 'final does not affect the type'. Interestingly, *[&i][0] = 5; gives an error:

finalvar.d(9): Error: cannot implicitly convert final `& i` to `int*`
    *[&i][0] = 5;
      ^

Would be good to add something like that to the test.

@rikkimax
Copy link
Contributor

rikkimax commented Dec 2, 2025

Given that this is running up against existing test cases (failing), it'll need to go behind a preview switch until it's ready & approved.

@WalterBright
Copy link
Member Author

Ran into an unexpected problem. Using __traits(getLinkage, F) to transfer final to a delegate causes problems.

@WalterBright
Copy link
Member Author

I think I can fix it. Tomorrow!

@ntrel
Copy link
Contributor

ntrel commented Dec 2, 2025

void main()
{
    final int[2] a;
    a[0]++; // no error
    final S s;
    s.i++; // no error
}

struct S
{
    int i;
}

The DIP says:

final applied to a declaration of a variable of struct type means the non-static fields of the struct are implicitly final, and cannot be modified after initialization.

It doesn't mention static arrays, but as they are values, the elements should be final IMO.

@thewilsonator thewilsonator added Review:Needs Changelog A changelog entry needs to be added to /changelog Review:Needs Spec PR A PR updating the language specification needs to be submitted to dlang.org Review:Needs Tests labels Dec 3, 2025
@WalterBright
Copy link
Member Author

@ntrel you're right

@WalterBright WalterBright force-pushed the final branch 4 times, most recently from 244704b to e22d4a7 Compare December 3, 2025 09:31
@ntrel
Copy link
Contributor

ntrel commented Dec 3, 2025

Casting a pointer to final should probably be allowed in @system code. It currently errors:

    final int i = 3;
    int* pm = cast(int*) &i; // Error: cannot implicitly convert final `& i` to `int*`

@WalterBright
Copy link
Member Author

As final is local to a function, and does not leak out of it, being able to override it in a function is not needed (one can simply delete the final in that function). It is not so simple with const, which is why that can be overridden in a @System function.

@ntrel
Copy link
Contributor

ntrel commented Dec 4, 2025

As final is local to a function

final can apply to a module scope variable too, and the user of that variable may not have write access to the module where it's defined. I suppose use const tempPtr = &finalVar; then cast away const (e.g. to pass to a non-const function parameter that a particular call doesn't write to).

@WalterBright
Copy link
Member Author

@ntrel yes you can do that in @System code. But you're no worse off if you didn't add the final.

@ibuclaw
Copy link
Member

ibuclaw commented Dec 5, 2025

How does final interact with other types and storage classes?

Structs, arrays, noreturn, ref, etc.

@WalterBright
Copy link
Member Author

How does final interact with other types and storage classes?

All in the same way. Any final variable declaration prevents modification of the contents of the variable.

  • structs: protects the fields
  • classes: protects the reference to the class
  • static arrays: protects the array contents
  • dynamic arrays: protects the pointer/length
  • ref: protects the reference already, final is redundant here
  • out: same as ref
  • delegate: protects the function pointer and context pointer

@WalterBright
Copy link
Member Author

out: same as ref

I'm amending that to 'final' being applied to an out parameter should be an error.

@WalterBright
Copy link
Member Author

The behavior of all these cases is based on the idea that a final object should not be subject to modification after initialization.

@WalterBright WalterBright force-pushed the final branch 4 times, most recently from 1ad6764 to 5c46e7c Compare December 6, 2025 07:57
@ntrel
Copy link
Contributor

ntrel commented Dec 6, 2025

ref: protects the reference already, final is redundant here

When I tried this a few days ago, the final actually applied to the pointed-to data, and I think that is more useful. See https://forum.dlang.org/post/[email protected].

@WalterBright
Copy link
Member Author

@ntrel thanks I will look into that.

@WalterBright WalterBright force-pushed the final branch 2 times, most recently from 95ef9e5 to 93ae869 Compare December 7, 2025 04:53
@WalterBright
Copy link
Member Author

As explained in the ng, I've put this on hold for the time being.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Review:Needs Changelog A changelog entry needs to be added to /changelog Review:Needs Spec PR A PR updating the language specification needs to be submitted to dlang.org Review:Needs Tests Severity:Enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants