Browse Source

Implement `#complete switch` by default, replace with `#partial switch` #511

gingerBill 5 years ago
parent
commit
d1c9fd4e01

+ 105 - 105
core/encoding/cel/cel.odin

@@ -106,11 +106,11 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
 	p := new(Parser);
 	p := new(Parser);
 	for {
 	for {
 		tok := scan(t);
 		tok := scan(t);
-		if tok.kind == Kind.Illegal {
+		if tok.kind == .Illegal {
 			return p, false;
 			return p, false;
 		}
 		}
 		append(&p.tokens, tok);
 		append(&p.tokens, tok);
-		if tok.kind == Kind.EOF {
+		if tok.kind == .EOF {
 			break;
 			break;
 		}
 		}
 	}
 	}
@@ -120,7 +120,7 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
 	}
 	}
 
 
 	if len(p.tokens) == 0 {
 	if len(p.tokens) == 0 {
-		tok := Token{kind = Kind.EOF};
+		tok := Token{kind = .EOF};
 		tok.line, tok.column = 1, 1;
 		tok.line, tok.column = 1, 1;
 		append(&p.tokens, tok);
 		append(&p.tokens, tok);
 		return p, true;
 		return p, true;
@@ -134,8 +134,8 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
 	p.dict_stack = make([dynamic]^Dict, 0, 4);
 	p.dict_stack = make([dynamic]^Dict, 0, 4);
 	append(&p.dict_stack, &p.root);
 	append(&p.dict_stack, &p.root);
 
 
-	for p.curr_token.kind != Kind.EOF &&
-	    p.curr_token.kind != Kind.Illegal &&
+	for p.curr_token.kind != .EOF &&
+	    p.curr_token.kind != .Illegal &&
 	    p.curr_token_index < len(p.tokens) {
 	    p.curr_token_index < len(p.tokens) {
 		if !parse_assignment(p) {
 		if !parse_assignment(p) {
 			break;
 			break;
@@ -147,7 +147,7 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
 
 
 destroy :: proc(p: ^Parser) {
 destroy :: proc(p: ^Parser) {
 	destroy_value :: proc(value: Value) {
 	destroy_value :: proc(value: Value) {
-		switch v in value {
+		#partial switch v in value {
 		case Array:
 		case Array:
 			for elem in v do destroy_value(elem);
 			for elem in v do destroy_value(elem);
 			delete(v);
 			delete(v);
@@ -287,7 +287,7 @@ unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool
 
 
 
 
 unquote_string :: proc(p: ^Parser, t: Token) -> (string, bool) {
 unquote_string :: proc(p: ^Parser, t: Token) -> (string, bool) {
-	if t.kind != Kind.String {
+	if t.kind != .String {
 		return t.lit, true;
 		return t.lit, true;
 	}
 	}
 	s := t.lit;
 	s := t.lit;
@@ -368,8 +368,8 @@ expect_operator :: proc(p: ^Parser) -> Token {
 
 
 fix_advance :: proc(p: ^Parser) {
 fix_advance :: proc(p: ^Parser) {
 	for {
 	for {
-		switch t := p.curr_token; t.kind {
-		case Kind.EOF, Kind.Semicolon:
+		#partial switch t := p.curr_token; t.kind {
+		case .EOF, .Semicolon:
 			return;
 			return;
 		}
 		}
 		next_token(p);
 		next_token(p);
@@ -377,7 +377,7 @@ fix_advance :: proc(p: ^Parser) {
 }
 }
 
 
 copy_value :: proc(value: Value) -> Value {
 copy_value :: proc(value: Value) -> Value {
-	switch v in value {
+	#partial switch v in value {
 	case Array:
 	case Array:
 		a := make(Array, len(v));
 		a := make(Array, len(v));
 		for elem, idx in v {
 		for elem, idx in v {
@@ -407,79 +407,79 @@ lookup_value :: proc(p: ^Parser, name: string) -> (Value, bool) {
 
 
 parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
 parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
 	tok := p.curr_token;
 	tok := p.curr_token;
-	switch p.curr_token.kind {
-	case Kind.Ident:
+	#partial switch p.curr_token.kind {
+	case .Ident:
 		next_token(p);
 		next_token(p);
 		v, ok := lookup_value(p, tok.lit);
 		v, ok := lookup_value(p, tok.lit);
 		if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
 		if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
 		return v, tok.pos;
 		return v, tok.pos;
 
 
-	case Kind.True:
+	case .True:
 		next_token(p);
 		next_token(p);
 		return true, tok.pos;
 		return true, tok.pos;
-	case Kind.False:
+	case .False:
 		next_token(p);
 		next_token(p);
 		return false, tok.pos;
 		return false, tok.pos;
 
 
-	case Kind.Nil:
+	case .Nil:
 		next_token(p);
 		next_token(p);
 		return Nil_Value{}, tok.pos;
 		return Nil_Value{}, tok.pos;
 
 
-	case Kind.Integer:
+	case .Integer:
 		next_token(p);
 		next_token(p);
 		return strconv.parse_i64(tok.lit), tok.pos;
 		return strconv.parse_i64(tok.lit), tok.pos;
 
 
-	case Kind.Float:
+	case .Float:
 		next_token(p);
 		next_token(p);
 		return strconv.parse_f64(tok.lit), tok.pos;
 		return strconv.parse_f64(tok.lit), tok.pos;
 
 
-	case Kind.String:
+	case .String:
 		next_token(p);
 		next_token(p);
 		str, ok := unquote_string(p, tok);
 		str, ok := unquote_string(p, tok);
 		if !ok do error(p, tok.pos, "Unable to unquote string");
 		if !ok do error(p, tok.pos, "Unable to unquote string");
 		return string(str), tok.pos;
 		return string(str), tok.pos;
 
 
-	case Kind.Open_Paren:
-		expect_token(p, Kind.Open_Paren);
+	case .Open_Paren:
+		expect_token(p, .Open_Paren);
 		expr, _ := parse_expr(p);
 		expr, _ := parse_expr(p);
-		expect_token(p, Kind.Close_Paren);
+		expect_token(p, .Close_Paren);
 		return expr, tok.pos;
 		return expr, tok.pos;
 
 
-	case Kind.Open_Bracket:
-		expect_token(p, Kind.Open_Bracket);
+	case .Open_Bracket:
+		expect_token(p, .Open_Bracket);
 		elems := make([dynamic]Value, 0, 4);
 		elems := make([dynamic]Value, 0, 4);
-		for p.curr_token.kind != Kind.Close_Bracket &&
-		    p.curr_token.kind != Kind.EOF {
+		for p.curr_token.kind != .Close_Bracket &&
+		    p.curr_token.kind != .EOF {
 			elem, _ := parse_expr(p);
 			elem, _ := parse_expr(p);
 			append(&elems, elem);
 			append(&elems, elem);
 
 
-			if p.curr_token.kind == Kind.Semicolon && p.curr_token.lit == "\n" {
+			if p.curr_token.kind == .Semicolon && p.curr_token.lit == "\n" {
 				next_token(p);
 				next_token(p);
-			} else if !allow_token(p, Kind.Comma) {
+			} else if !allow_token(p, .Comma) {
 				break;
 				break;
 			}
 			}
 
 
 		}
 		}
-		expect_token(p, Kind.Close_Bracket);
+		expect_token(p, .Close_Bracket);
 		return Array(elems[:]), tok.pos;
 		return Array(elems[:]), tok.pos;
 
 
-	case Kind.Open_Brace:
-		expect_token(p, Kind.Open_Brace);
+	case .Open_Brace:
+		expect_token(p, .Open_Brace);
 
 
     	dict := Dict{};
     	dict := Dict{};
     	append(&p.dict_stack, &dict);
     	append(&p.dict_stack, &dict);
     	defer pop(&p.dict_stack);
     	defer pop(&p.dict_stack);
 
 
-		for p.curr_token.kind != Kind.Close_Brace &&
-		    p.curr_token.kind != Kind.EOF {
+		for p.curr_token.kind != .Close_Brace &&
+		    p.curr_token.kind != .EOF {
 		    name_tok := p.curr_token;
 		    name_tok := p.curr_token;
-		    if !allow_token(p, Kind.Ident) && !allow_token(p, Kind.String) {
-		    	name_tok = expect_token(p, Kind.Ident);
+		    if !allow_token(p, .Ident) && !allow_token(p, .String) {
+		    	name_tok = expect_token(p, .Ident);
 		    }
 		    }
 
 
 			name, ok := unquote_string(p, name_tok);
 			name, ok := unquote_string(p, name_tok);
 			if !ok do error(p, tok.pos, "Unable to unquote string");
 			if !ok do error(p, tok.pos, "Unable to unquote string");
-		    expect_token(p, Kind.Assign);
+		    expect_token(p, .Assign);
 			elem, _ := parse_expr(p);
 			elem, _ := parse_expr(p);
 
 
 			if _, ok2 := dict[name]; ok2 {
 			if _, ok2 := dict[name]; ok2 {
@@ -488,13 +488,13 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
 				dict[name] = elem;
 				dict[name] = elem;
 			}
 			}
 
 
-			if p.curr_token.kind == Kind.Semicolon && p.curr_token.lit == "\n" {
+			if p.curr_token.kind == .Semicolon && p.curr_token.lit == "\n" {
 				next_token(p);
 				next_token(p);
-			} else if !allow_token(p, Kind.Comma) {
+			} else if !allow_token(p, .Comma) {
 				break;
 				break;
 			}
 			}
 		}
 		}
-		expect_token(p, Kind.Close_Brace);
+		expect_token(p, .Close_Brace);
 		return dict, tok.pos;
 		return dict, tok.pos;
 
 
 	}
 	}
@@ -504,13 +504,13 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
 parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
 parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
 	loop := true;
 	loop := true;
 	for operand := operand; loop;  {
 	for operand := operand; loop;  {
-		switch p.curr_token.kind {
-		case Kind.Period:
+		#partial switch p.curr_token.kind {
+		case .Period:
 			next_token(p);
 			next_token(p);
 			tok := next_token(p);
 			tok := next_token(p);
 
 
-			switch tok.kind {
-			case Kind.Ident:
+			#partial switch tok.kind {
+			case .Ident:
 				d, ok := operand.(Dict);
 				d, ok := operand.(Dict);
 				if !ok || d == nil {
 				if !ok || d == nil {
 					error(p, tok.pos, "Expected a dictionary");
 					error(p, tok.pos, "Expected a dictionary");
@@ -531,13 +531,13 @@ parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
 				operand = nil;
 				operand = nil;
 			}
 			}
 
 
-		case Kind.Open_Bracket:
-			expect_token(p, Kind.Open_Bracket);
+		case .Open_Bracket:
+			expect_token(p, .Open_Bracket);
 			index, index_pos := parse_expr(p);
 			index, index_pos := parse_expr(p);
-			expect_token(p, Kind.Close_Bracket);
+			expect_token(p, .Close_Bracket);
 
 
 
 
-			switch a in operand {
+			#partial switch a in operand {
 			case Array:
 			case Array:
 				i, ok := index.(i64);
 				i, ok := index.(i64);
 				if !ok {
 				if !ok {
@@ -587,22 +587,22 @@ parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
 
 
 parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
 parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
 	op := p.curr_token;
 	op := p.curr_token;
-	switch p.curr_token.kind {
-	case Kind.At:
+	#partial switch p.curr_token.kind {
+	case .At:
 		next_token(p);
 		next_token(p);
-		tok := expect_token(p, Kind.String);
+		tok := expect_token(p, .String);
 		v, ok := lookup_value(p, tok.lit);
 		v, ok := lookup_value(p, tok.lit);
 		if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
 		if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
 		return parse_atom_expr(p, v, tok.pos);
 		return parse_atom_expr(p, v, tok.pos);
 
 
-	case Kind.Add, Kind.Sub:
+	case .Add, .Sub:
 		next_token(p);
 		next_token(p);
 		// TODO(bill): Calcuate values as you go!
 		// TODO(bill): Calcuate values as you go!
 		expr, pos := parse_unary_expr(p);
 		expr, pos := parse_unary_expr(p);
 
 
-		switch e in expr {
-		case i64: if op.kind == Kind.Sub do return -e, pos;
-		case f64: if op.kind == Kind.Sub do return -e, pos;
+		#partial switch e in expr {
+		case i64: if op.kind == .Sub do return -e, pos;
+		case f64: if op.kind == .Sub do return -e, pos;
 		case:
 		case:
 			error(p, op.pos, "Unary operator %s can only be used on integers or floats", op.lit);
 			error(p, op.pos, "Unary operator %s can only be used on integers or floats", op.lit);
 			return nil, op.pos;
 			return nil, op.pos;
@@ -610,7 +610,7 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
 
 
 		return expr, op.pos;
 		return expr, op.pos;
 
 
-	case Kind.Not:
+	case .Not:
 		next_token(p);
 		next_token(p);
 		expr, _ := parse_unary_expr(p);
 		expr, _ := parse_unary_expr(p);
 		if v, ok := expr.(bool); ok {
 		if v, ok := expr.(bool); ok {
@@ -625,7 +625,7 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
 
 
 
 
 value_order :: proc(v: Value) -> int {
 value_order :: proc(v: Value) -> int {
-	switch _ in v {
+	#partial switch _ in v {
 	case bool, string:
 	case bool, string:
 		return 1;
 		return 1;
 	case i64:
 	case i64:
@@ -641,13 +641,13 @@ match_values :: proc(left, right: ^Value) -> bool {
 		return match_values(right, left);
 		return match_values(right, left);
 	}
 	}
 
 
-	switch x in left^ {
+	#partial switch x in left^ {
 	case:
 	case:
 		right^ = left^;
 		right^ = left^;
 	case bool, string:
 	case bool, string:
 		return true;
 		return true;
 	case i64:
 	case i64:
-		switch y in right^ {
+		#partial switch y in right^ {
 		case i64:
 		case i64:
 			return true;
 			return true;
 		case f64:
 		case f64:
@@ -656,7 +656,7 @@ match_values :: proc(left, right: ^Value) -> bool {
 		}
 		}
 
 
 	case f64:
 	case f64:
-		switch y in right {
+		#partial switch y in right {
 		case f64:
 		case f64:
 			return true;
 			return true;
 		}
 		}
@@ -671,59 +671,59 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
 	match_values(&x, &y);
 	match_values(&x, &y);
 
 
 
 
-	switch a in x {
+	#partial switch a in x {
 	case: return x, true;
 	case: return x, true;
 
 
 	case bool:
 	case bool:
 		b, ok := y.(bool);
 		b, ok := y.(bool);
 		if !ok do return nil, false;
 		if !ok do return nil, false;
-		switch op {
-		case Kind.Eq:    return a == b, true;
-		case Kind.NotEq: return a != b, true;
-		case Kind.And:   return a && b, true;
-		case Kind.Or:    return a || b, true;
+		#partial switch op {
+		case .Eq:    return a == b, true;
+		case .NotEq: return a != b, true;
+		case .And:   return a && b, true;
+		case .Or:    return a || b, true;
 		}
 		}
 
 
 	case i64:
 	case i64:
 		b, ok := y.(i64);
 		b, ok := y.(i64);
 		if !ok do return nil, false;
 		if !ok do return nil, false;
-		switch op {
-		case Kind.Add:   return a + b, true;
-		case Kind.Sub:   return a - b, true;
-		case Kind.Mul:   return a * b, true;
-		case Kind.Quo:   return a / b, true;
-		case Kind.Rem:   return a % b, true;
-		case Kind.Eq:    return a == b, true;
-		case Kind.NotEq: return a != b, true;
-		case Kind.Lt:    return a <  b, true;
-		case Kind.Gt:    return a >  b, true;
-		case Kind.LtEq:  return a <= b, true;
-		case Kind.GtEq:  return a >= b, true;
+		#partial switch op {
+		case .Add:   return a + b, true;
+		case .Sub:   return a - b, true;
+		case .Mul:   return a * b, true;
+		case .Quo:   return a / b, true;
+		case .Rem:   return a % b, true;
+		case .Eq:    return a == b, true;
+		case .NotEq: return a != b, true;
+		case .Lt:    return a <  b, true;
+		case .Gt:    return a >  b, true;
+		case .LtEq:  return a <= b, true;
+		case .GtEq:  return a >= b, true;
 		}
 		}
 
 
 	case f64:
 	case f64:
 		b, ok := y.(f64);
 		b, ok := y.(f64);
 		if !ok do return nil, false;
 		if !ok do return nil, false;
 
 
-		switch op {
-		case Kind.Add:   return a + b, true;
-		case Kind.Sub:   return a - b, true;
-		case Kind.Mul:   return a * b, true;
-		case Kind.Quo:   return a / b, true;
-		case Kind.Eq:    return a == b, true;
-		case Kind.NotEq: return a != b, true;
-		case Kind.Lt:    return a <  b, true;
-		case Kind.Gt:    return a >  b, true;
-		case Kind.LtEq:  return a <= b, true;
-		case Kind.GtEq:  return a >= b, true;
+		#partial switch op {
+		case .Add:   return a + b, true;
+		case .Sub:   return a - b, true;
+		case .Mul:   return a * b, true;
+		case .Quo:   return a / b, true;
+		case .Eq:    return a == b, true;
+		case .NotEq: return a != b, true;
+		case .Lt:    return a <  b, true;
+		case .Gt:    return a >  b, true;
+		case .LtEq:  return a <= b, true;
+		case .GtEq:  return a >= b, true;
 		}
 		}
 
 
 	case string:
 	case string:
 		b, ok := y.(string);
 		b, ok := y.(string);
 		if !ok do return nil, false;
 		if !ok do return nil, false;
 
 
-		switch op {
-		case Kind.Add:
+		#partial switch op {
+		case .Add:
 			n := len(a) + len(b);
 			n := len(a) + len(b);
 			data := make([]byte, n);
 			data := make([]byte, n);
 			copy(data[:], a);
 			copy(data[:], a);
@@ -732,12 +732,12 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
 			append(&p.allocated_strings, s);
 			append(&p.allocated_strings, s);
 			return s, true;
 			return s, true;
 
 
-		case Kind.Eq:    return a == b, true;
-		case Kind.NotEq: return a != b, true;
-		case Kind.Lt:    return a <  b, true;
-		case Kind.Gt:    return a >  b, true;
-		case Kind.LtEq:  return a <= b, true;
-		case Kind.GtEq:  return a >= b, true;
+		case .Eq:    return a == b, true;
+		case .NotEq: return a != b, true;
+		case .Lt:    return a <  b, true;
+		case .Gt:    return a >  b, true;
+		case .LtEq:  return a <= b, true;
+		case .GtEq:  return a >= b, true;
 		}
 		}
 	}
 	}
 
 
@@ -755,10 +755,10 @@ parse_binary_expr :: proc(p: ^Parser, prec_in: int) -> (Value, Pos) {
 			}
 			}
 			expect_operator(p);
 			expect_operator(p);
 
 
-			if op.kind == Kind.Question {
+			if op.kind == .Question {
 				cond := expr;
 				cond := expr;
 				x, _ := parse_expr(p);
 				x, _ := parse_expr(p);
-				expect_token(p, Kind.Colon);
+				expect_token(p, .Colon);
 				y, _ := parse_expr(p);
 				y, _ := parse_expr(p);
 
 
 				if t, ok := cond.(bool); ok {
 				if t, ok := cond.(bool); ok {
@@ -791,13 +791,13 @@ parse_expr :: proc(p: ^Parser) -> (Value, Pos) {
 expect_semicolon :: proc(p: ^Parser) {
 expect_semicolon :: proc(p: ^Parser) {
 	kind := p.curr_token.kind;
 	kind := p.curr_token.kind;
 
 
-	switch kind {
-	case Kind.Comma:
+	#partial switch kind {
+	case .Comma:
 		error(p, p.curr_token.pos, "Expected ';', got ','");
 		error(p, p.curr_token.pos, "Expected ';', got ','");
 		next_token(p);
 		next_token(p);
-	case Kind.Semicolon:
+	case .Semicolon:
 		next_token(p);
 		next_token(p);
-	case Kind.EOF:
+	case .EOF:
 		// okay
 		// okay
 	case:
 	case:
 		error(p, p.curr_token.pos, "Expected ';', got %s", p.curr_token.lit);
 		error(p, p.curr_token.pos, "Expected ';', got %s", p.curr_token.lit);
@@ -811,17 +811,17 @@ parse_assignment :: proc(p: ^Parser) -> bool {
 		return p.dict_stack[len(p.dict_stack)-1];
 		return p.dict_stack[len(p.dict_stack)-1];
 	}
 	}
 
 
-	if p.curr_token.kind == Kind.Semicolon {
+	if p.curr_token.kind == .Semicolon {
 		next_token(p);
 		next_token(p);
 		return true;
 		return true;
 	}
 	}
-	if p.curr_token.kind == Kind.EOF {
+	if p.curr_token.kind == .EOF {
 		return false;
 		return false;
 	}
 	}
 
 
 	tok := p.curr_token;
 	tok := p.curr_token;
-	if allow_token(p, Kind.Ident) || allow_token(p, Kind.String) {
-		expect_token(p, Kind.Assign);
+	if allow_token(p, .Ident) || allow_token(p, .String) {
+		expect_token(p, .Assign);
 		name, ok := unquote_string(p, tok);
 		name, ok := unquote_string(p, tok);
 		if !ok do error(p, tok.pos, "Unable to unquote string");
 		if !ok do error(p, tok.pos, "Unable to unquote string");
 		expr, _ := parse_expr(p);
 		expr, _ := parse_expr(p);

+ 1 - 1
core/encoding/cel/token.odin

@@ -137,7 +137,7 @@ kind_to_string := [len(Kind)]string{
 };
 };
 
 
 precedence :: proc(op: Kind) -> int {
 precedence :: proc(op: Kind) -> int {
-	switch op {
+	#partial switch op {
 	case Question:
 	case Question:
 		return 1;
 		return 1;
 	case Or:
 	case Or:

+ 2 - 2
core/encoding/json/marshal.odin

@@ -40,7 +40,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
 	ti := type_info_base(type_info_of(v.id));
 	ti := type_info_base(type_info_of(v.id));
 	a := any{v.data, ti.id};
 	a := any{v.data, ti.id};
 
 
-	switch info in ti.variant {
+	#partial switch info in ti.variant {
 	case Type_Info_Named:
 	case Type_Info_Named:
 		panic("Unreachable");
 		panic("Unreachable");
 
 
@@ -282,7 +282,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
 				return false;
 				return false;
 			}
 			}
 			t := runtime.type_info_base(ti);
 			t := runtime.type_info_base(ti);
-			switch info in t.variant {
+			#partial switch info in t.variant {
 			case runtime.Type_Info_Integer:
 			case runtime.Type_Info_Integer:
 				switch info.endianness {
 				switch info.endianness {
 				case .Platform: return false;
 				case .Platform: return false;

+ 2 - 2
core/encoding/json/parser.odin

@@ -70,7 +70,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
 	defer value.end = token_end_pos(p.prev_token);
 	defer value.end = token_end_pos(p.prev_token);
 
 
 	token := p.curr_token;
 	token := p.curr_token;
-	switch token.kind {
+	#partial switch token.kind {
 	case Kind.Null:
 	case Kind.Null:
 		value.value = Null{};
 		value.value = Null{};
 		advance_token(p);
 		advance_token(p);
@@ -105,7 +105,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
 
 
 	case:
 	case:
 		if p.spec == Specification.JSON5 {
 		if p.spec == Specification.JSON5 {
-			switch token.kind {
+			#partial switch token.kind {
 			case Kind.Infinity:
 			case Kind.Infinity:
 				inf: u64 = 0x7ff0000000000000;
 				inf: u64 = 0x7ff0000000000000;
 				if token.text[0] == '-' {
 				if token.text[0] == '-' {

+ 1 - 1
core/encoding/json/types.odin

@@ -56,7 +56,7 @@ Error :: enum {
 
 
 
 
 destroy_value :: proc(value: Value) {
 destroy_value :: proc(value: Value) {
-	switch v in value.value {
+	#partial switch v in value.value {
 	case Object:
 	case Object:
 		for key, elem in v {
 		for key, elem in v {
 			delete(key);
 			delete(key);

+ 8 - 9
core/encoding/json/validator.odin

@@ -91,28 +91,27 @@ validate_array :: proc(p: ^Parser) -> bool {
 validate_value :: proc(p: ^Parser) -> bool {
 validate_value :: proc(p: ^Parser) -> bool {
 	token := p.curr_token;
 	token := p.curr_token;
 
 
-	using Kind;
-	switch token.kind {
-	case Null, False, True:
+	#partial switch token.kind {
+	case .Null, .False, .True:
 		advance_token(p);
 		advance_token(p);
 		return true;
 		return true;
-	case Integer, Float:
+	case .Integer, .Float:
 		advance_token(p);
 		advance_token(p);
 		return true;
 		return true;
-	case String:
+	case .String:
 		advance_token(p);
 		advance_token(p);
 		return is_valid_string_literal(token.text, p.spec);
 		return is_valid_string_literal(token.text, p.spec);
 
 
-	case Open_Brace:
+	case .Open_Brace:
 		return validate_object(p);
 		return validate_object(p);
 
 
-	case Open_Bracket:
+	case .Open_Bracket:
 		return validate_array(p);
 		return validate_array(p);
 
 
 	case:
 	case:
 		if p.spec == Specification.JSON5 {
 		if p.spec == Specification.JSON5 {
-			switch token.kind {
-			case Infinity, NaN:
+			#partial switch token.kind {
+			case .Infinity, .NaN:
 				advance_token(p);
 				advance_token(p);
 				return true;
 				return true;
 			}
 			}

+ 11 - 8
core/fmt/fmt.odin

@@ -794,7 +794,7 @@ enum_value_to_string :: proc(val: any) -> (string, bool) {
 	v.id = runtime.typeid_base(v.id);
 	v.id = runtime.typeid_base(v.id);
 	type_info := type_info_of(v.id);
 	type_info := type_info_of(v.id);
 
 
-	switch e in type_info.variant {
+	#partial switch e in type_info.variant {
 	case: return "", false;
 	case: return "", false;
 	case runtime.Type_Info_Enum:
 	case runtime.Type_Info_Enum:
 		get_str :: proc(i: $T, e: runtime.Type_Info_Enum) -> (string, bool) {
 		get_str :: proc(i: $T, e: runtime.Type_Info_Enum) -> (string, bool) {
@@ -857,7 +857,7 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
 	}
 	}
 
 
 	type_info := type_info_of(v.id);
 	type_info := type_info_of(v.id);
-	switch e in type_info.variant {
+	#partial switch e in type_info.variant {
 	case: fmt_bad_verb(fi, verb);
 	case: fmt_bad_verb(fi, verb);
 	case runtime.Type_Info_Enum:
 	case runtime.Type_Info_Enum:
 		switch verb {
 		switch verb {
@@ -898,7 +898,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
 			return false;
 			return false;
 		}
 		}
 		t := runtime.type_info_base(ti);
 		t := runtime.type_info_base(ti);
-		switch info in t.variant {
+		#partial switch info in t.variant {
 		case runtime.Type_Info_Integer:
 		case runtime.Type_Info_Integer:
 			switch info.endianness {
 			switch info.endianness {
 			case .Platform: return false;
 			case .Platform: return false;
@@ -912,7 +912,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
 	byte_swap :: bits.byte_swap;
 	byte_swap :: bits.byte_swap;
 
 
 	type_info := type_info_of(v.id);
 	type_info := type_info_of(v.id);
-	switch info in type_info.variant {
+	#partial switch info in type_info.variant {
 	case runtime.Type_Info_Named:
 	case runtime.Type_Info_Named:
 		val := v;
 		val := v;
 		val.id = info.base.id;
 		val.id = info.base.id;
@@ -982,7 +982,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
 }
 }
 fmt_bit_field :: proc(fi: ^Info, v: any, bit_field_name: string = "") {
 fmt_bit_field :: proc(fi: ^Info, v: any, bit_field_name: string = "") {
 	type_info := type_info_of(v.id);
 	type_info := type_info_of(v.id);
-	switch info in type_info.variant {
+	#partial switch info in type_info.variant {
 	case runtime.Type_Info_Named:
 	case runtime.Type_Info_Named:
 		val := v;
 		val := v;
 		val.id = info.base.id;
 		val.id = info.base.id;
@@ -1052,7 +1052,7 @@ fmt_opaque :: proc(fi: ^Info, v: any) {
 		strings.write_byte(fi.buf, '{');
 		strings.write_byte(fi.buf, '{');
 		defer strings.write_byte(fi.buf, '}');
 		defer strings.write_byte(fi.buf, '}');
 
 
-		switch in elem.variant {
+		#partial switch in elem.variant {
 		case rt.Type_Info_Integer, rt.Type_Info_Pointer, rt.Type_Info_Float:
 		case rt.Type_Info_Integer, rt.Type_Info_Pointer, rt.Type_Info_Float:
 			fmt_value(fi, any{v.data, elem.id}, 'v');
 			fmt_value(fi, any{v.data, elem.id}, 'v');
 		case:
 		case:
@@ -1073,8 +1073,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 
 
 	type_info := type_info_of(v.id);
 	type_info := type_info_of(v.id);
 	switch info in type_info.variant {
 	switch info in type_info.variant {
+	case runtime.Type_Info_Any:   // Ignore
+	case runtime.Type_Info_Tuple: // Ignore
+
 	case runtime.Type_Info_Named:
 	case runtime.Type_Info_Named:
-		switch b in info.base.variant {
+		#partial switch b in info.base.variant {
 		case runtime.Type_Info_Struct:
 		case runtime.Type_Info_Struct:
 			if verb != 'v' {
 			if verb != 'v' {
 				fmt_bad_verb(fi, verb);
 				fmt_bad_verb(fi, verb);
@@ -1193,7 +1196,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 				a := any{ptr, info.elem.id};
 				a := any{ptr, info.elem.id};
 
 
 				elem := runtime.type_info_base(info.elem);
 				elem := runtime.type_info_base(info.elem);
-				if elem != nil do switch e in elem.variant {
+				if elem != nil do #partial switch e in elem.variant {
 				case runtime.Type_Info_Array,
 				case runtime.Type_Info_Array,
 				     runtime.Type_Info_Slice,
 				     runtime.Type_Info_Slice,
 				     runtime.Type_Info_Dynamic_Array,
 				     runtime.Type_Info_Dynamic_Array,

+ 2 - 2
core/odin/ast/ast.odin

@@ -331,7 +331,7 @@ Switch_Stmt :: struct {
 	init:       ^Stmt,
 	init:       ^Stmt,
 	cond:       ^Expr,
 	cond:       ^Expr,
 	body:       ^Stmt,
 	body:       ^Stmt,
-	complete:   bool,
+	partial:    bool,
 }
 }
 
 
 Type_Switch_Stmt :: struct {
 Type_Switch_Stmt :: struct {
@@ -341,7 +341,7 @@ Type_Switch_Stmt :: struct {
 	tag:        ^Stmt,
 	tag:        ^Stmt,
 	expr:       ^Expr,
 	expr:       ^Expr,
 	body:       ^Stmt,
 	body:       ^Stmt,
-	complete:   bool,
+	partial:    bool,
 }
 }
 
 
 Branch_Stmt :: struct {
 Branch_Stmt :: struct {

+ 25 - 25
core/odin/parser/parser.odin

@@ -390,7 +390,7 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
 				return true;
 				return true;
 			}
 			}
 		} else {
 		} else {
-			switch p.curr_tok.kind {
+			#partial switch p.curr_tok.kind {
 			case .Close_Brace:
 			case .Close_Brace:
 			case .Close_Paren:
 			case .Close_Paren:
 			case .Else:
 			case .Else:
@@ -475,7 +475,7 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
 	}
 	}
 
 
 	if allow_token(p, .Else) {
 	if allow_token(p, .Else) {
-		switch p.curr_tok.kind {
+		#partial switch p.curr_tok.kind {
 		case .When:
 		case .When:
 			else_stmt = parse_when_stmt(p);
 			else_stmt = parse_when_stmt(p);
 		case .Open_Brace:
 		case .Open_Brace:
@@ -550,7 +550,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
 	}
 	}
 
 
 	if allow_token(p, .Else) {
 	if allow_token(p, .Else) {
-		switch p.curr_tok.kind {
+		#partial switch p.curr_tok.kind {
 		case .If:
 		case .If:
 			else_stmt = parse_if_stmt(p);
 			else_stmt = parse_if_stmt(p);
 		case .Open_Brace:
 		case .Open_Brace:
@@ -859,7 +859,7 @@ parse_foreign_block :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Foreign_Bl
 	docs := p.lead_comment;
 	docs := p.lead_comment;
 
 
 	foreign_library: ^ast.Expr;
 	foreign_library: ^ast.Expr;
-	switch p.curr_tok.kind {
+	#partial switch p.curr_tok.kind {
 	case .Open_Brace:
 	case .Open_Brace:
 		i := ast.new(ast.Ident, tok.pos, end_pos(tok));
 		i := ast.new(ast.Ident, tok.pos, end_pos(tok));
 		i.name = "_";
 		i.name = "_";
@@ -901,7 +901,7 @@ parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl {
 	docs := p.lead_comment;
 	docs := p.lead_comment;
 	tok := expect_token(p, .Foreign);
 	tok := expect_token(p, .Foreign);
 
 
-	switch p.curr_tok.kind {
+	#partial switch p.curr_tok.kind {
 	case .Ident, .Open_Brace:
 	case .Ident, .Open_Brace:
 		return parse_foreign_block(p, tok);
 		return parse_foreign_block(p, tok);
 
 
@@ -955,7 +955,7 @@ parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl {
 
 
 
 
 parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
-	switch p.curr_tok.kind {
+	#partial switch p.curr_tok.kind {
 	// Operands
 	// Operands
 	case .Context, // Also allows for 'context = '
 	case .Context, // Also allows for 'context = '
 	     .Proc,
 	     .Proc,
@@ -1086,12 +1086,12 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 				stmt.state_flags |= {.No_Bounds_Check};
 				stmt.state_flags |= {.No_Bounds_Check};
 			}
 			}
 			return stmt;
 			return stmt;
-		case "complete":
+		case "partial":
 			stmt := parse_stmt(p);
 			stmt := parse_stmt(p);
 			switch s in &stmt.derived {
 			switch s in &stmt.derived {
-			case ast.Switch_Stmt:      s.complete = true;
-			case ast.Type_Switch_Stmt: s.complete = true;
-			case: error(p, stmt.pos, "#complete can only be applied to a switch statement");
+			case ast.Switch_Stmt:      s.partial = true;
+			case ast.Type_Switch_Stmt: s.partial = true;
+			case: error(p, stmt.pos, "#partial can only be applied to a switch statement");
 			}
 			}
 			return stmt;
 			return stmt;
 		case "assert", "panic":
 		case "assert", "panic":
@@ -1130,7 +1130,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 
 
 
 
 token_precedence :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> int {
 token_precedence :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> int {
-	switch kind {
+	#partial switch kind {
 	case .Question:
 	case .Question:
 		return 1;
 		return 1;
 	case .Ellipsis, .Range_Half:
 	case .Ellipsis, .Range_Half:
@@ -1308,7 +1308,7 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags,
 
 
 is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
 is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
 	using Field_Prefix;
 	using Field_Prefix;
-	switch p.curr_tok.kind {
+	#partial switch p.curr_tok.kind {
 	case .EOF:
 	case .EOF:
 		return Invalid;
 		return Invalid;
 	case .Using:
 	case .Using:
@@ -1323,7 +1323,7 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
 	case .Hash:
 	case .Hash:
 		advance_token(p);
 		advance_token(p);
 		defer advance_token(p);
 		defer advance_token(p);
-		switch p.curr_tok.kind {
+		#partial switch p.curr_tok.kind {
 		case .Ident:
 		case .Ident:
 			switch p.curr_tok.text {
 			switch p.curr_tok.text {
 			case "no_alias":
 			case "no_alias":
@@ -1359,7 +1359,7 @@ parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags {
 	for kind in Field_Prefix {
 	for kind in Field_Prefix {
 		count := counts[kind];
 		count := counts[kind];
 		using Field_Prefix;
 		using Field_Prefix;
-		#complete switch kind {
+		switch kind {
 		case Invalid, Unknown: // Ignore
 		case Invalid, Unknown: // Ignore
 		case Using:
 		case Using:
 			if count > 1 do error(p, p.curr_tok.pos, "multiple 'using' in this field list");
 			if count > 1 do error(p, p.curr_tok.pos, "multiple 'using' in this field list");
@@ -1391,7 +1391,7 @@ check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, se
 
 
 	for flag in ast.Field_Flag {
 	for flag in ast.Field_Flag {
 		if flag notin allowed_flags && flag in flags {
 		if flag notin allowed_flags && flag in flags {
-			#complete switch flag {
+			switch flag {
 			case .Using:
 			case .Using:
 				error(p, p.curr_tok.pos, "'using' is not allowed within this field list");
 				error(p, p.curr_tok.pos, "'using' is not allowed within this field list");
 			case .No_Alias:
 			case .No_Alias:
@@ -1832,7 +1832,7 @@ check_poly_params_for_type :: proc(p: ^Parser, poly_params: ^ast.Field_List, tok
 
 
 
 
 parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
-	switch p.curr_tok.kind {
+	#partial switch p.curr_tok.kind {
 	case .Ident:
 	case .Ident:
 		return parse_ident(p);
 		return parse_ident(p);
 
 
@@ -1945,7 +1945,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 		expr := parse_unary_expr(p, lhs);
 		expr := parse_unary_expr(p, lhs);
 
 
 		pi := ast.Proc_Inlining.None;
 		pi := ast.Proc_Inlining.None;
-		switch tok.kind {
+		#partial switch tok.kind {
 		case .Inline:
 		case .Inline:
 			pi = ast.Proc_Inlining.Inline;
 			pi = ast.Proc_Inlining.Inline;
 		case .No_Inline:
 		case .No_Inline:
@@ -2537,7 +2537,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
 	loop := true;
 	loop := true;
 	is_lhs := lhs;
 	is_lhs := lhs;
 	for loop {
 	for loop {
-		switch p.curr_tok.kind {
+		#partial switch p.curr_tok.kind {
 		case:
 		case:
 			loop = false;
 			loop = false;
 
 
@@ -2556,7 +2556,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
 			p.expr_level += 1;
 			p.expr_level += 1;
 			open := expect_token(p, .Open_Bracket);
 			open := expect_token(p, .Open_Bracket);
 
 
-			switch p.curr_tok.kind {
+			#partial switch p.curr_tok.kind {
 			case .Colon, .Ellipsis, .Range_Half:
 			case .Colon, .Ellipsis, .Range_Half:
 				// NOTE(bill): Do not err yet
 				// NOTE(bill): Do not err yet
 				break;
 				break;
@@ -2564,7 +2564,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
 				indicies[0] = parse_expr(p, false);
 				indicies[0] = parse_expr(p, false);
 			}
 			}
 
 
-			switch p.curr_tok.kind {
+			#partial switch p.curr_tok.kind {
 			case .Ellipsis, .Range_Half:
 			case .Ellipsis, .Range_Half:
 				error(p, p.curr_tok.pos, "expected a colon, not a range");
 				error(p, p.curr_tok.pos, "expected a colon, not a range");
 				fallthrough;
 				fallthrough;
@@ -2602,7 +2602,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
 
 
 		case .Period:
 		case .Period:
 			tok := expect_token(p, .Period);
 			tok := expect_token(p, .Period);
-			switch p.curr_tok.kind {
+			#partial switch p.curr_tok.kind {
 			case .Ident:
 			case .Ident:
 				field := parse_ident(p);
 				field := parse_ident(p);
 
 
@@ -2659,7 +2659,7 @@ parse_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 	return parse_binary_expr(p, lhs, 0+1);
 	return parse_binary_expr(p, lhs, 0+1);
 }
 }
 parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
-	switch p.curr_tok.kind {
+	#partial switch p.curr_tok.kind {
 	case .Transmute, .Cast:
 	case .Transmute, .Cast:
 		tok := advance_token(p);
 		tok := advance_token(p);
 		open := expect_token(p, .Open_Paren);
 		open := expect_token(p, .Open_Paren);
@@ -2812,7 +2812,7 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
 	case op.kind == .Colon:
 	case op.kind == .Colon:
 		expect_token_after(p, .Colon, "identifier list");
 		expect_token_after(p, .Colon, "identifier list");
 		if .Label in flags && len(lhs) == 1 {
 		if .Label in flags && len(lhs) == 1 {
-			switch p.curr_tok.kind {
+			#partial switch p.curr_tok.kind {
 			case .Open_Brace, .If, .For, .Switch:
 			case .Open_Brace, .If, .For, .Switch:
 				label := lhs[0];
 				label := lhs[0];
 				stmt := parse_stmt(p);
 				stmt := parse_stmt(p);
@@ -2847,7 +2847,7 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou
 	values: []^ast.Expr;
 	values: []^ast.Expr;
 	type := parse_type_or_ident(p);
 	type := parse_type_or_ident(p);
 
 
-	switch p.curr_tok.kind {
+	#partial switch p.curr_tok.kind {
 	case .Eq, .Colon:
 	case .Eq, .Colon:
 		sep := advance_token(p);
 		sep := advance_token(p);
 		is_mutable = sep.kind != .Colon;
 		is_mutable = sep.kind != .Colon;
@@ -2910,7 +2910,7 @@ parse_import_decl :: proc(p: ^Parser, kind := Import_Decl_Kind.Standard) -> ^ast
 	import_name: tokenizer.Token;
 	import_name: tokenizer.Token;
 	is_using := kind != Import_Decl_Kind.Standard;
 	is_using := kind != Import_Decl_Kind.Standard;
 
 
-	switch p.curr_tok.kind {
+	#partial switch p.curr_tok.kind {
 	case .Ident:
 	case .Ident:
 		import_name = advance_token(p);
 		import_name = advance_token(p);
 	case:
 	case:

+ 1 - 1
core/odin/tokenizer/token.odin

@@ -313,7 +313,7 @@ is_literal  :: proc(kind: Token_Kind) -> bool {
 	return Token_Kind.B_Literal_Begin  < kind && kind < Token_Kind.B_Literal_End;
 	return Token_Kind.B_Literal_Begin  < kind && kind < Token_Kind.B_Literal_End;
 }
 }
 is_operator :: proc(kind: Token_Kind) -> bool {
 is_operator :: proc(kind: Token_Kind) -> bool {
-	switch kind {
+	#partial switch kind {
 	case .B_Operator_Begin .. .B_Operator_End:
 	case .B_Operator_Begin .. .B_Operator_End:
 		return true;
 		return true;
 	case .In, .Notin:
 	case .In, .Notin:

+ 1 - 1
core/reflect/reflect.odin

@@ -37,7 +37,7 @@ Type_Kind :: enum {
 type_kind :: proc(T: typeid) -> Type_Kind {
 type_kind :: proc(T: typeid) -> Type_Kind {
 	ti := type_info_of(T);
 	ti := type_info_of(T);
 	if ti != nil {
 	if ti != nil {
-		#complete switch _ in ti.variant {
+		switch _ in ti.variant {
 		case runtime.Type_Info_Named:         return .Named;
 		case runtime.Type_Info_Named:         return .Named;
 		case runtime.Type_Info_Integer:       return .Integer;
 		case runtime.Type_Info_Integer:       return .Integer;
 		case runtime.Type_Info_Rune:          return .Rune;
 		case runtime.Type_Info_Rune:          return .Rune;

+ 14 - 2
core/reflect/types.odin

@@ -40,6 +40,14 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
 		_, ok := b.variant.(rt.Type_Info_Complex);
 		_, ok := b.variant.(rt.Type_Info_Complex);
 		return ok;
 		return ok;
 
 
+	case rt.Type_Info_Quaternion:
+		_, ok := b.variant.(rt.Type_Info_Quaternion);
+		return ok;
+
+	case rt.Type_Info_Type_Id:
+		_, ok := b.variant.(rt.Type_Info_Type_Id);
+		return ok;
+
 	case rt.Type_Info_String:
 	case rt.Type_Info_String:
 		_, ok := b.variant.(rt.Type_Info_String);
 		_, ok := b.variant.(rt.Type_Info_String);
 		return ok;
 		return ok;
@@ -174,7 +182,7 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
 
 
 is_signed :: proc(info: ^rt.Type_Info) -> bool {
 is_signed :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
 	if info == nil do return false;
-	switch i in rt.type_info_base(info).variant {
+	#partial switch i in rt.type_info_base(info).variant {
 	case rt.Type_Info_Integer: return i.signed;
 	case rt.Type_Info_Integer: return i.signed;
 	case rt.Type_Info_Float:   return true;
 	case rt.Type_Info_Float:   return true;
 	}
 	}
@@ -309,6 +317,7 @@ write_type :: proc(buf: ^strings.Builder, ti: ^rt.Type_Info) {
 			write_byte(buf, info.signed ? 'i' : 'u');
 			write_byte(buf, info.signed ? 'i' : 'u');
 			write_i64(buf, i64(8*ti.size), 10);
 			write_i64(buf, i64(8*ti.size), 10);
 			switch info.endianness {
 			switch info.endianness {
+			case .Platform: // Okay
 			case .Little: write_string(buf, "le");
 			case .Little: write_string(buf, "le");
 			case .Big:    write_string(buf, "be");
 			case .Big:    write_string(buf, "be");
 			}
 			}
@@ -321,6 +330,9 @@ write_type :: proc(buf: ^strings.Builder, ti: ^rt.Type_Info) {
 	case rt.Type_Info_Complex:
 	case rt.Type_Info_Complex:
 		write_string(buf, "complex");
 		write_string(buf, "complex");
 		write_i64(buf, i64(8*ti.size), 10);
 		write_i64(buf, i64(8*ti.size), 10);
+	case rt.Type_Info_Quaternion:
+		write_string(buf, "quaternion");
+		write_i64(buf, i64(8*ti.size), 10);
 	case rt.Type_Info_String:
 	case rt.Type_Info_String:
 		if info.is_cstring {
 		if info.is_cstring {
 			write_string(buf, "cstring");
 			write_string(buf, "cstring");
@@ -399,7 +411,7 @@ write_type :: proc(buf: ^strings.Builder, ti: ^rt.Type_Info) {
 		write_type(buf, info.value);
 		write_type(buf, info.value);
 
 
 	case rt.Type_Info_Struct:
 	case rt.Type_Info_Struct:
-		#complete switch info.soa_kind {
+		switch info.soa_kind {
 		case .None: // Ignore
 		case .None: // Ignore
 		case .Fixed:
 		case .Fixed:
 			write_string(buf, "#soa[");
 			write_string(buf, "#soa[");

+ 2 - 2
core/runtime/core.odin

@@ -305,7 +305,7 @@ type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
 
 
 	base := info;
 	base := info;
 	loop: for {
 	loop: for {
-		switch i in base.variant {
+		#partial switch i in base.variant {
 		case Type_Info_Named: base = i.base;
 		case Type_Info_Named: base = i.base;
 		case: break loop;
 		case: break loop;
 		}
 		}
@@ -319,7 +319,7 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
 
 
 	base := info;
 	base := info;
 	loop: for {
 	loop: for {
-		switch i in base.variant {
+		#partial switch i in base.variant {
 		case Type_Info_Named:  base = i.base;
 		case Type_Info_Named:  base = i.base;
 		case Type_Info_Enum:   base = i.base;
 		case Type_Info_Enum:   base = i.base;
 		case Type_Info_Opaque: base = i.elem;
 		case Type_Info_Opaque: base = i.elem;

+ 5 - 2
core/runtime/internal.odin

@@ -110,6 +110,9 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
 	case Type_Info_Complex:
 	case Type_Info_Complex:
 		os.write_string(fd, "complex");
 		os.write_string(fd, "complex");
 		print_u64(fd, u64(8*ti.size));
 		print_u64(fd, u64(8*ti.size));
+	case Type_Info_Quaternion:
+		os.write_string(fd, "quaternion");
+		print_u64(fd, u64(8*ti.size));
 	case Type_Info_String:
 	case Type_Info_String:
 		os.write_string(fd, "string");
 		os.write_string(fd, "string");
 	case Type_Info_Boolean:
 	case Type_Info_Boolean:
@@ -183,7 +186,7 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
 		print_type(fd, info.value);
 		print_type(fd, info.value);
 
 
 	case Type_Info_Struct:
 	case Type_Info_Struct:
-		#complete switch info.soa_kind {
+		switch info.soa_kind {
 		case .None: // Ignore
 		case .None: // Ignore
 		case .Fixed:
 		case .Fixed:
 			os.write_string(fd, "#soa[");
 			os.write_string(fd, "#soa[");
@@ -263,7 +266,7 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
 	case Type_Info_Bit_Set:
 	case Type_Info_Bit_Set:
 		os.write_string(fd, "bit_set[");
 		os.write_string(fd, "bit_set[");
 
 
-		switch elem in type_info_base(info.elem).variant {
+		#partial switch elem in type_info_base(info.elem).variant {
 		case Type_Info_Enum:
 		case Type_Info_Enum:
 			print_type(fd, info.elem);
 			print_type(fd, info.elem);
 		case Type_Info_Rune:
 		case Type_Info_Rune:

+ 11 - 11
core/sync/atomic.odin

@@ -11,7 +11,7 @@ Ordering :: enum {
 }
 }
 
 
 strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> Ordering {
 strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> Ordering {
-	#complete switch order {
+	switch order {
 	case .Relaxed:                 return .Relaxed;
 	case .Relaxed:                 return .Relaxed;
 	case .Release:                 return .Relaxed;
 	case .Release:                 return .Relaxed;
 	case .Acquire:                 return .Acquire;
 	case .Acquire:                 return .Acquire;
@@ -22,7 +22,7 @@ strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> Ord
 }
 }
 
 
 fence :: inline proc "contextless" ($order: Ordering) {
 fence :: inline proc "contextless" ($order: Ordering) {
-	#complete switch order {
+	switch order {
 	case .Relaxed:                 panic("there is no such thing as a relaxed fence");
 	case .Relaxed:                 panic("there is no such thing as a relaxed fence");
 	case .Release:                 intrinsics.atomic_fence_rel();
 	case .Release:                 intrinsics.atomic_fence_rel();
 	case .Acquire:                 intrinsics.atomic_fence_acq();
 	case .Acquire:                 intrinsics.atomic_fence_acq();
@@ -34,7 +34,7 @@ fence :: inline proc "contextless" ($order: Ordering) {
 
 
 
 
 atomic_store :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) {
 atomic_store :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) {
-	#complete switch order {
+	switch order {
 	case .Relaxed:                 intrinsics.atomic_store_relaxed(dst, val);
 	case .Relaxed:                 intrinsics.atomic_store_relaxed(dst, val);
 	case .Release:                 intrinsics.atomic_store_rel(dst, val);
 	case .Release:                 intrinsics.atomic_store_rel(dst, val);
 	case .Sequentially_Consistent: intrinsics.atomic_store(dst, val);
 	case .Sequentially_Consistent: intrinsics.atomic_store(dst, val);
@@ -45,7 +45,7 @@ atomic_store :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) {
 }
 }
 
 
 atomic_load :: inline proc "contextless" (dst: ^$T, $order: Ordering) -> T {
 atomic_load :: inline proc "contextless" (dst: ^$T, $order: Ordering) -> T {
-	#complete switch order {
+	switch order {
 	case .Relaxed:                 return intrinsics.atomic_load_relaxed(dst);
 	case .Relaxed:                 return intrinsics.atomic_load_relaxed(dst);
 	case .Acquire:                 return intrinsics.atomic_load_acq(dst);
 	case .Acquire:                 return intrinsics.atomic_load_acq(dst);
 	case .Sequentially_Consistent: return intrinsics.atomic_load(dst);
 	case .Sequentially_Consistent: return intrinsics.atomic_load(dst);
@@ -57,7 +57,7 @@ atomic_load :: inline proc "contextless" (dst: ^$T, $order: Ordering) -> T {
 }
 }
 
 
 atomic_swap :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
 atomic_swap :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
-	#complete switch order {
+	switch order {
 	case .Relaxed:                 return intrinsics.atomic_xchg_relaxed(dst, val);
 	case .Relaxed:                 return intrinsics.atomic_xchg_relaxed(dst, val);
 	case .Release:                 return intrinsics.atomic_xchg_rel(dst, val);
 	case .Release:                 return intrinsics.atomic_xchg_rel(dst, val);
 	case .Acquire:                 return intrinsics.atomic_xchg_acq(dst, val);
 	case .Acquire:                 return intrinsics.atomic_xchg_acq(dst, val);
@@ -138,7 +138,7 @@ atomic_compare_exchange_weak :: inline proc "contextless" (dst: ^$T, old, new: T
 
 
 
 
 atomic_add :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
 atomic_add :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
-	#complete switch order {
+	switch order {
 	case .Relaxed:                 return intrinsics.atomic_add_relaxed(dst, val);
 	case .Relaxed:                 return intrinsics.atomic_add_relaxed(dst, val);
 	case .Release:                 return intrinsics.atomic_add_rel(dst, val);
 	case .Release:                 return intrinsics.atomic_add_rel(dst, val);
 	case .Acquire:                 return intrinsics.atomic_add_acq(dst, val);
 	case .Acquire:                 return intrinsics.atomic_add_acq(dst, val);
@@ -150,7 +150,7 @@ atomic_add :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
 }
 }
 
 
 atomic_sub :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
 atomic_sub :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
-	#complete switch order {
+	switch order {
 	case .Relaxed:                 return intrinsics.atomic_sub_relaxed(dst, val);
 	case .Relaxed:                 return intrinsics.atomic_sub_relaxed(dst, val);
 	case .Release:                 return intrinsics.atomic_sub_rel(dst, val);
 	case .Release:                 return intrinsics.atomic_sub_rel(dst, val);
 	case .Acquire:                 return intrinsics.atomic_sub_acq(dst, val);
 	case .Acquire:                 return intrinsics.atomic_sub_acq(dst, val);
@@ -162,7 +162,7 @@ atomic_sub :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
 }
 }
 
 
 atomic_and :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
 atomic_and :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
-	#complete switch order {
+	switch order {
 	case .Relaxed:                 return intrinsics.atomic_and_relaxed(dst, val);
 	case .Relaxed:                 return intrinsics.atomic_and_relaxed(dst, val);
 	case .Release:                 return intrinsics.atomic_and_rel(dst, val);
 	case .Release:                 return intrinsics.atomic_and_rel(dst, val);
 	case .Acquire:                 return intrinsics.atomic_and_acq(dst, val);
 	case .Acquire:                 return intrinsics.atomic_and_acq(dst, val);
@@ -174,7 +174,7 @@ atomic_and :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
 }
 }
 
 
 atomic_nand :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
 atomic_nand :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
-	#complete switch order {
+	switch order {
 	case .Relaxed:                 return intrinsics.atomic_nand_relaxed(dst, val);
 	case .Relaxed:                 return intrinsics.atomic_nand_relaxed(dst, val);
 	case .Release:                 return intrinsics.atomic_nand_rel(dst, val);
 	case .Release:                 return intrinsics.atomic_nand_rel(dst, val);
 	case .Acquire:                 return intrinsics.atomic_nand_acq(dst, val);
 	case .Acquire:                 return intrinsics.atomic_nand_acq(dst, val);
@@ -186,7 +186,7 @@ atomic_nand :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
 }
 }
 
 
 atomic_or :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
 atomic_or :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
-	#complete switch order {
+	switch order {
 	case .Relaxed:                 return intrinsics.atomic_or_relaxed(dst, val);
 	case .Relaxed:                 return intrinsics.atomic_or_relaxed(dst, val);
 	case .Release:                 return intrinsics.atomic_or_rel(dst, val);
 	case .Release:                 return intrinsics.atomic_or_rel(dst, val);
 	case .Acquire:                 return intrinsics.atomic_or_acq(dst, val);
 	case .Acquire:                 return intrinsics.atomic_or_acq(dst, val);
@@ -198,7 +198,7 @@ atomic_or :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T
 }
 }
 
 
 atomic_xor :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
 atomic_xor :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
-	#complete switch order {
+	switch order {
 	case .Relaxed:                 return intrinsics.atomic_xor_relaxed(dst, val);
 	case .Relaxed:                 return intrinsics.atomic_xor_relaxed(dst, val);
 	case .Release:                 return intrinsics.atomic_xor_rel(dst, val);
 	case .Release:                 return intrinsics.atomic_xor_rel(dst, val);
 	case .Acquire:                 return intrinsics.atomic_xor_acq(dst, val);
 	case .Acquire:                 return intrinsics.atomic_xor_acq(dst, val);

+ 15 - 5
examples/demo/demo.odin

@@ -7,6 +7,7 @@ import "core:thread"
 import "core:reflect"
 import "core:reflect"
 import "intrinsics"
 import "intrinsics"
 
 
+
 /*
 /*
 	The Odin programming language is fast, concise, readable, pragmatic and open sourced.
 	The Odin programming language is fast, concise, readable, pragmatic and open sourced.
 	It is designed with the intent of replacing C with the following goals:
 	It is designed with the intent of replacing C with the following goals:
@@ -1233,8 +1234,8 @@ implicit_selector_expression :: proc() {
 }
 }
 
 
 
 
-complete_switch :: proc() {
-	fmt.println("\n# complete_switch");
+partial_switch :: proc() {
+	fmt.println("\n# partial_switch");
 	{ // enum
 	{ // enum
 		Foo :: enum {
 		Foo :: enum {
 			A,
 			A,
@@ -1244,22 +1245,31 @@ complete_switch :: proc() {
 		};
 		};
 
 
 		f := Foo.A;
 		f := Foo.A;
-		#complete switch f {
+		switch f {
 		case .A: fmt.println("A");
 		case .A: fmt.println("A");
 		case .B: fmt.println("B");
 		case .B: fmt.println("B");
 		case .C: fmt.println("C");
 		case .C: fmt.println("C");
 		case .D: fmt.println("D");
 		case .D: fmt.println("D");
 		case:    fmt.println("?");
 		case:    fmt.println("?");
 		}
 		}
+
+		#partial switch f {
+		case .A: fmt.println("A");
+		case .D: fmt.println("D");
+		}
 	}
 	}
 	{ // union
 	{ // union
 		Foo :: union {int, bool};
 		Foo :: union {int, bool};
 		f: Foo = 123;
 		f: Foo = 123;
-		#complete switch in f {
+		switch in f {
 		case int:  fmt.println("int");
 		case int:  fmt.println("int");
 		case bool: fmt.println("bool");
 		case bool: fmt.println("bool");
 		case:
 		case:
 		}
 		}
+
+		#partial switch in f {
+		case bool: fmt.println("bool");
+		}
 	}
 	}
 }
 }
 
 
@@ -1820,7 +1830,7 @@ main :: proc() {
 		array_programming();
 		array_programming();
 		map_type();
 		map_type();
 		implicit_selector_expression();
 		implicit_selector_expression();
-		complete_switch();
+		partial_switch();
 		cstring_example();
 		cstring_example();
 		bit_set_type();
 		bit_set_type();
 		deferred_procedure_associations();
 		deferred_procedure_associations();

+ 24 - 33
src/check_stmt.cpp

@@ -807,12 +807,11 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 		}
 		}
 	}
 	}
 
 
-	bool complete = ss->complete;
+	bool is_partial = ss->partial;
 
 
-	if (complete) {
+	if (is_partial) {
 		if (!is_type_enum(x.type)) {
 		if (!is_type_enum(x.type)) {
-			error(x.expr, "#complete switch statement can be only used with an enum type");
-			complete = false;
+			error(x.expr, "#partial switch statement can be only used with an enum type");
 		}
 		}
 	}
 	}
 
 
@@ -877,9 +876,6 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 				Operand a1 = lhs;
 				Operand a1 = lhs;
 				Operand b1 = rhs;
 				Operand b1 = rhs;
 				check_comparison(ctx, &a1, &b1, Token_LtEq);
 				check_comparison(ctx, &a1, &b1, Token_LtEq);
-				if (complete) {
-					error(lhs.expr, "#complete switch statement does not allow ranges");
-				}
 
 
 				add_constant_switch_case(ctx, &seen, lhs);
 				add_constant_switch_case(ctx, &seen, lhs);
 				if (upper_op == Token_GtEq) {
 				if (upper_op == Token_GtEq) {
@@ -926,9 +922,6 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 						continue;
 						continue;
 					}
 					}
 					if (y.mode != Addressing_Constant) {
 					if (y.mode != Addressing_Constant) {
-						if (complete) {
-							error(y.expr, "#complete switch statement only allows constant case clauses");
-						}
 						continue;
 						continue;
 					}
 					}
 
 
@@ -942,7 +935,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 		check_close_scope(ctx);
 		check_close_scope(ctx);
 	}
 	}
 
 
-	if (complete) {
+	if (!is_partial && is_type_enum(x.type)) {
 		Type *et = base_type(x.type);
 		Type *et = base_type(x.type);
 		GB_ASSERT(is_type_enum(et));
 		GB_ASSERT(is_type_enum(et));
 		auto fields = et->Enum.fields;
 		auto fields = et->Enum.fields;
@@ -968,18 +961,17 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 			defer (begin_error_block());
 			defer (begin_error_block());
 
 
 			if (unhandled.count == 1) {
 			if (unhandled.count == 1) {
-				error_no_newline(node, "Unhandled switch case: ");
+				error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string));
 			} else {
 			} else {
 				error_no_newline(node, "Unhandled switch cases: ");
 				error_no_newline(node, "Unhandled switch cases: ");
-			}
-			for_array(i, unhandled) {
-				Entity *f = unhandled[i];
-				if (i > 0)  {
-					error_line(", ");
+				for_array(i, unhandled) {
+					Entity *f = unhandled[i];
+					error_line("\t%.*s\n", LIT(f->token.string));
 				}
 				}
-				error_line("%.*s", LIT(f->token.string));
 			}
 			}
 			error_line("\n");
 			error_line("\n");
+
+			error_line("\tSuggestion: Was '#partial switch' wanted? This replaces the previous '#complete switch'.\n");
 		}
 		}
 	}
 	}
 }
 }
@@ -1042,11 +1034,10 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 		return;
 		return;
 	}
 	}
 
 
-	bool complete = ss->complete;
-	if (complete) {
+	bool is_partial = ss->partial;
+	if (is_partial) {
 		if (switch_kind != TypeSwitch_Union) {
 		if (switch_kind != TypeSwitch_Union) {
-			error(node, "#complete switch statement may only be used with a union");
-			complete = false;
+			error(node, "#partial switch statement may only be used with a union");
 		}
 		}
 	}
 	}
 
 
@@ -1174,7 +1165,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 		check_close_scope(ctx);
 		check_close_scope(ctx);
 	}
 	}
 
 
-	if (complete) {
+	if (!is_partial && is_type_union(type_deref(x.type))) {
 		Type *ut = base_type(type_deref(x.type));
 		Type *ut = base_type(type_deref(x.type));
 		GB_ASSERT(is_type_union(ut));
 		GB_ASSERT(is_type_union(ut));
 		auto variants = ut->Union.variants;
 		auto variants = ut->Union.variants;
@@ -1191,20 +1182,20 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 
 
 		if (unhandled.count > 0) {
 		if (unhandled.count > 0) {
 			if (unhandled.count == 1) {
 			if (unhandled.count == 1) {
-				error_no_newline(node, "Unhandled switch case: ");
+				gbString s = type_to_string(unhandled[0]);
+				error_no_newline(node, "Unhandled switch case: %s", s);
+				gb_string_free(s);
 			} else {
 			} else {
-				error_no_newline(node, "Unhandled switch cases: ");
-			}
-			for_array(i, unhandled) {
-				Type *t = unhandled[i];
-				if (i > 0)  {
-					error_line(", ");
+				error_no_newline(node, "Unhandled switch cases:\n");
+				for_array(i, unhandled) {
+					Type *t = unhandled[i];
+					gbString s = type_to_string(t);
+					error_line("\t%s\n", s);
+					gb_string_free(s);
 				}
 				}
-				gbString s = type_to_string(t);
-				error_line("%s", s);
-				gb_string_free(s);
 			}
 			}
 			error_line("\n");
 			error_line("\n");
+			error_line("\tSuggestion: Was '#partial switch' wanted? This replaces the previous '#complete switch'.\n");
 		}
 		}
 	}
 	}
 }
 }

+ 20 - 2
src/parser.cpp

@@ -766,6 +766,7 @@ Ast *ast_switch_stmt(AstFile *f, Token token, Ast *init, Ast *tag, Ast *body) {
 	result->SwitchStmt.init  = init;
 	result->SwitchStmt.init  = init;
 	result->SwitchStmt.tag   = tag;
 	result->SwitchStmt.tag   = tag;
 	result->SwitchStmt.body  = body;
 	result->SwitchStmt.body  = body;
+	result->SwitchStmt.partial = false;
 	return result;
 	return result;
 }
 }
 
 
@@ -775,6 +776,7 @@ Ast *ast_type_switch_stmt(AstFile *f, Token token, Ast *tag, Ast *body) {
 	result->TypeSwitchStmt.token = token;
 	result->TypeSwitchStmt.token = token;
 	result->TypeSwitchStmt.tag   = tag;
 	result->TypeSwitchStmt.tag   = tag;
 	result->TypeSwitchStmt.body  = body;
 	result->TypeSwitchStmt.body  = body;
+	result->TypeSwitchStmt.partial = false;
 	return result;
 	return result;
 }
 }
 
 
@@ -4060,16 +4062,32 @@ Ast *parse_stmt(AstFile *f) {
 			s = parse_stmt(f);
 			s = parse_stmt(f);
 			switch (s->kind) {
 			switch (s->kind) {
 			case Ast_SwitchStmt:
 			case Ast_SwitchStmt:
-				s->SwitchStmt.complete = true;
+				s->SwitchStmt.partial = false;
+				syntax_warning(token, "#complete is now the default and has been replaced with its opposite: #partial");
 				break;
 				break;
 			case Ast_TypeSwitchStmt:
 			case Ast_TypeSwitchStmt:
-				s->TypeSwitchStmt.complete = true;
+				s->TypeSwitchStmt.partial = false;
+				syntax_warning(token, "#complete is now the default and has been replaced with its opposite: #partial");
 				break;
 				break;
 			default:
 			default:
 				syntax_error(token, "#complete can only be applied to a switch statement");
 				syntax_error(token, "#complete can only be applied to a switch statement");
 				break;
 				break;
 			}
 			}
 			return s;
 			return s;
+		} else if (tag == "partial") {
+			s = parse_stmt(f);
+			switch (s->kind) {
+			case Ast_SwitchStmt:
+				s->SwitchStmt.partial = true;
+				break;
+			case Ast_TypeSwitchStmt:
+				s->TypeSwitchStmt.partial = true;
+				break;
+			default:
+				syntax_error(token, "#partial can only be applied to a switch statement");
+				break;
+			}
+			return s;
 		} else if (tag == "assert") {
 		} else if (tag == "assert") {
 			Ast *t = ast_basic_directive(f, hash_token, tag);
 			Ast *t = ast_basic_directive(f, hash_token, tag);
 			return ast_expr_stmt(f, parse_call_expr(f, t));
 			return ast_expr_stmt(f, parse_call_expr(f, t));

+ 12 - 12
src/parser.hpp

@@ -363,20 +363,20 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
 		Entity *implicit_entity; \
 		Entity *implicit_entity; \
 	}) \
 	}) \
 	AST_KIND(SwitchStmt, "switch statement", struct { \
 	AST_KIND(SwitchStmt, "switch statement", struct { \
-		Token token;    \
-		Ast *label;    \
-		Ast *init;     \
-		Ast *tag;      \
-		Ast *body;     \
-		bool complete; \
+		Token token;  \
+		Ast *label;   \
+		Ast *init;    \
+		Ast *tag;     \
+		Ast *body;    \
+		bool partial; \
 	}) \
 	}) \
 	AST_KIND(TypeSwitchStmt, "type switch statement", struct { \
 	AST_KIND(TypeSwitchStmt, "type switch statement", struct { \
-		Token token;   \
-		Ast *label;    \
-		Ast *tag;      \
-		Ast *body;     \
-		bool complete; \
-	}) \
+		Token token; \
+		Ast *label;  \
+		Ast *tag;    \
+		Ast *body;   \
+		bool partial; \
+}) \
 	AST_KIND(DeferStmt,  "defer statement",  struct { Token token; Ast *stmt; }) \
 	AST_KIND(DeferStmt,  "defer statement",  struct { Token token; Ast *stmt; }) \
 	AST_KIND(BranchStmt, "branch statement", struct { Token token; Ast *label; }) \
 	AST_KIND(BranchStmt, "branch statement", struct { Token token; Ast *label; }) \
 	AST_KIND(UsingStmt,  "using statement",  struct { \
 	AST_KIND(UsingStmt,  "using statement",  struct { \