www.digitalmars.com
Last update Mon Jun 19 11:50:11 2006

Expressions

C and C++ programmers will find the D expressions very familiar, with a few interesting additions.

Expressions are used to compute values with a resulting type. These values can then be assigned, tested, or ignored. Expressions can also have side effects.

Expression:
	AssignExpression
	AssignExpression , Expression

AssignExpression:
	ConditionalExpression
	ConditionalExpression = AssignExpression
	ConditionalExpression += AssignExpression
	ConditionalExpression -= AssignExpression
	ConditionalExpression *= AssignExpression
	ConditionalExpression /= AssignExpression
	ConditionalExpression %= AssignExpression
	ConditionalExpression &= AssignExpression
	ConditionalExpression |= AssignExpression
	ConditionalExpression ^= AssignExpression
	ConditionalExpression ~= AssignExpression
	ConditionalExpression <<= AssignExpression
	ConditionalExpression >>= AssignExpression
	ConditionalExpression >>>= AssignExpression

ConditionalExpression:
	OrOrExpression
	OrOrExpression ? Expression : ConditionalExpression

OrOrExpression:
	AndAndExpression
	OrOrExpression || AndAndExpression

AndAndExpression:
	OrExpression
	AndAndExpression && OrExpression

OrExpression:
	XorExpression
	OrExpression | XorExpression

XorExpression:
	AndExpression
	XorExpression ^ AndExpression

AndExpression:
	EqualExpression
	AndExpression & EqualExpression

EqualExpression:
	RelExpression
	EqualExpression == RelExpression
	EqualExpression != RelExpression
	EqualExpression is RelExpression
	EqualExpression !is RelExpression

RelExpression:
	ShiftExpression
	InExpression
	RelExpression < ShiftExpression
	RelExpression <= ShiftExpression
	RelExpression > ShiftExpression
	RelExpression >= ShiftExpression
	RelExpression !<>= ShiftExpression
	RelExpression !<> ShiftExpression
	RelExpression <> ShiftExpression
	RelExpression <>= ShiftExpression
	RelExpression !> ShiftExpression
	RelExpression !>= ShiftExpression
	RelExpression !< ShiftExpression
	RelExpression !<= ShiftExpression

InExpression:
	RelExpression in ShiftExpression

ShiftExpression:
	AddExpression
	ShiftExpression << AddExpression
	ShiftExpression >> AddExpression
	ShiftExpression >>> AddExpression

AddExpression:
	MulExpression
	AddExpression + MulExpression
	AddExpression - MulExpression
	AddExpression ~ MulExpression

MulExpression:
	UnaryExpression
	MulExpression * UnaryExpression
	MulExpression / UnaryExpression
	MulExpression % UnaryExpression

UnaryExpression:
	PostfixExpression
	& UnaryExpression
	++ UnaryExpression
	-- UnaryExpression
	* UnaryExpression
	- UnaryExpression
	+ UnaryExpression
	! UnaryExpression
	~ UnaryExpression
	delete UnaryExpression
	NewExpression
	CastExpression
	NewAnonClassExpression
	( Type ) . Identifier

CastExpression:
	cast ( Type ) UnaryExpression

PostfixExpression:
	PrimaryExpression
	PostfixExpression . Identifier
	PostfixExpression . NewExpression
	PostfixExpression ++
	PostfixExpression --
	PostfixExpression ( )
	PostfixExpression ( ArgumentList )
	IndexExpression
	SliceExpression

IndexExpression:
	PostfixExpression [ ArgumentList ]

SliceExpression:
	PostfixExpression [ ]
	PostfixExpression [ AssignExpression .. AssignExpression ]

PrimaryExpression:
	Identifier
	.Identifier
	this
	super
	null
	true
	false
	NumericLiteral
	CharacterLiteral
	StringLiterals
	FunctionLiteral
	AssertExpression
	BasicType . Identifier
	typeid ( Type )
	IsExpression
	( Expression )

StringLiterals:
	StringLiteral
	StringLiterals StringLiteral

AssertExpression:
	assert ( Expression )
	assert ( Expression , Expression )

ArgumentList:
	AssignExpression
	AssignExpression , ArgumentList

NewExpression:
	NewArguments Type [ AssignExpression ]
	NewArguments Type ( ArgumentList )
	NewArguments Type
	NewArguments ClassArguments BaseClasslistopt { DeclDefs } 

NewArguments:
	new ( ArgumentList )
	new ( )
	new

ClassArguments:
	class ( ArgumentList )
	class ( )
	class

Evaluation Order

Unless otherwise specified, the implementation is free to evaluate the components of an expression in any order. It is an error to depend on order of evaluation when it is not specified. For example, the following are illegal:
i = ++i;
c = a + (a = b);
func(++i, ++i);
If the compiler can determine that the result of an expression is illegally dependent on the order of evaluation, it can issue an error (but is not required to). The ability to detect these kinds of errors is a quality of implementation issue.

Expressions

AssignExpression , Expression
The left operand of the , is evaluated, then the right operand is evaluated. The type of the expression is the type of the right operand, and the result is the result of the right operand.

Assign Expressions

ConditionalExpression = AssignExpression
The right operand is implicitly converted to the type of the left operand, and assigned to it. The result type is the type of the lvalue, and the result value is the value of the lvalue after the assignment.

The left operand must be an lvalue.

Assignment Operator Expressions

ConditionalExpression += AssignExpression
ConditionalExpression -= AssignExpression
ConditionalExpression *= AssignExpression
ConditionalExpression /= AssignExpression
ConditionalExpression %= AssignExpression
ConditionalExpression &= AssignExpression
ConditionalExpression |= AssignExpression
ConditionalExpression ^= AssignExpression
ConditionalExpression <<= AssignExpression
ConditionalExpression >>= AssignExpression
ConditionalExpression >>>= AssignExpression
Assignment operator expressions, such as:
a op= b
are semantically equivalent to:
a = a op b
except that operand a is only evaluated once.

Conditional Expressions

OrOrExpression ? Expression : ConditionalExpression
The first expression is converted to bool, and is evaluated. If it is true, then the second expression is evaluated, and its result is the result of the conditional expression. If it is false, then the third expression is evaluated, and its result is the result of the conditional expression. If either the second or third expressions are of type void, then the resulting type is void. Otherwise, the second and third expressions are implicitly converted to a common type which becomes the result type of the conditional expression.

OrOr Expressions

OrOrExpression || AndAndExpression
The result type of an OrOrExpression is bool, unless the right operand has type void, when the result is type void.

The OrOrExpression evaluates its left operand. If the left operand, converted to type bool, evaluates to true, then the right operand is not evaluated. If the result type of the OrOrExpression is bool then the result of the expression is true. If the left operand is false, then the right operand is evaluated. If the result type of the OrOrExpression is bool then the result of the expression is the right operand converted to type bool.

AndAnd Expressions

AndAndExpression && OrExpression

The result type of an AndAndExpression is bool, unless the right operand has type void, when the result is type void.

The AndAndExpression evaluates its left operand.

If the left operand, converted to type bool, evaluates to false, then the right operand is not evaluated. If the result type of the AndAndExpression is bool then the result of the expression is false.

If the left operand is true, then the right operand is evaluated. If the result type of the AndAndExpression is bool then the result of the expression is the right operand converted to type bool.

Bitwise Expressions

Bit wise expressions perform a bitwise operation on their operands. Their operands must be integral types. First, the default integral promotions are done. Then, the bitwise operation is done.

Or Expressions

OrExpression | XorExpression
The operands are OR'd together.

Xor Expressions

XorExpression ^ AndExpression
The operands are XOR'd together.

And Expressions

AndExpression & EqualExpression
The operands are AND'd together.

Equality Expressions

EqualExpression == RelExpression
EqualExpression != RelExpression
Equality expressions compare the two operands for equality (==) or inequality (!=). The type of the result is bool. The operands go through the usual conversions to bring them to a common type before comparison.

If they are integral values or pointers, equality is defined as the bit pattern of the type matches exactly. Equality for struct objects means the bit patterns of the objects match exactly (the existence of alignment holes in the objects is accounted for, usually by setting them all to 0 upon initialization). Equality for floating point types is more complicated. -0 and +0 compare as equal. If either or both operands are NAN, then both the == returns false and != returns true. Otherwise, the bit patterns are compared for equality.

For complex numbers, equality is defined as equivalent to:

x.re == y.re && x.im == y.im
and inequality is defined as equivalent to:
x.re != y.re || x.im != y.im
For class and struct objects, the expression (a == b) is rewritten as a.opEquals(b), and (a != b) is rewritten as !a.opEquals(b).

For static and dynamic arrays, equality is defined as the lengths of the arrays matching, and all the elements are equal.

Identity Expressions

EqualExpression is RelExpression
EqualExpression !is RelExpression
The is compares for identity. To compare for not identity, use e1 !is e2. The type of the result is bool. The operands go through the usual conversions to bring them to a common type before comparison.

For operand types other than class objects, static or dynamic arrays, identity is defined as being the same as equality.

For class objects, identity is defined as the object references are for the same object. Null class objects can be compared with is.

For static and dynamic arrays, identity is defined as referring to the same array elements.

The identity operator is cannot be overloaded.

Relational Expressions

RelExpression < ShiftExpression
RelExpression <= ShiftExpression
RelExpression > ShiftExpression
RelExpression >= ShiftExpression
RelExpression !<>= ShiftExpression
RelExpression !<> ShiftExpression
RelExpression <> ShiftExpression
RelExpression <>= ShiftExpression
RelExpression !> ShiftExpression
RelExpression !>= ShiftExpression
RelExpression !< ShiftExpression
RelExpression !<= ShiftExpression
First, the integral promotions are done on the operands. The result type of a relational expression is bool.

For class objects, the result of Object.opCmp() forms the left operand, and 0 forms the right operand. The result of the relational expression (o1 op o2) is:

(o1.opCmp(o2) op 0)
It is an error to compare objects if one is null.

For static and dynamic arrays, the result of the relational op is the result of the operator applied to the first non-equal element of the array. If two arrays compare equal, but are of different lengths, the shorter array compares as "less" than the longer array.

Integer comparisons

Integer comparisons happen when both operands are integral types.

Integer comparison operators
OperatorRelation
< less
> greater
<= less or equal
>= greater or equal
== equal
!= not equal

It is an error to have one operand be signed and the other unsigned for a <, <=, > or >= expression. Use casts to make both operands signed or both operands unsigned.

Floating point comparisons

If one or both operands are floating point, then a floating point comparison is performed.

Useful floating point operations must take into account NAN values. In particular, a relational operator can have NAN operands. The result of a relational operation on float values is less, greater, equal, or unordered (unordered means either or both of the operands is a NAN). That means there are 14 possible comparison conditions to test for:

Floating point comparison operators
Operator Greater Than Less Than Equal Unordered Exception Relation
== F F T F no equal
!= T T F T no unordered, less, or greater
> T F F F yes greater
>= T F T F yes greater or equal
< F T F F yes less
<= F T T F yes less or equal
!<>= F F F T no unordered
<> T T F F yes less or greater
<>= T T T F yes less, equal, or greater
!<= T F F T no unordered or greater
!< T F T T no unordered, greater, or equal
!>= F T F T no unordered or less
!> F T T T no unordered, less, or equal
!<> F F T T no unordered or equal

Notes:

  1. For floating point comparison operators, (a !op b) is not the same as !(a op b).
  2. "Unordered" means one or both of the operands is a NAN.
  3. "Exception" means the Invalid Exception is raised if one of the operands is a NAN. It does not mean an exception is thrown. The Invalid Exception can be checked using the functions in std.c.fenv.

In Expressions

RelExpression in ShiftExpression
An associative array can be tested to see if an element is in the array:
int foo[char[]];
...
if ("hello" in foo)
	...
The in expression has the same precedence as the relational expressions <, <=, etc. The return value of the InExpression is null if the element is not in the array; if it is in the array it is a pointer to the element.

Shift Expressions

ShiftExpression << AddExpression
ShiftExpression >> AddExpression
ShiftExpression >>> AddExpression
The operands must be integral types, and undergo the usual integral promotions. The result type is the type of the left operand after the promotions. The result value is the result of shifting the bits by the right operand's value.

<< is a left shift. >> is a signed right shift. >>> is an unsigned right shift.

It's illegal to shift by more bits than the size of the quantity being shifted:

int c;
c << 33;	// error

Add Expressions

AddExpression + MulExpression
AddExpression - MulExpression
AddExpression ~ MulExpression
If the operands are of integral types, they undergo integral promotions, and then are brought to a common type using the usual arithmetic conversions.

If either operand is a floating point type, the other is implicitly converted to floating point and they are brought to a common type via the usual arithmetic conversions.

If the operator is + or -, and the first operand is a pointer, and the second is an integral type, the resulting type is the type of the first operand, and the resulting value is the pointer plus (or minus) the second operand multiplied by the size of the type pointed to by the first operand.

If the second operand is a pointer, and the first is an integral type, and the operator is +, the operands are reversed and the pointer arithmetic just described is applied.

Add expressions for floating point operands are not associative.

Mul Expressions

MulExpression * UnaryExpression
MulExpression / UnaryExpression
MulExpression % UnaryExpression
The operands must be arithmetic types. They undergo integral promotions, and then are brought to a common type using the usual arithmetic conversions.

For integral operands, the *, /, and % correspond to multiply, divide, and modulus operations. For multiply, overflows are ignored and simply chopped to fit into the integral type. If the right operand of divide or modulus operators is 0, a DivideByZeroException is thrown.

For floating point operands, the operations correspond to the IEEE 754 floating point equivalents. The modulus operator only works with reals, it is illegal to use it with imaginary or complex operands.

Mul expressions for floating point operands are not associative.

Unary Expressions

& UnaryExpression
++ UnaryExpression
-- UnaryExpression
* UnaryExpression
- UnaryExpression
+ UnaryExpression
! UnaryExpression
~ UnaryExpression
delete UnaryExpression
NewExpression
cast ( Type ) UnaryExpression
( Type ) . Identifier
( Expression )

New Expressions

New expressions are used to allocate memory on the garbage collected heap (default) or using a class or struct specific allocator.

To allocate multidimensional arrays, the declaration reads in the same order as the prefix array declaration order.

char[][] foo;	// dynamic array of strings
...
foo = new char[][30];	// allocate array of 30 strings
If there is a new ( ArgumentList ), then those arguments are passed to the class or struct specific allocator function after the size argument.

Delete Expressions

If the UnaryExpression is a class object reference, and there is a destructor for that class, the destructor is called for that object instance.

Next, if the UnaryExpression is a class object reference, or a pointer to a struct instance, and the class or struct has overloaded operator delete, then that operator delete is called for that class object instance or struct instance.

Otherwise, the garbage collector is called to immediately free the memory allocated for the class instance or struct instance. If the garbage collector was not used to allocate the memory for the instance, undefined behavior will result.

If the UnaryExpression is a pointer or a dynamic array, the garbage collector is called to immediately release the memory. If the garbage collector was not used to allocate the memory for the instance, undefined behavior will result.

The pointer, dynamic array, or reference is set to null after the delete is performed.

Cast Expressions

A CastExpression converts the UnaryExpression to Type.
cast(foo) -p;	// cast (-p) to type foo
(foo) - p;	// subtract p from foo
Any casting of a class reference to a derived class reference is done with a runtime check to make sure it really is a downcast. null is the result if it isn't. Note: This is equivalent to the behavior of the dynamic_cast operator in C++.
class A { ... }
class B : A { ... }

void test(A a, B b)
{
     B bx = a;		// error, need cast
     B bx = cast(B) a;	// bx is null if a is not a B
     A ax = b;		// no cast needed
     A ax = cast(A) b;	// no runtime check needed for upcast
}
In order to determine if an object o is an instance of a class B use a cast:
if (cast(B) o)
{
    // o is an instance of B
}
else
{
    // o is not an instance of B
}
Casting a floating point literal from one type to another changes its type, but internally it is retained at full precision for the purposes of constant folding.
void test()
{
    real a = 3.40483L;
    real b;
    b = 3.40483;         // literal is not truncated to double precision
    assert(a == b);
    assert(a == 3.40483);
    assert(a == 3.40483L);
    assert(a == 3.40483F);
    double d = 3.40483;	// truncate literal when assigned to variable
    assert(d != a);     // so it is no longer the same
    const double x = 3.40483; // assignment to const is not
    assert(x == a);           // truncated if the initializer is visible
}

Postfix Expressions

PostfixExpression . Identifier
PostfixExpression . NewExpression
PostfixExpression ++
PostfixExpression --
PostfixExpression ( ArgumentList )
PostfixExpression [ ArgumentList ]
PostfixExpression [ AssignExpression .. AssignExpression ]

Index Expressions

PostfixExpression [ ArgumentList ]
PostfixExpression is evaluated. if PostfixExpression is an expression of type static array or dynamic array, the variable length is declared and set to be the length of the array. A new declaration scope is created for the evaluation of the ArgumentList and length appears in that scope only.

Slice Expressions

PostfixExpression [ AssignExpression .. AssignExpression ]
PostfixExpression is evaluated. if PostfixExpression is an expression of type static array or dynamic array, the variable length is declared and set to be the length of the array. A new declaration scope is created for the evaluation of the AssignExpression..AssignExpression and length appears in that scope only.

The first AssignExpression is taken to be the inclusive lower bound of the slice, and the second AssignExpression is the exclusive upper bound. The result of the expression is a slice of the PostfixExpression array.

Primary Expressions

Identifier
.Identifier
this
super
null
true
false
NumericLiteral
CharacterLiteral
StringLiteral
FunctionLiteral
AssertExpression
BasicType . Identifier
typeid ( Type )

.Identifier

Identifier is looked up at module scope, rather than the current lexically nested scope.

this

Within a non-static member function, this resolves to a reference to the object that called the function. If a member function is called with an explicit reference to typeof(this), a non-virtual call is made:
class A
{
    char get() { return 'A'; }

    char foo() { return typeof(this).get(); }
    char bar() { return this.get(); }
}

class B : A
{
    char get() { return 'B'; }
}

void main()
{
    B b = new B();

    b.foo();		// returns 'A'
    b.bar();		// returns 'B'
}

super

Within a non-static member function, super resolves to a reference to the object that called the function, cast to its base class. It is an error if there is no base class. super is not allowed in struct member functions. If a member function is called with an explicit reference to super, a non-virtual call is made.

null

The keyword null represents the null pointer value; technically it is of type (void *). It can be implicitly cast to any pointer type. The integer 0 cannot be cast to the null pointer. Nulls are also used for empty arrays.

true, false

These are of type bool and when cast to another integral type become the values 1 and 0, respectively.

Character Literals

Character literals are single characters and resolve to one of type char, wchar, or dchar. If the literal is a \u escape sequence, it resolves to type wchar. If the literal is a \U escape sequence, it resolves to type dchar. Otherwise, it resolves to the type with the smallest size it will fit into.

Function Literals

FunctionLiteral
	function Typeopt ( ArgumentList )opt FunctionBody
	delegate Typeopt ( ArgumentList )opt FunctionBody
	( ArgumentList ) FunctionBody
	FunctionBody
FunctionLiterals enable embedding anonymous functions and anonymous delegates directly into expressions. Type is the return type of the function or delegate, if omitted it is inferred from any ReturnStatements in the FunctionBody. ( ArgumentList ) forms the arguments to the function. If omitted it defaults to the empty argument list (). The type of a function literal is pointer to function or pointer to delegate. If the keywords function or delegate are omitted, it defaults to being a delegate.

For example:

int function(char c) fp;	// declare pointer to a function

void test()
{
    static int foo(char c) { return 6; }

    fp = &foo;
}
is exactly equivalent to:
int function(char c) fp;

void test()
{
    fp = function int(char c) { return 6;} ;
}
And:
int abc(int delegate(long i));

void test()
{   int b = 3;
    int foo(long c) { return 6 + b; }

    abc(&foo);
}
is exactly equivalent to:
int abc(int delegate(long i));

void test()
{   int b = 3;

    abc( delegate int(long c) { return 6 + b; } );
}

and the following where the return type int is inferred:

int abc(int delegate(long i));

void test()
{   int b = 3;

    abc( (long c) { return 6 + b; } );
}
Anonymous delegates can behave like arbitrary statement literals. For example, here an arbitrary statement is executed by a loop:
double test()
{   double d = 7.6;
    float f = 2.3;

    void loop(int k, int j, void delegate() statement)
    {
	for (int i = k; i < j; i++)
	{
	    statement();
	}
    }

    loop(5, 100, { d += 1; } );
    loop(3, 10,  { f += 3; } );

    return d + f;
}
When comparing with nested functions, the function form is analogous to static or non-nested functions, and the delegate form is analogous to non-static nested functions. In other words, a delegate literal can access stack variables in its enclosing function, a function literal cannot.

Assert Expressions

AssertExpression:
	assert ( Expression )
	assert ( Expression , Expression )

Asserts evaluate the expression. If the result is false, an AssertError is thrown. If the result is true, then no exception is thrown. It is an error if the expression contains any side effects that the program depends on. The compiler may optionally not evaluate assert expressions at all. The result type of an assert expression is void. Asserts are a fundamental part of the Contract Programming support in D.

The expression assert(0) is a special case; it signifies that it is unreachable code. Either AssertError is thrown at runtime if it is reachable, or the execution is halted (on the x86 processor, a HLT instruction can be used to halt execution). The optimization and code generation phases of compilation may assume that it is unreachable code.

The second Expression, if present, must be implicitly convertible to type char[]. It is evaluated if the result is false, and the string result is appended to the AssertError's message.

void main()
{
    assert(0, "an" ~ " error message");
}

When compiled and run, it will produce the message:

Error: AssertError Failure test.d(3) an error message

Typeid Expressions

TypeidExpression:
    typeid ( Type )
Returns an instance of class TypeInfo corresponding to Type.

IsExpression

IsExpression:
	is ( Type )
	is ( Type : TypeSpecialization )
	is ( Type == TypeSpecialization )
	is ( Type Identifier )
	is ( Type Identifier : TypeSpecialization )
	is ( Type Identifier == TypeSpecialization )

TypeSpecialization:
	Type
	typedef
	struct
	union
	class
	interface
	enum
	function
	delegate
IsExpressions are evaluated at compile time and are used for checking for valid types, comparing types for equivalence, determining if one type can be implicitly converted to another, and deducing the subtypes of a type. The result of an IsExpression is an int of type 0 if the condition is not satisified, 1 if it is.

Type is the type being tested. It must be syntactically correct, but it need not be semantically correct. If it is not semantically correct, the condition is not satisfied.

Identifier is declared to be an alias of the resulting type if the condition is satisfied. The Identifier forms can only be used if the IsExpression appears in a StaticIfCondition.

TypeSpecialization is the type that Type is being compared against.

The forms of the IsExpression are:

  1. is ( Type )
    The condition is satisfied if Type is semantically correct (it must be syntactically correct regardless).
    alias int func(int);	// func is a alias to a function type
    void foo()
    {
        if ( is(func[]) )	// not satisfied because arrays of
    			// functions are not allowed
    	printf("satisfied\n");
        else
    	printf("not satisfied\n");
    
        if (is([][]))	// error, [][] is not a syntactically valid type
    	...
    }
    
  2. is ( Type : TypeSpecialization )
    The condition is satisfied if Type is semantically correct and it is the same as or can be implicitly converted to TypeSpecialization. TypeSpecialization is only allowed to be a Type.
    alias short bar;
    void foo(bar x)
    {
        if ( is(bar : int) )	// satisfied because short can be
    				// implicitly converted to int
    	printf("satisfied\n");
        else
    	printf("not satisfied\n");
    }
    
  3. is ( Type == TypeSpecialization )
    The condition is satisfied if Type is semantically correct and is the same type as TypeSpecialization.

    If TypeSpecialization is one of typedef struct union class interface enum function delegate then the condition is satisifed if Type is one of those.

    alias short bar;
    typedef char foo;
    void foo(bar x)
    {
        if ( is(bar == int) )	// not satisfied because short is not
    				// the same type as int
    	printf("satisfied\n");
        else
    	printf("not satisfied\n");
    
        if ( is(foo == typedef) ) // satisfied because foo is a typedef
    	printf("satisfied\n");
        else
    	printf("not satisfied\n");
    }
    
  4. is ( Type Identifier )
    The condition is satisfied if Type is semantically correct. If so, Identifier is declared to be an alias of Type.
    alias short bar;
    void foo(bar x)
    {
        static if ( is(bar T) )
    	alias T S;
        else
    	alias long S;
        writefln(typeid(S));   // prints "short"
    
        if ( is(bar T) )  // error, Identifier T form can
    		      // only be in StaticIfConditions
    	...
    }
    
  5. is ( Type Identifier : TypeSpecialization )
    The condition is satisfied if Type is the same as or can be implicitly converted to TypeSpecialization. The Identifier is declared to be either an alias of the TypeSpecialization or, if TypeSpecialization is dependent on Identifier, the deduced type. TypeSpecialization is only allowed to be a Type.
    alias short bar;
    alias long* abc;
    void foo(bar x, abc a)
    {
        static if ( is(bar T : int) )
    	alias T S;
        else
    	alias long S;
    
        writefln(typeid(S));	// prints "int"
    
        static if ( is(abc U : U*) )
    	U u;
    
        writefln(typeid(typeof(u)));	// prints "long"
    }
    
    The way the type of Identifier is determined is analogous to the way template parameter types are determined by TemplateTypeParameterSpecialization.

  6. is ( Type Identifier == TypeSpecialization )
    The condition is satisfied if Type is semantically correct and is the same as TypeSpecialization. The Identifier is declared to be either an alias of the TypeSpecialization or, if TypeSpecialization is dependent on Identifier, the deduced type.

    If TypeSpecialization is one of typedef struct union class interface enum function delegate then the condition is satisifed if Type is one of those. Furthermore, Identifier is set to be an alias of the type:

    keyword alias type for Identifier
    typedef the type that Type is a typedef of
    struct Type
    union Type
    class Type
    interface Type
    enum the base type of the enum
    function the return type of the function
    delegate the function type of the delegate
    alias short bar;
    enum E : byte { Emember }
    void foo(bar x, abc a)
    {
        static if ( is(bar T == int) ) // not satisfied, short is not int
    	alias T S;
        alias T U;			   // error, T is not defined
    
        static if ( is(E V == enum) )  // satisified, E is an enum
    	V x;			   // x is declared to be a byte
    }