Browse Source

`distinct` keyword for type declarations

gingerBill 7 years ago
parent
commit
92780e2683
17 changed files with 180 additions and 124 deletions
  1. 24 24
      core/c.odin
  2. 1 1
      core/fmt.odin
  3. 6 6
      core/math.odin
  4. 1 1
      core/mem.odin
  5. 2 2
      core/os_essence.odin
  6. 3 3
      core/os_linux.odin
  7. 3 3
      core/os_windows.odin
  8. 3 3
      core/os_x.odin
  9. 2 2
      core/sys/wgl.odin
  10. 36 36
      core/sys/windows.odin
  11. 18 4
      examples/demo.odin
  12. 57 18
      src/check_decl.cpp
  13. 3 4
      src/check_expr.cpp
  14. 3 3
      src/check_type.cpp
  15. 16 13
      src/parser.cpp
  16. 1 1
      src/parser.hpp
  17. 1 0
      src/tokenizer.cpp

+ 24 - 24
core/c.odin

@@ -1,38 +1,38 @@
 CHAR_BIT :: 8;
 
-c_bool   :: #alias bool;
-c_char   :: #alias u8;
-c_byte   :: #alias u8;
-c_schar  :: #alias i8;
-c_uchar  :: #alias u8;
-c_short  :: #alias i16;
-c_ushort :: #alias u16;
-c_int    :: #alias i32;
-c_uint   :: #alias u32;
+c_bool   :: bool;
+c_char   :: u8;
+c_byte   :: u8;
+c_schar  :: i8;
+c_uchar  :: u8;
+c_short  :: i16;
+c_ushort :: u16;
+c_int    :: i32;
+c_uint   :: u32;
 
 when ODIN_OS == "windows" || size_of(rawptr) == 4 {
-	c_long :: #alias i32;
+	c_long :: i32;
 } else {
-	c_long :: #alias i64;
+	c_long :: i64;
 }
 
 when ODIN_OS == "windows" || size_of(rawptr) == 4 {
-	c_ulong :: #alias u32;
+	c_ulong :: u32;
 } else {
-	c_ulong :: #alias u64;
+	c_ulong :: u64;
 }
 
-c_longlong       :: #alias i64;
-c_ulonglong      :: #alias u64;
-c_float          :: #alias f32;
-c_double         :: #alias f64;
-c_complex_float  :: #alias complex64;
-c_complex_double :: #alias complex128;
+c_longlong       :: i64;
+c_ulonglong      :: u64;
+c_float          :: f32;
+c_double         :: f64;
+c_complex_float  :: complex64;
+c_complex_double :: complex128;
 
 _ :: compile_assert(size_of(uintptr) == size_of(int));
 
-c_size_t    :: #alias uint;
-c_ssize_t   :: #alias int;
-c_ptrdiff_t :: #alias int;
-c_uintptr_t :: #alias uintptr;
-c_intptr_t  :: #alias int;
+c_size_t    :: uint;
+c_ssize_t   :: int;
+c_ptrdiff_t :: int;
+c_uintptr_t :: uintptr;
+c_intptr_t  :: int;

+ 1 - 1
core/fmt.odin

@@ -8,7 +8,7 @@ import "core:raw.odin"
 
 _BUFFER_SIZE :: 1<<12;
 
-String_Buffer :: [dynamic]byte;
+String_Buffer :: distinct [dynamic]byte;
 
 Fmt_Info :: struct {
 	minus:     bool,

+ 6 - 6
core/math.odin

@@ -14,14 +14,14 @@ EPSILON      :: 1.19209290e-7;
 τ :: TAU;
 π :: PI;
 
-Vec2 :: [2]f32;
-Vec3 :: [3]f32;
-Vec4 :: [4]f32;
+Vec2 :: distinct [2]f32;
+Vec3 :: distinct [3]f32;
+Vec4 :: distinct [4]f32;
 
 // Column major
-Mat2 :: [2][2]f32;
-Mat3 :: [3][3]f32;
-Mat4 :: [4][4]f32;
+Mat2 :: distinct [2][2]f32;
+Mat3 :: distinct [3][3]f32;
+Mat4 :: distinct [4][4]f32;
 
 Quat :: struct {x, y, z: f32, w: f32 = 1};
 

+ 1 - 1
core/mem.odin

@@ -84,7 +84,7 @@ allocation_header :: proc(data: rawptr) -> ^AllocationHeader {
 }
 
 
-Fixed_Byte_Buffer :: [dynamic]byte;
+Fixed_Byte_Buffer :: distinct [dynamic]byte;
 
 make_fixed_byte_buffer :: proc(backing: []byte) -> Fixed_Byte_Buffer {
 	s := transmute(raw.Slice)backing;

+ 2 - 2
core/os_essence.odin

@@ -1,7 +1,7 @@
 foreign import api "system:api"
 
-Handle    :: int;
-Errno     :: int;
+Handle    :: distinct int;
+Errno     :: distinct int;
 
 O_RDONLY   :: 1;
 O_WRONLY   :: 2;

+ 3 - 3
core/os_linux.odin

@@ -4,9 +4,9 @@ foreign import libc "system:c"
 import "core:strings.odin"
 import "core:mem.odin"
 
-Handle    :: i32;
-File_Time :: u64;
-Errno     :: i32;
+Handle    :: distinct i32;
+File_Time :: distinct u64;
+Errno     :: distinct i32;
 
 
 O_RDONLY   :: 0x00000;

+ 3 - 3
core/os_windows.odin

@@ -1,8 +1,9 @@
 import win32 "core:sys/windows.odin"
 import "core:mem.odin"
 
-Handle    :: uintptr;
-File_Time :: u64;
+Handle    :: distinct uintptr;
+File_Time :: distinct u64;
+Errno     :: distinct int;
 
 
 INVALID_HANDLE :: ~Handle(0);
@@ -22,7 +23,6 @@ O_SYNC     :: 0x01000;
 O_ASYNC    :: 0x02000;
 O_CLOEXEC  :: 0x80000;
 
-Errno :: int;
 
 ERROR_NONE:                   Errno : 0;
 ERROR_FILE_NOT_FOUND:         Errno : 2;

+ 3 - 3
core/os_x.odin

@@ -4,9 +4,9 @@ foreign import libc "system:c"
 import "core:strings.odin"
 import "core:mem.odin"
 
-Handle    :: i32;
-File_Time :: u64;
-Errno     :: int;
+Handle    :: distinct i32;
+File_Time :: distinct u64;
+Errno     :: distinct int;
 
 
 O_RDONLY   :: 0x00000;

+ 2 - 2
core/sys/wgl.odin

@@ -12,8 +12,8 @@ CONTEXT_FORWARD_COMPATIBLE_BIT_ARB    :: 0x0002;
 CONTEXT_CORE_PROFILE_BIT_ARB          :: 0x00000001;
 CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x00000002;
 
-Hglrc     :: Handle;
-Color_Ref :: u32;
+Hglrc     :: distinct Handle;
+Color_Ref :: distinct u32;
 
 Layer_Plane_Descriptor :: struct {
 	size:             u16,

+ 36 - 36
core/sys/windows.odin

@@ -6,27 +6,27 @@ when ODIN_OS == "windows" {
 	foreign import "system:shell32.lib"
 }
 
-Handle    :: rawptr;
-Hwnd      :: Handle;
-Hdc       :: Handle;
-Hinstance :: Handle;
-Hicon     :: Handle;
-Hcursor   :: Handle;
-Hmenu     :: Handle;
-Hbrush    :: Handle;
-Hgdiobj   :: Handle;
-Hmodule   :: Handle;
-Hmonitor  :: Handle;
-Hrawinput :: Handle;
-HKL       :: Handle;
-Wparam    :: uint;
-Lparam    :: int;
-Lresult   :: int;
-Wnd_Proc  :: #type proc "c" (Hwnd, u32, Wparam, Lparam) -> Lresult;
-
-Long_Ptr :: int;
-
-Bool :: b32;
+Handle    :: distinct rawptr;
+Hwnd      :: distinct Handle;
+Hdc       :: distinct Handle;
+Hinstance :: distinct Handle;
+Hicon     :: distinct Handle;
+Hcursor   :: distinct Handle;
+Hmenu     :: distinct Handle;
+Hbrush    :: distinct Handle;
+Hgdiobj   :: distinct Handle;
+Hmodule   :: distinct Handle;
+Hmonitor  :: distinct Handle;
+Hrawinput :: distinct Handle;
+HKL       :: distinct Handle;
+Wparam    :: distinct uint;
+Lparam    :: distinct int;
+Lresult   :: distinct int;
+Wnd_Proc  :: distinct #type proc "c" (Hwnd, u32, Wparam, Lparam) -> Lresult;
+
+Long_Ptr :: distinct int;
+
+Bool :: distinct b32;
 
 Point :: struct {
 	x, y: i32,
@@ -126,7 +126,7 @@ Security_Attributes :: struct {
 
 Process_Information :: struct {
 	process:    Handle,
-	thread:     Handle, 
+	thread:     Handle,
 	process_id: u32,
 	thread_id:  u32
 }
@@ -471,7 +471,7 @@ PFD_DEPTH_DONTCARE        :: 0x20000000;
 PFD_DOUBLEBUFFER_DONTCARE :: 0x40000000;
 PFD_STEREO_DONTCARE       :: 0x80000000;
 
-GET_FILEEX_INFO_LEVELS :: i32;
+GET_FILEEX_INFO_LEVELS :: distinct i32;
 GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0;
 GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1;
 
@@ -498,14 +498,14 @@ MOVEFILE_WRITE_THROUGH         :: 0x00000008;
 MOVEFILE_CREATE_HARDLINK       :: 0x00000010;
 MOVEFILE_FAIL_IF_NOT_TRACKABLE :: 0x00000020;
 
-FILE_NOTIFY_CHANGE_FILE_NAME   :: 0x00000001;   
-FILE_NOTIFY_CHANGE_DIR_NAME    :: 0x00000002;   
-FILE_NOTIFY_CHANGE_ATTRIBUTES  :: 0x00000004;   
-FILE_NOTIFY_CHANGE_SIZE        :: 0x00000008;   
-FILE_NOTIFY_CHANGE_LAST_WRITE  :: 0x00000010;   
-FILE_NOTIFY_CHANGE_LAST_ACCESS :: 0x00000020;   
-FILE_NOTIFY_CHANGE_CREATION    :: 0x00000040;   
-FILE_NOTIFY_CHANGE_SECURITY    :: 0x00000100; 
+FILE_NOTIFY_CHANGE_FILE_NAME   :: 0x00000001;
+FILE_NOTIFY_CHANGE_DIR_NAME    :: 0x00000002;
+FILE_NOTIFY_CHANGE_ATTRIBUTES  :: 0x00000004;
+FILE_NOTIFY_CHANGE_SIZE        :: 0x00000008;
+FILE_NOTIFY_CHANGE_LAST_WRITE  :: 0x00000010;
+FILE_NOTIFY_CHANGE_LAST_ACCESS :: 0x00000020;
+FILE_NOTIFY_CHANGE_CREATION    :: 0x00000040;
+FILE_NOTIFY_CHANGE_SECURITY    :: 0x00000100;
 
 FILE_FLAG_WRITE_THROUGH        :: 0x80000000;
 FILE_FLAG_OVERLAPPED           :: 0x40000000;
@@ -537,9 +537,9 @@ CP_UTF8       :: 65001; // UTF-8 translation
 @(default_calling_convention = "std")
 foreign kernel32 {
 	@(link_name="GetLastError")              get_last_error              :: proc() -> i32 ---;
-	@(link_name="CreateProcessA")		     create_process_a		     :: proc(application_name, command_line: ^byte, 
-	                                                              				 process_attributes, thread_attributes: ^Security_Attributes, 
-	                                                              				 inherit_handle: Bool, creation_flags: u32, environment: rawptr, 
+	@(link_name="CreateProcessA")		     create_process_a		     :: proc(application_name, command_line: ^byte,
+	                                                              				 process_attributes, thread_attributes: ^Security_Attributes,
+	                                                              				 inherit_handle: Bool, creation_flags: u32, environment: rawptr,
 	                                                              				 current_direcotry: ^byte, startup_info : ^Startup_Info,
 	                                                              				 process_information : ^Process_Information) -> Bool ---;
 	@(link_name="GetExitCodeProcess")		 get_exit_code_process       :: proc(process: Handle, exit: ^u32) -> Bool ---;
@@ -606,7 +606,7 @@ foreign kernel32 {
 	@(link_name="FindNextChangeNotification")   find_next_change_notification    :: proc(h: Handle) -> Bool ---;
 	@(link_name="FindCloseChangeNotification")  find_close_change_notification   :: proc(h: Handle) -> Bool ---;
 
-	@(link_name="ReadDirectoryChangesW") read_directory_changes_w :: proc(dir: Handle, buf: rawptr, buf_length: u32, 
+	@(link_name="ReadDirectoryChangesW") read_directory_changes_w :: proc(dir: Handle, buf: rawptr, buf_length: u32,
 	                                                                      watch_subtree: Bool, notify_filter: u32,
 	                                                                      bytes_returned: ^u32, overlapped: ^Overlapped,
 	                                                                      completion: rawptr) -> Bool ---;
@@ -615,7 +615,7 @@ foreign kernel32 {
 	                                                                   wchar_str: ^u16, wchar: i32,
 	                                                                   multi_str: ^byte, multi: i32,
 	                                                                   default_char: ^byte, used_default_char: ^Bool) -> i32 ---;
-	
+
 	@(link_name="CreateSemaphoreA")    create_semaphore_a     :: proc(attributes: ^Security_Attributes, initial_count, maximum_count: i32, name: ^byte) -> Handle ---;
 	@(link_name="ReleaseSemaphore")    release_semaphore      :: proc(semaphore: Handle, release_count: i32, previous_count: ^i32) -> Bool ---;
 	@(link_name="WaitForSingleObject") wait_for_single_object :: proc(handle: Handle, milliseconds: u32) -> u32 ---;

+ 18 - 4
examples/demo.odin

@@ -22,7 +22,7 @@ when ODIN_OS == "windows" {
 @(link_name="general_stuff")
 general_stuff :: proc() {
 	fmt.println("# general_stuff");
-	{ // `do` for inline statmes rather than block
+	{ // `do` for inline statments rather than block
 		foo :: proc() do fmt.println("Foo!");
 		if   false do foo();
 		for  false do foo();
@@ -93,6 +93,21 @@ general_stuff :: proc() {
 		// Having specific sized booleans is very useful when dealing with foreign code
 		// and to enforce specific alignment for a boolean, especially within a struct
 	}
+
+	{ // `distinct` types
+		// Originally, all type declarations would create a distinct type unless #type_alias was present.
+		// Now the behaviour has been reversed. All type declarations create a type alias unless `distinct` is present.
+		// If the type expression is `struct`, `union`, `enum`, `proc`, or `bit_field`, the types will always been distinct.
+
+		Int32 :: i32;
+		compile_assert(Int32 == i32);
+
+		My_Int32 :: distinct i32;
+		compile_assert(My_Int32 != i32);
+
+		My_Struct :: struct{x: int};
+		compile_assert(My_Struct != struct{x: int});
+	}
 }
 
 default_struct_values :: proc() {
@@ -601,7 +616,7 @@ array_programming :: proc() {
 	}
 
 	{
-		Vector3 :: [3]f32;
+		Vector3 :: distinct [3]f32;
 		a := Vector3{1, 2, 3};
 		b := Vector3{5, 6, 7};
 		c := (a * b)/2 + 1;
@@ -675,7 +690,7 @@ enum_export :: proc() {
 }
 
 main :: proc() {
-	when true {
+	when false {
 		general_stuff();
 		default_struct_values();
 		union_type();
@@ -687,4 +702,3 @@ main :: proc() {
 		enum_export();
 	}
 }
-

+ 57 - 18
src/check_decl.cpp

@@ -170,22 +170,57 @@ void check_init_constant(Checker *c, Entity *e, Operand *operand) {
 	e->Constant.value = operand->value;
 }
 
-AstNode *remove_type_alias(AstNode *node) {
+
+bool is_type_distinct(AstNode *node) {
+	for (;;) {
+		if (node == nullptr) {
+			return false;
+		}
+		if (node->kind == AstNode_ParenExpr) {
+			node = node->ParenExpr.expr;
+		} else if (node->kind == AstNode_HelperType) {
+			node = node->HelperType.type;
+		} else {
+			break;
+		}
+	}
+
+	switch (node->kind) {
+	case AstNode_DistinctType:
+		return true;
+
+	case AstNode_StructType:
+	case AstNode_UnionType:
+	case AstNode_EnumType:
+	case AstNode_BitFieldType:
+	case AstNode_ProcType:
+		return true;
+
+	case AstNode_PointerType:
+	case AstNode_ArrayType:
+	case AstNode_DynamicArrayType:
+	case AstNode_MapType:
+		return true;
+	}
+	return false;
+}
+
+AstNode *remove_type_alias_clutter(AstNode *node) {
 	for (;;) {
 		if (node == nullptr) {
 			return nullptr;
 		}
 		if (node->kind == AstNode_ParenExpr) {
 			node = node->ParenExpr.expr;
-		} else if (node->kind == AstNode_AliasType) {
-			node = node->AliasType.type;
+		} else if (node->kind == AstNode_DistinctType) {
+			node = node->DistinctType.type;
 		} else {
 			return node;
 		}
 	}
 }
 
-void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def, bool is_alias) {
+void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def) {
 	GB_ASSERT(e->type == nullptr);
 
 	DeclInfo *decl = decl_info_of_entity(&c->info, e);
@@ -193,7 +228,8 @@ void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def, bool
 		error(decl->attributes[0], "Attributes are not allowed on type declarations");
 	}
 
-	AstNode *te = remove_type_alias(type_expr);
+	bool is_distinct = is_type_distinct(type_expr);
+	AstNode *te = remove_type_alias_clutter(type_expr);
 	e->type = t_invalid;
 	String name = e->token.string;
 	Type *named = make_type_named(c->allocator, name, nullptr, e);
@@ -205,16 +241,20 @@ void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def, bool
 
 	Type *bt = check_type(c, te, named);
 	named->Named.base = base_type(bt);
-	if (is_alias) {
-		if (is_type_named(bt)) {
-			e->type = bt;
-			e->TypeName.is_type_alias = true;
-		} else {
-			gbString str = type_to_string(bt);
-			error(type_expr, "Type alias declaration with a non-named type '%s'", str);
-			gb_string_free(str);
-		}
-	}
+	if (!is_distinct) {
+		e->type = bt;
+		e->TypeName.is_type_alias = true;
+	}
+	// if (is_alias) {
+	// 	if (is_type_named(bt)) {
+	// 		e->type = bt;
+	// 		e->TypeName.is_type_alias = true;
+	// 	} else {
+	// 		gbString str = type_to_string(bt);
+	// 		error(type_expr, "Type alias declaration with a non-named type '%s'", str);
+	// 		gb_string_free(str);
+	// 	}
+	// }
 }
 
 void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init, Type *named_type) {
@@ -261,7 +301,7 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init,
 				error(e->token, "A type declaration cannot have an type parameter");
 			}
 			d->type_expr = d->init_expr;
-			check_type_decl(c, e, d->type_expr, named_type, false);
+			check_type_decl(c, e, d->type_expr, named_type);
 			return;
 		}
 
@@ -866,8 +906,7 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
 		check_const_decl(c, e, d->type_expr, d->init_expr, named_type);
 		break;
 	case Entity_TypeName: {
-		bool is_alias = unparen_expr(d->type_expr)->kind == AstNode_AliasType;
-		check_type_decl(c, e, d->type_expr, named_type, is_alias);
+		check_type_decl(c, e, d->type_expr, named_type);
 		break;
 	}
 	case Entity_Procedure:

+ 3 - 4
src/check_expr.cpp

@@ -57,7 +57,7 @@ ExprKind check_expr_base                (Checker *c, Operand *operand, AstNode *
 void     check_expr_with_type_hint      (Checker *c, Operand *o, AstNode *e, Type *t);
 Type *   check_type                     (Checker *c, AstNode *expression, Type *named_type = nullptr);
 Type *   make_optional_ok_type          (gbAllocator a, Type *value);
-void     check_type_decl                (Checker *c, Entity *e, AstNode *type_expr, Type *def, bool alias);
+void     check_type_decl                (Checker *c, Entity *e, AstNode *type_expr, Type *def);
 Entity * check_selector                 (Checker *c, Operand *operand, AstNode *node, Type *type_hint);
 Entity * check_ident                    (Checker *c, Operand *o, AstNode *n, Type *named_type, Type *type_hint, bool allow_import_name);
 Entity * find_polymorphic_struct_entity (Checker *c, Type *original_type, isize param_count, Array<Operand> ordered_operands);
@@ -6255,12 +6255,11 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 		str = write_expr_to_string(str, ht->type);
 	case_end;
 
-	case_ast_node(ht, AliasType, node);
-		str = gb_string_appendc(str, "#type_alias ");
+	case_ast_node(ht, DistinctType, node);
+		str = gb_string_appendc(str, "distinct ");
 		str = write_expr_to_string(str, ht->type);
 	case_end;
 
-
 	case_ast_node(pt, PolyType, node);
 		str = gb_string_append_rune(str, '$');
 		str = write_expr_to_string(str, pt->type);

+ 3 - 3
src/check_type.cpp

@@ -1963,10 +1963,10 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type)
 		return check_type_internal(c, ht->type, type, named_type);
 	case_end;
 
-	case_ast_node(at, AliasType, e);
-		error(e, "Invalid use of '#type_alias'");
+	case_ast_node(dt, DistinctType, e);
+		error(e, "Invalid use of a distinct type");
 		// NOTE(bill): Treat it as a HelperType to remove errors
-		return check_type_internal(c, at->type, type, named_type);
+		return check_type_internal(c, dt->type, type, named_type);
 	case_end;
 
 	case_ast_node(pt, PolyType, e);

+ 16 - 13
src/parser.cpp

@@ -80,7 +80,7 @@ Token ast_node_token(AstNode *node) {
 
 	case AstNode_TypeType:         return node->TypeType.token;
 	case AstNode_HelperType:       return node->HelperType.token;
-	case AstNode_AliasType:        return node->AliasType.token;
+	case AstNode_DistinctType:     return node->DistinctType.token;
 	case AstNode_PolyType:         return node->PolyType.token;
 	case AstNode_ProcType:         return node->ProcType.token;
 	case AstNode_PointerType:      return node->PointerType.token;
@@ -321,8 +321,8 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) {
 	case AstNode_HelperType:
 		n->HelperType.type = clone_ast_node(a, n->HelperType.type);
 		break;
-	case AstNode_AliasType:
-		n->AliasType.type = clone_ast_node(a, n->AliasType.type);
+	case AstNode_DistinctType:
+		n->DistinctType.type = clone_ast_node(a, n->DistinctType.type);
 		break;
 	case AstNode_ProcType:
 		n->ProcType.params  = clone_ast_node(a, n->ProcType.params);
@@ -837,14 +837,13 @@ AstNode *ast_helper_type(AstFile *f, Token token, AstNode *type) {
 	return result;
 }
 
-AstNode *ast_alias_type(AstFile *f, Token token, AstNode *type) {
-	AstNode *result = make_ast_node(f, AstNode_AliasType);
-	result->AliasType.token = token;
-	result->AliasType.type  = type;
+AstNode *ast_distinct_type(AstFile *f, Token token, AstNode *type) {
+	AstNode *result = make_ast_node(f, AstNode_DistinctType);
+	result->DistinctType.token = token;
+	result->DistinctType.type  = type;
 	return result;
 }
 
-
 AstNode *ast_poly_type(AstFile *f, Token token, AstNode *type, AstNode *specialization) {
 	AstNode *result = make_ast_node(f, AstNode_PolyType);
 	result->PolyType.token = token;
@@ -1277,8 +1276,8 @@ bool is_semicolon_optional_for_node(AstFile *f, AstNode *s) {
 
 	case AstNode_HelperType:
 		return is_semicolon_optional_for_node(f, s->HelperType.type);
-	case AstNode_AliasType:
-		return is_semicolon_optional_for_node(f, s->AliasType.type);
+	case AstNode_DistinctType:
+		return is_semicolon_optional_for_node(f, s->DistinctType.type);
 
 	case AstNode_PointerType:
 		return is_semicolon_optional_for_node(f, s->PointerType.type);
@@ -1614,15 +1613,19 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
 		return ast_paren_expr(f, operand, open, close);
 	}
 
+	case Token_distinct: {
+		Token token = expect_token(f, Token_distinct);
+		AstNode *type = parse_type(f);
+		return ast_distinct_type(f, token, type);
+	}
+
 	case Token_Hash: {
 		Token token = expect_token(f, Token_Hash);
 		if (allow_token(f, Token_type)) {
 			return ast_helper_type(f, token, parse_type(f));
 		}
 		Token name = expect_token(f, Token_Ident);
-		if (name.string == "type_alias") {
-			return ast_alias_type(f, token, parse_type(f));
-		} else if (name.string == "run") {
+		if (name.string == "run") {
 			AstNode *expr = parse_expr(f, false);
 			operand = ast_run_expr(f, token, name, expr);
 			if (unparen_expr(expr)->kind != AstNode_CallExpr) {

+ 1 - 1
src/parser.hpp

@@ -408,7 +408,7 @@ AST_NODE_KIND(_TypeBegin, "", struct {}) \
 		Token token; \
 		AstNode *type; \
 	}) \
-	AST_NODE_KIND(AliasType, "alias type", struct { \
+	AST_NODE_KIND(DistinctType, "distinct type", struct { \
 		Token token; \
 		AstNode *type; \
 	}) \

+ 1 - 0
src/tokenizer.cpp

@@ -110,6 +110,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_dynamic,                "dynamic"),                \
 	TOKEN_KIND(Token_cast,                   "cast"),                   \
 	TOKEN_KIND(Token_transmute,              "transmute"),              \
+	TOKEN_KIND(Token_distinct,               "distinct"),               \
 	TOKEN_KIND(Token_using,                  "using"),                  \
 	TOKEN_KIND(Token_inline,                 "inline"),                 \
 	TOKEN_KIND(Token_no_inline,              "no_inline"),              \