Field | Value |
---|---|
DIP: | (number/id -- assigned by DIP Manager) |
Author: | Richard (Rikki) Andrew Cattermole [email protected] |
Implementation: | dlang/dmd#16161 |
Status: | Draft |
The member of the operator performs a get member operation on a contextual symbol to obtain either a type or a value.
- Rationale
- Prior Work
- Description
- Breaking Changes and Deprecations
- Reference
- Copyright & License
- History
Having a way to specify that an identifier is to be used in lookup given a context is useful both as a syntax sugar for aiding in using D, but it also offers a way to provide a tag for a sumtype or value type exception which is not backed by the type system.
Previously it has been desired to have a member-of-operator for context-aware lookups and it resulted in an ideas thread.
The typing behaviour has a known possible origin from Neat. This is called a symbol identifier or identifier type. It can be used for sumtypes to differentiate same value but different tag types. It also allows for an element to not have a value type associated with it.
The member of operator, is an operator that operates on a contextual symbol with respect to a given statement, declaration or expression.
It may appear only as the first term in an expression.
The syntax of the operator is ':' Identifier
.
It is commonly implemented by doing a rewrite to: context.Identifier
. Where the context is a symbol which was provided by the usage syntax.
CmpExpression:
+ MemberOfOperator
+ MemberOfOperator:
+ : Identitifer
It is given a non-unique type, when a type is taken of it, of size zero, and cannot have a pointer taken of it.
static assert(typeof(:Identifier).sizeof == 0);
The non-uniqueness of the type dictates that no meta-data is to be associated with it that is not by value.
The only valid value is the initializer as it is no-op.
Taking a string of the type will result in the identifier.
static assert(typeof(:Identifier).stringof == "Identifier");
The type that the member of the operator results in is the same as the one it is in the context of.
If it does not match, it will be an error.
enum E {
First
}
E e = :First; // ok
struct S {
enum Second = S.init;
int field;
}
S s1 = :Second; // ok
S s2;
s2 = :field; // Error: Type of the field `field` on `s2` has type `int` not `S`
- Return expressions
The compiler rewrites
return :Identifier;
asreturn typeof(return).Identifier;
. - Variable declarations
Type qualifiers may not appear as the variable type, there must be a concrete type.
The type of the variable declaration acts as the context.
Instead of:
alias Type = int; Type var = Type.min;
int var = :min;
. - Switch statements
The expression used by the switch statement will need to be aliased as per variable declarations.
So
would be rewritten as
switch(expr) { case :Identifier: break; }
alias __Alias = typeof(expr); switch(expr) { case __Alias.Identifier: break; }
- Function calls
During parameter-to-argument matching, a check to see if the
typeof(param).Identifier
is possible forfunc(:Identifier)
. - Function parameter default initialization
It must support the default initialization of a parameter.
void func(Enum e = :Start)
. - Comparison
The left-hand side type of a comparison is used as the context for the right-hand side
e == :Start
. This may require an intermediary variable to get the type of, prior to the comparison.
There are no expected breaking changes. It is not expected to conflict with any existing D syntax.
It does however conflict with inline assembly for GDC, but this can be worked around within the implementation and is not a concern.
- https://neat-lang.github.io/manual.html#symbol-identifier
- https://forum.dlang.org/post/[email protected]
Copyright (c) 2024 by the D Language Foundation
Licensed under Creative Commons Zero 1.0
The DIP Manager will supplement this section with links to forum discussions and a summary of the formal assessment.