~mna/snow

639c42868508de1696aa4d67f15985c8fd7b5372 — Martin Angers 1 year, 9 months ago 74ef139 + cfe87e6
Merge branch 'wip-tuple'
114 files changed, 1507 insertions(+), 406 deletions(-)

M cmd/goast/main.go
M doc/swift.ebnf
D exp/codegen/add_one.ssa
D exp/codegen/array.ssa
D exp/codegen/hello.ssa
D exp/codegen/literal_string.ssa
D exp/codegen/new.ssa
D exp/codegen/optional.ssa
M pkg/ast/ast.go
M pkg/ast/visitor.go
M pkg/codegen/mangle.go
M pkg/codegen/position.go
A pkg/codegen/testdata/fn_tuple.snow
A pkg/codegen/testdata/fn_tuple.snow.err
A pkg/codegen/testdata/fn_tuple.snow.want
M pkg/codegen/translate.go
M pkg/grammar/grammar.ebnf
M pkg/parser/parser.go
A pkg/parser/testdata/fn_invalid_empty_tuple.snow
A pkg/parser/testdata/fn_invalid_empty_tuple.snow.err
A pkg/parser/testdata/fn_invalid_empty_tuple.snow.want
A pkg/parser/testdata/fn_nested_tuples.snow
A pkg/parser/testdata/fn_nested_tuples.snow.err
A pkg/parser/testdata/fn_nested_tuples.snow.want
A pkg/parser/testdata/var_invalid_empty_tuple.snow
A pkg/parser/testdata/var_invalid_empty_tuple.snow.err
A pkg/parser/testdata/var_invalid_empty_tuple.snow.want
A pkg/parser/testdata/var_paren_type.snow
A pkg/parser/testdata/var_paren_type.snow.err
A pkg/parser/testdata/var_paren_type.snow.want
A pkg/parser/testdata/var_paren_type_trail_comma.snow
A pkg/parser/testdata/var_paren_type_trail_comma.snow.err
A pkg/parser/testdata/var_paren_type_trail_comma.snow.want
A pkg/parser/testdata/var_tuple.snow
A pkg/parser/testdata/var_tuple.snow.err
A pkg/parser/testdata/var_tuple.snow.want
A pkg/parser/testdata/var_tuple_type.snow
A pkg/parser/testdata/var_tuple_type.snow.err
A pkg/parser/testdata/var_tuple_type.snow.want
M pkg/printer/printer.go
M pkg/scanner/scanner.go
M pkg/typecheck/check_pass.go
M pkg/typecheck/testdata/check/fn_access_invalid_struct_field.snow.want
A pkg/typecheck/testdata/check/fn_complex_selectors.snow.err
A pkg/typecheck/testdata/check/fn_complex_selectors.snow.want
A pkg/typecheck/testdata/check/fn_invalid_tuple_access.snow.err
A pkg/typecheck/testdata/check/fn_invalid_tuple_access.snow.want
A pkg/typecheck/testdata/check/fn_nested_tuple_type.snow.err
A pkg/typecheck/testdata/check/fn_nested_tuple_type.snow.want
M pkg/typecheck/testdata/check/fn_non_type_selectors.snow.err
M pkg/typecheck/testdata/check/fn_non_type_selectors.snow.want
A pkg/typecheck/testdata/check/fn_tuple_assign_compatible_types.snow.err
A pkg/typecheck/testdata/check/fn_tuple_assign_compatible_types.snow.want
A pkg/typecheck/testdata/check/fn_tuple_assign_incompatible_types.snow.err
A pkg/typecheck/testdata/check/fn_tuple_assign_incompatible_types.snow.want
A pkg/typecheck/testdata/check/fn_tuple_expr_select_field.snow.err
A pkg/typecheck/testdata/check/fn_tuple_expr_select_field.snow.want
A pkg/typecheck/testdata/check/let_invalid_tuple_type.snow.err
A pkg/typecheck/testdata/check/let_invalid_tuple_type.snow.want
A pkg/typecheck/testdata/check/var_tuple_type.snow.err
A pkg/typecheck/testdata/check/var_tuple_type.snow.want
A pkg/typecheck/testdata/fn_complex_selectors.snow
A pkg/typecheck/testdata/fn_invalid_tuple_access.snow
A pkg/typecheck/testdata/fn_nested_tuple_type.snow
A pkg/typecheck/testdata/fn_tuple_assign_compatible_types.snow
A pkg/typecheck/testdata/fn_tuple_assign_incompatible_types.snow
A pkg/typecheck/testdata/fn_tuple_expr_select_field.snow
A pkg/typecheck/testdata/let_invalid_tuple_type.snow
A pkg/typecheck/testdata/scopes/fn_complex_selectors.snow.err
A pkg/typecheck/testdata/scopes/fn_complex_selectors.snow.want
A pkg/typecheck/testdata/scopes/fn_invalid_tuple_access.snow.err
A pkg/typecheck/testdata/scopes/fn_invalid_tuple_access.snow.want
A pkg/typecheck/testdata/scopes/fn_nested_tuple_type.snow.err
A pkg/typecheck/testdata/scopes/fn_nested_tuple_type.snow.want
A pkg/typecheck/testdata/scopes/fn_tuple_assign_compatible_types.snow.err
A pkg/typecheck/testdata/scopes/fn_tuple_assign_compatible_types.snow.want
A pkg/typecheck/testdata/scopes/fn_tuple_assign_incompatible_types.snow.err
A pkg/typecheck/testdata/scopes/fn_tuple_assign_incompatible_types.snow.want
A pkg/typecheck/testdata/scopes/fn_tuple_expr_select_field.snow.err
A pkg/typecheck/testdata/scopes/fn_tuple_expr_select_field.snow.want
A pkg/typecheck/testdata/scopes/let_invalid_tuple_type.snow.err
A pkg/typecheck/testdata/scopes/let_invalid_tuple_type.snow.want
A pkg/typecheck/testdata/scopes/var_tuple_type.snow.err
A pkg/typecheck/testdata/scopes/var_tuple_type.snow.want
A pkg/typecheck/testdata/static/fn_complex_selectors.snow.err
A pkg/typecheck/testdata/static/fn_invalid_tuple_access.snow.err
A pkg/typecheck/testdata/static/fn_nested_tuple_type.snow.err
M pkg/typecheck/testdata/static/fn_non_type_selectors.snow.err
A pkg/typecheck/testdata/static/fn_tuple_assign_compatible_types.snow.err
A pkg/typecheck/testdata/static/fn_tuple_assign_incompatible_types.snow.err
A pkg/typecheck/testdata/static/fn_tuple_expr_select_field.snow.err
A pkg/typecheck/testdata/static/let_invalid_tuple_type.snow.err
A pkg/typecheck/testdata/static/var_tuple_type.snow.err
M pkg/typecheck/testdata/types/fn_access_invalid_struct_field.snow.want
A pkg/typecheck/testdata/types/fn_complex_selectors.snow.err
A pkg/typecheck/testdata/types/fn_complex_selectors.snow.want
A pkg/typecheck/testdata/types/fn_invalid_tuple_access.snow.err
A pkg/typecheck/testdata/types/fn_invalid_tuple_access.snow.want
A pkg/typecheck/testdata/types/fn_nested_tuple_type.snow.err
A pkg/typecheck/testdata/types/fn_nested_tuple_type.snow.want
M pkg/typecheck/testdata/types/fn_non_type_selectors.snow.want
A pkg/typecheck/testdata/types/fn_tuple_assign_compatible_types.snow.err
A pkg/typecheck/testdata/types/fn_tuple_assign_compatible_types.snow.want
A pkg/typecheck/testdata/types/fn_tuple_assign_incompatible_types.snow.err
A pkg/typecheck/testdata/types/fn_tuple_assign_incompatible_types.snow.want
A pkg/typecheck/testdata/types/fn_tuple_expr_select_field.snow.err
A pkg/typecheck/testdata/types/fn_tuple_expr_select_field.snow.want
A pkg/typecheck/testdata/types/let_invalid_tuple_type.snow.err
A pkg/typecheck/testdata/types/let_invalid_tuple_type.snow.want
A pkg/typecheck/testdata/types/var_tuple_type.snow.err
A pkg/typecheck/testdata/types/var_tuple_type.snow.want
A pkg/typecheck/testdata/var_tuple_type.snow
M pkg/typecheck/type.go
M pkg/typecheck/type_pass.go
M cmd/goast/main.go => cmd/goast/main.go +3 -3
@@ 10,10 10,10 @@ func main() {
	src := `
package main

type S struct {}
func (s *S) do() {}

func main() { 
	var s struct {X int}
	s = struct{X int}{42}
	_ = s
}
`


M doc/swift.ebnf => doc/swift.ebnf +183 -173
@@ 1,176 1,3 @@
Whitespace  →  Whitespace-Item [ Whitespace ] 
Whitespace-Item  →  Line-Break 
Whitespace-Item  →  Comment 
Whitespace-Item  →  Multiline-Comment 
Whitespace-Item  →  U+0000, U+0009, U+000B, U+000C, or U+0020 

Line-Break  →  U+000A 
Line-Break  →  U+000D 
Line-Break  →  U+000D followed by U+000A 

Comment  →   "//"  Comment-Text Line-Break 
Multiline-Comment  →   "/*"  Multiline-Comment-Text  "*/"  

Comment-Text  →  Comment-Text-Item [ Comment-Text ] 
Comment-Text-Item  →  Any Unicode scalar value except U+000A or U+000D 

Multiline-Comment-Text  →  Multiline-Comment-Text-Item [ Multiline-Comment-Text ] 
Multiline-Comment-Text-Item  →  Multiline-Comment 
Multiline-Comment-Text-Item  →  Comment-Text-Item 
Multiline-Comment-Text-Item  →  Any Unicode scalar value except  "/*"  or  "*/"  

Identifier  →  Identifier-Head [ Identifier-Characters ] 
Identifier  →   "`"  Identifier-Head [ Identifier-Characters ]  "`"  
Identifier  →  Implicit-Parameter-Name 
Identifier  →  Property-Wrapper-Projection 
Identifier-List  →  Identifier | Identifier  ","  Identifier-List 

Identifier-Head  →  Upper- or lowercase letter A through Z 
Identifier-Head  →   "_"  
Identifier-Head  →  U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, or U+00B7–U+00BA 
Identifier-Head  →  U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, or U+00F8–U+00FF 
Identifier-Head  →  U+0100–U+02FF, U+0370–U+167F, U+1681–U+180D, or U+180F–U+1DBF 
Identifier-Head  →  U+1E00–U+1FFF 
Identifier-Head  →  U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, or U+2060–U+206F 
Identifier-Head  →  U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, or U+2776–U+2793 
Identifier-Head  →  U+2C00–U+2DFF or U+2E80–U+2FFF 
Identifier-Head  →  U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, or U+3040–U+D7FF 
Identifier-Head  →  U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, or U+FE30–U+FE44 
Identifier-Head  →  U+FE47–U+FFFD 
Identifier-Head  →  U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, or U+40000–U+4FFFD 
Identifier-Head  →  U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, or U+80000–U+8FFFD 
Identifier-Head  →  U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, or U+C0000–U+CFFFD 
Identifier-Head  →  U+D0000–U+DFFFD or U+E0000–U+EFFFD 

Identifier-Character  →  Digit 0 through 9 
Identifier-Character  →  U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, or U+FE20–U+FE2F 
Identifier-Character  →  Identifier-Head 
Identifier-Characters  →  Identifier-Character [ Identifier-Characters ] 

Implicit-Parameter-Name  →   "$"  Decimal-Digits 
Property-Wrapper-Projection  →   "$"  Identifier-Characters 

Literal  →  Numeric-Literal | String-Literal | Boolean-Literal | Nil-Literal 

Numeric-Literal  →  [  "-"  ] Integer-Literal | [  "-"  ] Floating-Point-Literal 
Boolean-Literal  →   "true"  |  "false"  
Nil-Literal  →   "nil"  

Integer-Literal  →  Binary-Literal 
Integer-Literal  →  Octal-Literal 
Integer-Literal  →  Decimal-Literal 
Integer-Literal  →  Hexadecimal-Literal 

Binary-Literal  →   "0b"  Binary-Digit [ Binary-Literal-Characters ] 
Binary-Digit  →  Digit 0 or 1 
Binary-Literal-Character  →  Binary-Digit |  "_"  
Binary-Literal-Characters  →  Binary-Literal-Character [ Binary-Literal-Characters ] 

Octal-Literal  →   "0o"  Octal-Digit [ Octal-Literal-Characters ] 
Octal-Digit  →  Digit 0 through 7 
Octal-Literal-Character  →  Octal-Digit |  "_"  
Octal-Literal-Characters  →  Octal-Literal-Character [ Octal-Literal-Characters ] 

Decimal-Literal  →  Decimal-Digit [ Decimal-Literal-Characters ] 
Decimal-Digit  →  Digit 0 through 9 
Decimal-Digits  →  Decimal-Digit [ Decimal-Digits ] 
Decimal-Literal-Character  →  Decimal-Digit |  "_"  
Decimal-Literal-Characters  →  Decimal-Literal-Character [ Decimal-Literal-Characters ] 

Hexadecimal-Literal  →   "0x"  Hexadecimal-Digit [ Hexadecimal-Literal-Characters ] 
Hexadecimal-Digit  →  Digit 0 through 9, a through f, or A through F 
Hexadecimal-Literal-Character  →  Hexadecimal-Digit |  "_"  
Hexadecimal-Literal-Characters  →  Hexadecimal-Literal-Character [ Hexadecimal-Literal-Characters ] 

Floating-Point-Literal  →  Decimal-Literal [ Decimal-Fraction ] [ Decimal-Exponent ] 
Floating-Point-Literal  →  Hexadecimal-Literal [ Hexadecimal-Fraction ] Hexadecimal-Exponent 

Decimal-Fraction  →   "."  Decimal-Literal 
Decimal-Exponent  →  Floating-Point-E [ Sign ] Decimal-Literal 

Hexadecimal-Fraction  →   "."  Hexadecimal-Digit [ Hexadecimal-Literal-Characters ] 
Hexadecimal-Exponent  →  Floating-Point-P [ Sign ] Decimal-Literal 

Floating-Point-E  →   "e"  |  "E"  
Floating-Point-P  →   "p"  |  "P"  
Sign  →   "+"  |  "-"  

String-Literal  →  Static-String-Literal | Interpolated-String-Literal 

String-Literal-Opening-Delimiter  →  [ Extended-String-Literal-Delimiter ]  """  
String-Literal-Closing-Delimiter  →   """  [ Extended-String-Literal-Delimiter ] 

Static-String-Literal  →  String-Literal-Opening-Delimiter [ Quoted-Text ] String-Literal-Closing-Delimiter 
Static-String-Literal  →  Multiline-String-Literal-Opening-Delimiter [ Multiline-Quoted-Text ] Multiline-String-Literal-Closing-Delimiter 

Multiline-String-Literal-Opening-Delimiter  →  Extended-String-Literal-Delimiter  """""  
Multiline-String-Literal-Closing-Delimiter  →   """""  Extended-String-Literal-Delimiter 
Extended-String-Literal-Delimiter  →   "#"  [ Extended-String-Literal-Delimiter ] 

Quoted-Text  →  Quoted-Text-Item [ Quoted-Text ] 
Quoted-Text-Item  →  Escaped-Character 
Quoted-Text-Item  →  Any Unicode scalar value except  """  ,  "\"  , U+000A, or U+000D 

Multiline-Quoted-Text  →  Multiline-Quoted-Text-Item [ Multiline-Quoted-Text ] 
Multiline-Quoted-Text-Item  →  Escaped-Character 
Multiline-Quoted-Text-Item  →  Any Unicode scalar value except  "\"  
Multiline-Quoted-Text-Item  →  Escaped-Newline 

Interpolated-String-Literal  →  String-Literal-Opening-Delimiter [ Interpolated-Text ] String-Literal-Closing-Delimiter 
Interpolated-String-Literal  →  Multiline-String-Literal-Opening-Delimiter [ Interpolated-Text ] Multiline-String-Literal-Closing-Delimiter 

Interpolated-Text  →  Interpolated-Text-Item [ Interpolated-Text ] 
Interpolated-Text-Item  →   "\("  Expression  ")"  | Quoted-Text-Item 

Multiline-Interpolated-Text  →  Multiline-Interpolated-Text-Item [ Multiline-Interpolated-Text ] 
Multiline-Interpolated-Text-Item  →   "\("  Expression  ")"  | Multiline-Quoted-Text-Item 

Escape-Sequence  →   "\"  Extended-String-Literal-Delimiter 
Escaped-Character  →  Escape-Sequence  "0"  | Escape-Sequence  "\"  | Escape-Sequence  "t"  | Escape-Sequence  "n"  | Escape-Sequence  "r"  | Escape-Sequence  """  | Escape-Sequence  "'"  
Escaped-Character  →  Escape-Sequence  "u"   "{"  Unicode-Scalar-Digits  "}"  
Unicode-Scalar-Digits  →  Between one and eight hexadecimal digits 

Escaped-Newline  →  Escape-Sequence [ Whitespace ] Line-Break 

Operator  →  Operator-Head [ Operator-Characters ] 
Operator  →  Dot-Operator-Head Dot-Operator-Characters 

Operator-Head  →   "/"  |  "="  |  "-"  |  "+"  |  "!"  |  "*"  |  "%"  |  "<"  |  ">"  |  "&"  |  "|"  |  "^"  |  "~"  |  "?"  
Operator-Head  →  U+00A1–U+00A7 
Operator-Head  →  U+00A9 or U+00AB 
Operator-Head  →  U+00AC or U+00AE 
Operator-Head  →  U+00B0–U+00B1 
Operator-Head  →  U+00B6, U+00BB, U+00BF, U+00D7, or U+00F7 
Operator-Head  →  U+2016–U+2017 
Operator-Head  →  U+2020–U+2027 
Operator-Head  →  U+2030–U+203E 
Operator-Head  →  U+2041–U+2053 
Operator-Head  →  U+2055–U+205E 
Operator-Head  →  U+2190–U+23FF 
Operator-Head  →  U+2500–U+2775 
Operator-Head  →  U+2794–U+2BFF 
Operator-Head  →  U+2E00–U+2E7F 
Operator-Head  →  U+3001–U+3003 
Operator-Head  →  U+3008–U+3020 
Operator-Head  →  U+3030 

Operator-Character  →  Operator-Head 
Operator-Character  →  U+0300–U+036F 
Operator-Character  →  U+1DC0–U+1DFF 
Operator-Character  →  U+20D0–U+20FF 
Operator-Character  →  U+FE00–U+FE0F 
Operator-Character  →  U+FE20–U+FE2F 
Operator-Character  →  U+E0100–U+E01EF 
Operator-Characters  →  Operator-Character [ Operator-Characters ] 

Dot-Operator-Head  →   "."  
Dot-Operator-Character  →   "."  | Operator-Character 
Dot-Operator-Characters  →  Dot-Operator-Character [ Dot-Operator-Characters ] 

Binary-Operator  →  Operator 
Prefix-Operator  →  Operator 
Postfix-Operator  →  Operator 

Type  →  Function-Type 
Type  →  Array-Type 
Type  →  Dictionary-Type 


@@ 545,6 372,10 @@ Pattern-Initializer  →  Pattern [ Initializer ]
Initializer  →   "="  Expression 

Variable-Declaration  →  Variable-Declaration-Head Pattern-Initializer-List 

# the rest of the variable declaration productions are just special case where a single
# variable can be on the left side, and the type or init is followed by special getter/setter/etc.
# The only real general declaration form is the one above (and the equivalent Constant-Declaration).
Variable-Declaration  →  Variable-Declaration-Head Variable-Name Type-Annotation Code-Block 
Variable-Declaration  →  Variable-Declaration-Head Variable-Name Type-Annotation Getter-Setter-Block 
Variable-Declaration  →  Variable-Declaration-Head Variable-Name Type-Annotation Getter-Setter-Keyword-Block 


@@ 775,3 606,182 @@ Generic-Argument-Clause  →   "<"  Generic-Argument-List  ">"
Generic-Argument-List  →  Generic-Argument | Generic-Argument  ","  Generic-Argument-List 
Generic-Argument  →  Type 



########## SCANNER-RELATED PRODUCTIONS ############ 



Whitespace  →  Whitespace-Item [ Whitespace ] 
Whitespace-Item  →  Line-Break 
Whitespace-Item  →  Comment 
Whitespace-Item  →  Multiline-Comment 
Whitespace-Item  →  U+0000, U+0009, U+000B, U+000C, or U+0020 

Line-Break  →  U+000A 
Line-Break  →  U+000D 
Line-Break  →  U+000D followed by U+000A 

Comment  →   "//"  Comment-Text Line-Break 
Multiline-Comment  →   "/*"  Multiline-Comment-Text  "*/"  

Comment-Text  →  Comment-Text-Item [ Comment-Text ] 
Comment-Text-Item  →  Any Unicode scalar value except U+000A or U+000D 

Multiline-Comment-Text  →  Multiline-Comment-Text-Item [ Multiline-Comment-Text ] 
Multiline-Comment-Text-Item  →  Multiline-Comment 
Multiline-Comment-Text-Item  →  Comment-Text-Item 
Multiline-Comment-Text-Item  →  Any Unicode scalar value except  "/*"  or  "*/"  

Identifier  →  Identifier-Head [ Identifier-Characters ] 
Identifier  →   "`"  Identifier-Head [ Identifier-Characters ]  "`"  
Identifier  →  Implicit-Parameter-Name 
Identifier  →  Property-Wrapper-Projection 
Identifier-List  →  Identifier | Identifier  ","  Identifier-List 

Identifier-Head  →  Upper- or lowercase letter A through Z 
Identifier-Head  →   "_"  
Identifier-Head  →  U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, or U+00B7–U+00BA 
Identifier-Head  →  U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, or U+00F8–U+00FF 
Identifier-Head  →  U+0100–U+02FF, U+0370–U+167F, U+1681–U+180D, or U+180F–U+1DBF 
Identifier-Head  →  U+1E00–U+1FFF 
Identifier-Head  →  U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, or U+2060–U+206F 
Identifier-Head  →  U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, or U+2776–U+2793 
Identifier-Head  →  U+2C00–U+2DFF or U+2E80–U+2FFF 
Identifier-Head  →  U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, or U+3040–U+D7FF 
Identifier-Head  →  U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, or U+FE30–U+FE44 
Identifier-Head  →  U+FE47–U+FFFD 
Identifier-Head  →  U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, or U+40000–U+4FFFD 
Identifier-Head  →  U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, or U+80000–U+8FFFD 
Identifier-Head  →  U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, or U+C0000–U+CFFFD 
Identifier-Head  →  U+D0000–U+DFFFD or U+E0000–U+EFFFD 

Identifier-Character  →  Digit 0 through 9 
Identifier-Character  →  U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, or U+FE20–U+FE2F 
Identifier-Character  →  Identifier-Head 
Identifier-Characters  →  Identifier-Character [ Identifier-Characters ] 

Implicit-Parameter-Name  →   "$"  Decimal-Digits 
Property-Wrapper-Projection  →   "$"  Identifier-Characters 

Literal  →  Numeric-Literal | String-Literal | Boolean-Literal | Nil-Literal 

Numeric-Literal  →  [  "-"  ] Integer-Literal | [  "-"  ] Floating-Point-Literal 
Boolean-Literal  →   "true"  |  "false"  
Nil-Literal  →   "nil"  

Integer-Literal  →  Binary-Literal 
Integer-Literal  →  Octal-Literal 
Integer-Literal  →  Decimal-Literal 
Integer-Literal  →  Hexadecimal-Literal 

Binary-Literal  →   "0b"  Binary-Digit [ Binary-Literal-Characters ] 
Binary-Digit  →  Digit 0 or 1 
Binary-Literal-Character  →  Binary-Digit |  "_"  
Binary-Literal-Characters  →  Binary-Literal-Character [ Binary-Literal-Characters ] 

Octal-Literal  →   "0o"  Octal-Digit [ Octal-Literal-Characters ] 
Octal-Digit  →  Digit 0 through 7 
Octal-Literal-Character  →  Octal-Digit |  "_"  
Octal-Literal-Characters  →  Octal-Literal-Character [ Octal-Literal-Characters ] 

Decimal-Literal  →  Decimal-Digit [ Decimal-Literal-Characters ] 
Decimal-Digit  →  Digit 0 through 9 
Decimal-Digits  →  Decimal-Digit [ Decimal-Digits ] 
Decimal-Literal-Character  →  Decimal-Digit |  "_"  
Decimal-Literal-Characters  →  Decimal-Literal-Character [ Decimal-Literal-Characters ] 

Hexadecimal-Literal  →   "0x"  Hexadecimal-Digit [ Hexadecimal-Literal-Characters ] 
Hexadecimal-Digit  →  Digit 0 through 9, a through f, or A through F 
Hexadecimal-Literal-Character  →  Hexadecimal-Digit |  "_"  
Hexadecimal-Literal-Characters  →  Hexadecimal-Literal-Character [ Hexadecimal-Literal-Characters ] 

Floating-Point-Literal  →  Decimal-Literal [ Decimal-Fraction ] [ Decimal-Exponent ] 
Floating-Point-Literal  →  Hexadecimal-Literal [ Hexadecimal-Fraction ] Hexadecimal-Exponent 

Decimal-Fraction  →   "."  Decimal-Literal 
Decimal-Exponent  →  Floating-Point-E [ Sign ] Decimal-Literal 

Hexadecimal-Fraction  →   "."  Hexadecimal-Digit [ Hexadecimal-Literal-Characters ] 
Hexadecimal-Exponent  →  Floating-Point-P [ Sign ] Decimal-Literal 

Floating-Point-E  →   "e"  |  "E"  
Floating-Point-P  →   "p"  |  "P"  
Sign  →   "+"  |  "-"  

String-Literal  →  Static-String-Literal | Interpolated-String-Literal 

String-Literal-Opening-Delimiter  →  [ Extended-String-Literal-Delimiter ]  """  
String-Literal-Closing-Delimiter  →   """  [ Extended-String-Literal-Delimiter ] 

Static-String-Literal  →  String-Literal-Opening-Delimiter [ Quoted-Text ] String-Literal-Closing-Delimiter 
Static-String-Literal  →  Multiline-String-Literal-Opening-Delimiter [ Multiline-Quoted-Text ] Multiline-String-Literal-Closing-Delimiter 

Multiline-String-Literal-Opening-Delimiter  →  Extended-String-Literal-Delimiter  """""  
Multiline-String-Literal-Closing-Delimiter  →   """""  Extended-String-Literal-Delimiter 
Extended-String-Literal-Delimiter  →   "#"  [ Extended-String-Literal-Delimiter ] 

Quoted-Text  →  Quoted-Text-Item [ Quoted-Text ] 
Quoted-Text-Item  →  Escaped-Character 
Quoted-Text-Item  →  Any Unicode scalar value except  """  ,  "\"  , U+000A, or U+000D 

Multiline-Quoted-Text  →  Multiline-Quoted-Text-Item [ Multiline-Quoted-Text ] 
Multiline-Quoted-Text-Item  →  Escaped-Character 
Multiline-Quoted-Text-Item  →  Any Unicode scalar value except  "\"  
Multiline-Quoted-Text-Item  →  Escaped-Newline 

Interpolated-String-Literal  →  String-Literal-Opening-Delimiter [ Interpolated-Text ] String-Literal-Closing-Delimiter 
Interpolated-String-Literal  →  Multiline-String-Literal-Opening-Delimiter [ Interpolated-Text ] Multiline-String-Literal-Closing-Delimiter 

Interpolated-Text  →  Interpolated-Text-Item [ Interpolated-Text ] 
Interpolated-Text-Item  →   "\("  Expression  ")"  | Quoted-Text-Item 

Multiline-Interpolated-Text  →  Multiline-Interpolated-Text-Item [ Multiline-Interpolated-Text ] 
Multiline-Interpolated-Text-Item  →   "\("  Expression  ")"  | Multiline-Quoted-Text-Item 

Escape-Sequence  →   "\"  Extended-String-Literal-Delimiter 
Escaped-Character  →  Escape-Sequence  "0"  | Escape-Sequence  "\"  | Escape-Sequence  "t"  | Escape-Sequence  "n"  | Escape-Sequence  "r"  | Escape-Sequence  """  | Escape-Sequence  "'"  
Escaped-Character  →  Escape-Sequence  "u"   "{"  Unicode-Scalar-Digits  "}"  
Unicode-Scalar-Digits  →  Between one and eight hexadecimal digits 

Escaped-Newline  →  Escape-Sequence [ Whitespace ] Line-Break 

Operator  →  Operator-Head [ Operator-Characters ] 
Operator  →  Dot-Operator-Head Dot-Operator-Characters 

Operator-Head  →   "/"  |  "="  |  "-"  |  "+"  |  "!"  |  "*"  |  "%"  |  "<"  |  ">"  |  "&"  |  "|"  |  "^"  |  "~"  |  "?"  
Operator-Head  →  U+00A1–U+00A7 
Operator-Head  →  U+00A9 or U+00AB 
Operator-Head  →  U+00AC or U+00AE 
Operator-Head  →  U+00B0–U+00B1 
Operator-Head  →  U+00B6, U+00BB, U+00BF, U+00D7, or U+00F7 
Operator-Head  →  U+2016–U+2017 
Operator-Head  →  U+2020–U+2027 
Operator-Head  →  U+2030–U+203E 
Operator-Head  →  U+2041–U+2053 
Operator-Head  →  U+2055–U+205E 
Operator-Head  →  U+2190–U+23FF 
Operator-Head  →  U+2500–U+2775 
Operator-Head  →  U+2794–U+2BFF 
Operator-Head  →  U+2E00–U+2E7F 
Operator-Head  →  U+3001–U+3003 
Operator-Head  →  U+3008–U+3020 
Operator-Head  →  U+3030 

Operator-Character  →  Operator-Head 
Operator-Character  →  U+0300–U+036F 
Operator-Character  →  U+1DC0–U+1DFF 
Operator-Character  →  U+20D0–U+20FF 
Operator-Character  →  U+FE00–U+FE0F 
Operator-Character  →  U+FE20–U+FE2F 
Operator-Character  →  U+E0100–U+E01EF 
Operator-Characters  →  Operator-Character [ Operator-Characters ] 

Dot-Operator-Head  →   "."  
Dot-Operator-Character  →   "."  | Operator-Character 
Dot-Operator-Characters  →  Dot-Operator-Character [ Dot-Operator-Characters ] 

Binary-Operator  →  Operator 
Prefix-Operator  →  Operator 
Postfix-Operator  →  Operator 


D exp/codegen/add_one.ssa => exp/codegen/add_one.ssa +0 -14
@@ 1,14 0,0 @@
data $fmt = { b "%d\n", b 0 }

function w $add(w %arg) {
@start
  %val =w add 1, %arg
  ret %val
}

export function w $main() {
@start
  %val =w call $add(w 42)
  call $printf(l $fmt, w %val)
  ret 0
}

D exp/codegen/array.ssa => exp/codegen/array.ssa +0 -31
@@ 1,31 0,0 @@
data $fmt = { b "%d\n", b 0 }

export function w $main() {
@start
  %ar =l alloc4 16          # allocate array [4]i32
  %i =l add 0, 0
  %max =l add 4, 0

@assignloop
  %off =l mul 4, %i
  %addr =l add %off, %ar
  storew %i, %addr
  %i =l add 1, %i
  %loop =l sub %max, %i
  jnz %loop, @assignloop, @print

@print
  %i =l add 0, 0
  %max =l add 4, 0
@printloop
  %off =l mul 4, %i
  %addr =l add %off, %ar
  %val =w loadw %addr
  call $printf(l $fmt, w %val)
  %i =l add 1, %i
  %loop =l sub %max, %i
  jnz %loop, @printloop, @end

@end
  ret 0
}

D exp/codegen/hello.ssa => exp/codegen/hello.ssa +0 -9
@@ 1,9 0,0 @@
# Define the string constant.
data $str = { b "hello world", b 0 }

export function w $main() {
@start
        # Call the puts function with $str as argument.
        %r =w call $puts(l $str)
        ret 0
}

D exp/codegen/literal_string.ssa => exp/codegen/literal_string.ssa +0 -12
@@ 1,12 0,0 @@
# Define the string constant.
data $str = { b "hello world", b 0 }

export function w $main() {
@start
        call $puts(l $str)
        %w =l add $str, 6
        call $puts(l %w)
        storeb 62, %w
        call $puts(l %w)
        ret 0
}

D exp/codegen/new.ssa => exp/codegen/new.ssa +0 -13
@@ 1,13 0,0 @@
data $pfmt = { b "%p\n", b 0 }
data $vfmt = { b "%d\n", b 0 }

export function w $main() {
@start
  %addr =l call $malloc(w 4)
  call $printf(l $pfmt, l %addr)
  storew 42, %addr
  %val =w loadw %addr
  call $printf(l $vfmt, w %val)
  call $free(l %addr)
  ret
}

D exp/codegen/optional.ssa => exp/codegen/optional.ssa +0 -51
@@ 1,51 0,0 @@
data $fmtSome = { b "%d %d\n", b 0 }
data $fmtNone = { b "none\n", b 0 }

# a random type just as example
type :type = { w, w }

# the optional wraps it in a tagged struct, can
# be optimized when :type is a pointer so that
# 0 is used as null indicator (no need to wrap).
# Other types could be optimized too (e.g. bool
# could use a value other than 0 and 1).
type :optional = { b, :type }

export function w $main() {
@start
  # first call with Optional{ .none }
  %opt =l alloc4 9
  storeb 0, %opt
  call $printOptional(:optional %opt)

  # then with Optional{ .some(...) }
  %some =l alloc4 9
  storeb 1, %some
  %f1 =l add %some, 1
  storew 36, %f1
  %f2 =l add %f1, 4
  storew 987, %f2
  call $printOptional(:optional %some)

  ret 0
}

function $printOptional(:optional %p) {
@start
  # unwrap (e.g. if optional { /* access unwrapped value */ } else { /* none */ }
  # unwrapping an error (e.g. let res = may_crash(); res!.value) is the same
  %tag =w loadub %p
  jnz %tag, @some, @none

@some
  %f1 =l add %p, 1
  %v1 =w loadw %f1
  %f2 =l add %f1, 4
  %v2 =w loadw %f2
  call $printf(l $fmtSome, w %v1, w %v2)
  ret

@none
  call $printf(l $fmtNone)
  ret
}

M pkg/ast/ast.go => pkg/ast/ast.go +33 -10
@@ 107,12 107,19 @@ type (
	// EXPRESSIONS
	// ====================

	// FnType is a function type literal, it is the same as an FnSig
	// in a FnDecl, except that it is an expression, the return
	// type is required and the parameters have no identifiers, only
	// types.
	// FnType is a function type literal, it is the same as an FnSig in a FnDecl,
	// except that it is an expression, the return type is required and the
	// parameters have no identifiers, only types.
	FnType FnSig

	// TupleType is a tuple type literal, consisting of a list of types in
	// between parentheses.
	TupleType struct {
		Lparen token.Pos
		Types  []*ParamDef // at least 2
		Rparen token.Pos
	}

	// BinaryExpr represents a binary expression, e.g. a * 2.
	BinaryExpr struct {
		Left  Expr


@@ 135,6 142,13 @@ type (
		Rparen token.Pos
	}

	// TupleExpr represents a tuple value.
	TupleExpr struct {
		Lparen token.Pos
		Values []*ExprItem // at least 2 values
		Rparen token.Pos
	}

	// CallExpr represents a function or method call expression.
	CallExpr struct {
		Left   Expr


@@ 148,20 162,21 @@ type (
	SelectorExpr struct {
		Left Expr
		Dot  token.Pos
		Sel  *Ident
		Sel  *Ident // the parser will process the sel in x.0 as an identifier, even though the scanner returns an Int
	}

	// BasicLit represents a basic literal, e.g. 33 or "abc".
	BasicLit struct {
		LitPos  token.Pos
		Literal token.Token // e.g. String or Int
		Value   string
		Value   string      // raw value, including e.g. the quotes for a string literal
	}

	// Ident represents an identifier.
	Ident struct {
		Name     string
		IdentPos token.Pos
		Name       string
		TupleIndex int // if Ident is a tuple index in a selector expression, this is the index value, otherwise -1
		IdentPos   token.Pos
	}

	// BadExpr represents a bad (invalid) expression.


@@ 185,8 200,8 @@ type (

	// ParamDef represents a parameter definition in a function signature.
	ParamDef struct {
		Name  *Ident
		Colon token.Pos
		Name  *Ident    // possibly nil (i.e. for FnType)
		Colon token.Pos // token.NoPos if no name
		Type  Expr
		Comma token.Pos // token.NoPos if no trailing comma
	}


@@ 243,6 258,8 @@ func (i *Ident) exprNode()        {}
func (b *BadExpr) exprNode()      {}
func (c *CallExpr) exprNode()     {}
func (s *SelectorExpr) exprNode() {}
func (t *TupleType) exprNode()    {}
func (t *TupleExpr) exprNode()    {}

// Stmt represents a statement in the AST.
type Stmt interface {


@@ 436,6 453,12 @@ func (b *BasicLit) End() token.Pos { return token.Pos(int(b.LitPos) + len(b.Valu
func (i *Ident) Pos() token.Pos { return i.IdentPos }
func (i *Ident) End() token.Pos { return token.Pos(int(i.IdentPos) + len(i.Name)) }

func (t *TupleType) Pos() token.Pos { return t.Lparen }
func (t *TupleType) End() token.Pos { return t.Rparen + 1 }

func (t *TupleExpr) Pos() token.Pos { return t.Lparen }
func (t *TupleExpr) End() token.Pos { return t.Rparen + 1 }

func (g *CommentGroup) Pos() token.Pos { return g.List[0].Pos() }
func (g *CommentGroup) End() token.Pos { return g.List[len(g.List)-1].End() }


M pkg/ast/visitor.go => pkg/ast/visitor.go +10 -0
@@ 50,6 50,16 @@ func Walk(v Visitor, node Node) {
			Walk(v, n.RetType)
		}

	case *TupleType:
		for _, typ := range n.Types {
			Walk(v, typ)
		}

	case *TupleExpr:
		for _, val := range n.Values {
			Walk(v, val)
		}

	case *ParamDef:
		if n.Name != nil {
			Walk(v, n.Name)

M pkg/codegen/mangle.go => pkg/codegen/mangle.go +7 -0
@@ 21,6 21,13 @@ func mangledNameForExternParam(index int) string {
	return "_" + strconv.Itoa(index)
}

func mangledNameForTuple(index int, exported bool) string {
	var buf strings.Builder
	mangleWriteExported(&buf, exported)
	buf.WriteString(strconv.Itoa(index))
	return buf.String()
}

// mangleIdents mangles all identifiers declared in decls. The logic is as follows.
// For each declared identifier:
//   - If it is unused, mangle it as "_" so that it doesn't generate unused error, and

M pkg/codegen/position.go => pkg/codegen/position.go +68 -1
@@ 34,8 34,31 @@ func (p *positioner) Visit(n goast.Node) goast.Visitor {
		}
		n.Closing = p.getIncByLen("}\n")
		return nil
	case *goast.FuncDecl:
		p.getIncByLen("\n\n")
		// must process the full decl here, because func type assumes func is followed by args,
		// not by the name of the declared func.
		funcPos := p.getIncByLen("func ")
		if n.Recv != nil {
			goast.Walk(p, n.Recv)
		}
		if n.Name != nil {
			goast.Walk(p, n.Name)
		}
		if n.Type != nil {
			n.Type.Func = funcPos
			if n.Type.Params != nil {
				goast.Walk(p, n.Type.Params)
			}
			if n.Type.Results != nil {
				goast.Walk(p, n.Type.Results)
			}
		}
		if n.Body != nil {
			goast.Walk(p, n.Body)
		}
		return nil
	case *goast.FuncType:
		p.getIncByLen("\n")
		n.Func = p.getIncByLen("func ")
	case *goast.BlockStmt:
		n.Lbrace = p.getIncByLen("{\n")


@@ 49,6 72,50 @@ func (p *positioner) Visit(n goast.Node) goast.Visitor {
		n.Return = p.getIncByLen("return ")
	case *goast.StarExpr:
		n.Star = p.getIncByLen("*")
	case *goast.UnaryExpr:
		n.OpPos = p.getIncByLen(n.Op.String())
	case *goast.BinaryExpr:
		goast.Walk(p, n.X)
		n.OpPos = p.getIncByLen(n.Op.String())
		goast.Walk(p, n.Y)
		return nil
	case *goast.BasicLit:
		n.ValuePos = p.getIncByLen(n.Value + " ")
	case *goast.CompositeLit:
		goast.Walk(p, n.Type)
		n.Lbrace = p.getIncByLen("{")
		for _, e := range n.Elts {
			goast.Walk(p, e)
		}
		n.Rbrace = p.getIncByLen("}")
		return nil
	case *goast.ParenExpr:
		n.Lparen = p.getIncByLen("(")
		goast.Walk(p, n.X)
		n.Rparen = p.getIncByLen(")")
		return nil
	case *goast.CallExpr:
		goast.Walk(p, n.Fun)
		n.Lparen = p.getIncByLen("(")
		for i, arg := range n.Args {
			if i > 0 {
				p.getIncByLen(", ")
			}
			goast.Walk(p, arg)
		}
		n.Rparen = p.getIncByLen(")")
		return nil
	case *goast.AssignStmt:
		for _, e := range n.Lhs {
			goast.Walk(p, e)
		}
		n.TokPos = p.getIncByLen(n.Tok.String())
		for _, e := range n.Rhs {
			goast.Walk(p, e)
		}
		return nil
	case *goast.IfStmt:
		n.If = p.getIncByLen("if ")
	}
	return p
}

A pkg/codegen/testdata/fn_tuple.snow => pkg/codegen/testdata/fn_tuple.snow +7 -0
@@ 0,0 1,7 @@
fn main() {
  var x = (1, "a", true)
  println(x)
}

@extern{import: "fmt", symbol: "Println"}
fn println(x: (int, string, bool))

A pkg/codegen/testdata/fn_tuple.snow.err => pkg/codegen/testdata/fn_tuple.snow.err +0 -0
A pkg/codegen/testdata/fn_tuple.snow.want => pkg/codegen/testdata/fn_tuple.snow.want +26 -0
@@ 0,0 1,26 @@
package main

import "fmt"

func main() {
	var x struct {
		_ɤ0 int
		_ɤ1 string
		_ɤ2 bool
	} = struct {
		_ɤ0 int
		_ɤ1 string
		_ɤ2 bool
	}{1, "a", true}
	_ɤprintln(x)
}

func _ɤprintln(
	_0 struct {
		_ɤ0 int
		_ɤ1 string
		_ɤ2 bool
	},
) {
	fmt.Println(_0)
}

M pkg/codegen/translate.go => pkg/codegen/translate.go +36 -1
@@ 213,6 213,27 @@ func (t *translator) Visit(n ast.Node) ast.Visitor {
			// structs are added on top-level file, so no tt.generated is returned
		}

	case *ast.TupleType:
		T := t.types[n].Type
		t.generated = t.goTypeExprFor(T)

	case *ast.TupleExpr:
		// TODO: the expression might not match the target type, but reconciling
		// is not as easy as with basic types - each field has to be converted
		// individually.
		T := t.types[n].Type
		cl := &goast.CompositeLit{
			Type: t.goTypeExprFor(T),
			Elts: make([]goast.Expr, len(n.Values)),
		}
		for i, val := range n.Values {
			tt := t.clone(nil)
			ast.Walk(tt, val)
			expr := tt.generated.(goast.Expr)
			cl.Elts[i] = expr
		}
		t.generated = cl

	case *ast.Block:
		bs := &goast.BlockStmt{
			List: make([]goast.Stmt, 0, len(n.Stmts)),


@@ 438,7 459,7 @@ func (t *translator) Visit(n ast.Node) ast.Visitor {
		t.generated = expr

	default:
		panic(fmt.Sprintf("unknown AST node %T", n))
		panic(fmt.Sprintf("codegen: unknown AST node %T", n))
	}
	return nil
}


@@ 486,6 507,20 @@ func (t *translator) goTypeExprFor(T typecheck.Type) goast.Expr {
		}
		return ft

	case *typecheck.TupleType:
		// tuples are generated as struct literals
		st := &goast.StructType{
			Fields: &goast.FieldList{List: make([]*goast.Field, len(tt.Fields))},
		}
		for i, field := range tt.Fields {
			st.Fields.List[i] = &goast.Field{
				// TODO: for now, always unexported
				Names: []*goast.Ident{{Name: mangledNameForTuple(i, false)}},
				Type:  t.goTypeExprFor(field),
			}
		}
		return st

	case *typecheck.StructType:
		name := mangledNameFor(t.mangling, T.Object())
		return &goast.Ident{Name: name}

M pkg/grammar/grammar.ebnf => pkg/grammar/grammar.ebnf +5 -3
@@ 5,9 5,10 @@ SourceFile = { TopLevelStmt } .
// => Types 
Type = TypeName | TypeLiteral | "(" Type ")" .
TypeName = ident | ident "." TypeName .
TypeLiteral = FuncType .
TypeLiteral = FuncType | TupleType .

FuncType = "(" [ TypeList [ "," ] ] ")" "->" Type .
TupleType = "(" Type "," TypeList [ "," ] ")" . // at least 2 types
TypeList = Type { "," Type } .

// => Statements


@@ 62,14 63,15 @@ Arithmetic = Term { ( "+" | "-" ) Term } .
Term = Factor { ( "*" | "/" | "%" ) Factor } .
Factor = ( "+" | "-" | "!" ) Factor | Compound .
Compound = Atom { Trailer } .
Atom = "(" Expr ")" | ident | Literal .
Atom = "(" Expr ")" | TupleExpr | ident | Literal .
TupleExpr = "(" Expr "," ExprList [ "," ] ")" . // at least 2 expressions
Literal = number | string .

// => Trailers
Trailer = Arguments | Selector .
Arguments = "(" [ ExprList [ "," ] ] ")" .
ExprList = Expr { "," Expr } .
Selector = "." ident .
Selector = "." ( ident | number ) .

// => Terminals


M pkg/parser/parser.go => pkg/parser/parser.go +69 -19
@@ 3,6 3,7 @@ package parser

import (
	"io/ioutil"
	"strconv"

	"git.sr.ht/~mna/snow/pkg/ast"
	"git.sr.ht/~mna/snow/pkg/scanner"


@@ 235,14 236,31 @@ func (p *parser) parseVarDecl() *ast.VarDecl {
	return vd
}

func (p *parser) parseIdentOrTupleIndex() *ast.Ident {
	if p.tok == token.Int {
		// must be a base10 integer (i.e. not 0xabc nor 0b1101)
		lit := p.lit
		ix, err := strconv.ParseInt(lit, 10, 32)
		if err == nil {
			return &ast.Ident{
				Name:       lit,
				TupleIndex: int(ix),
				IdentPos:   p.expect(token.Int),
			}
		}
	}
	return p.parseIdent()
}

func (p *parser) parseIdent() *ast.Ident {
	var name string
	if p.tok == token.Ident {
		name = p.lit
	}
	return &ast.Ident{
		Name:     name,
		IdentPos: p.expect(token.Ident),
		Name:       name,
		TupleIndex: -1,
		IdentPos:   p.expect(token.Ident),
	}
}



@@ 275,8 293,8 @@ func (p *parser) parseType() ast.Expr {
		return expr

	case token.Lparen:
		// this could be a type in paren, or a function type
		return p.parseFuncOrParenType()
		// this could be a type in paren, a tuple or a function type
		return p.parseFuncTupleOrParenType()

	default:
		pos := p.pos


@@ 285,9 303,10 @@ func (p *parser) parseType() ast.Expr {
	}
}

func (p *parser) parseFuncOrParenType() ast.Expr {
func (p *parser) parseFuncTupleOrParenType() ast.Expr {
	var types []*ast.ParamDef

	pos := p.pos
	lparen := p.expect(token.Lparen)
	for !tokenIn(p.tok, token.Rparen, token.EOF) {
		pd := &ast.ParamDef{


@@ 304,13 323,27 @@ func (p *parser) parseFuncOrParenType() ast.Expr {
	rparen := p.expect(token.Rparen)

	// at this point everything from ( to ) has been parsed, determine
	// if this is a type in parenthesis or a func signature.
	if len(types) == 1 && p.tok != token.Rarrow {
		// this is a parenthesized type
		return &ast.ParenExpr{
			Lparen: lparen,
			Value:  types[0].Type,
			Rparen: rparen,
	// if this is a tuple, a type in parenthesis or a func signature.
	if p.tok != token.Rarrow {
		switch len(types) {
		case 0:
			// invalid empty tuple
			p.errorExpected(pos, "type")
			return &ast.BadExpr{From: pos, To: p.pos}
		case 1:
			// this is a parenthesized type
			return &ast.ParenExpr{
				Lparen: lparen,
				Value:  types[0].Type,
				Rparen: rparen,
			}
		default:
			// this is a tuple type
			return &ast.TupleType{
				Lparen: lparen,
				Types:  types,
				Rparen: rparen,
			}
		}
	}



@@ 450,7 483,7 @@ loop:
				Left: expr,
				Dot:  p.expect(token.Dot),
			}
			sel.Sel = p.parseIdent()
			sel.Sel = p.parseIdentOrTupleIndex()
			expr = sel

		default:


@@ 484,13 517,30 @@ func (p *parser) parseExprList(stop ...token.Token) []*ast.ExprItem {
func (p *parser) parseAtom() ast.Expr {
	switch p.tok {
	case token.Lparen:
		pe := &ast.ParenExpr{
			Lparen: p.pos,
		lparen := p.expect(token.Lparen)
		values := p.parseExprList(token.Rparen)
		rparen := p.expect(token.Rparen)

		switch len(values) {
		case 1:
			return &ast.ParenExpr{
				Lparen: lparen,
				Value:  values[0].Expr,
				Rparen: rparen,
			}

		case 0:
			pos := lparen
			p.errorExpected(pos, "expression")
			return &ast.BadExpr{From: pos, To: p.pos}

		default:
			return &ast.TupleExpr{
				Lparen: lparen,
				Values: values,
				Rparen: rparen,
			}
		}
		p.next()
		pe.Value = p.parseExpr()
		pe.Rparen = p.expect(token.Rparen)
		return pe

	case token.Ident:
		return p.parseIdent()

A pkg/parser/testdata/fn_invalid_empty_tuple.snow => pkg/parser/testdata/fn_invalid_empty_tuple.snow +3 -0
@@ 0,0 1,3 @@
fn main() {
  let x = ()
}

A pkg/parser/testdata/fn_invalid_empty_tuple.snow.err => pkg/parser/testdata/fn_invalid_empty_tuple.snow.err +1 -0
@@ 0,0 1,1 @@
testdata/fn_invalid_empty_tuple.snow:2:11: expected expression

A pkg/parser/testdata/fn_invalid_empty_tuple.snow.want => pkg/parser/testdata/fn_invalid_empty_tuple.snow.want +8 -0
@@ 0,0 1,8 @@
file [1, #0] [0:26]
  fn [0:26]
    ident [main] [3:7]
    sig [0->0] [7:9]
    block [1] [10:26]
      let= [14:24]
        ident [x] [18:19]
        bad expr [22:24]

A pkg/parser/testdata/fn_nested_tuples.snow => pkg/parser/testdata/fn_nested_tuples.snow +6 -0
@@ 0,0 1,6 @@
fn main() {
  var a = (1, 2)
  var b = ("x", a)
  var c = a.0
  var d = b.1.1
}

A pkg/parser/testdata/fn_nested_tuples.snow.err => pkg/parser/testdata/fn_nested_tuples.snow.err +0 -0
A pkg/parser/testdata/fn_nested_tuples.snow.want => pkg/parser/testdata/fn_nested_tuples.snow.want +31 -0
@@ 0,0 1,31 @@
file [1, #0] [0:79]
  fn [0:79]
    ident [main] [3:7]
    sig [0->0] [7:9]
    block [4] [10:79]
      var= [14:28]
        ident [a] [18:19]
        tuple value [2] [22:28]
          item [23:25]
            int [1] [23:24]
          item [26:27]
            int [2] [26:27]
      var= [31:47]
        ident [b] [35:36]
        tuple value [2] [39:47]
          item [40:44]
            string ["x"] [40:43]
          item [45:46]
            ident [a] [45:46]
      var= [50:61]
        ident [c] [54:55]
        select [58:61]
          ident [a] [58:59]
          ident [0] [60:61]
      var= [64:77]
        ident [d] [68:69]
        select [72:77]
          select [72:75]
            ident [b] [72:73]
            ident [1] [74:75]
          ident [1] [76:77]

A pkg/parser/testdata/var_invalid_empty_tuple.snow => pkg/parser/testdata/var_invalid_empty_tuple.snow +1 -0
@@ 0,0 1,1 @@
var x: ()

A pkg/parser/testdata/var_invalid_empty_tuple.snow.err => pkg/parser/testdata/var_invalid_empty_tuple.snow.err +1 -0
@@ 0,0 1,1 @@
testdata/var_invalid_empty_tuple.snow:1:8: expected type

A pkg/parser/testdata/var_invalid_empty_tuple.snow.want => pkg/parser/testdata/var_invalid_empty_tuple.snow.want +4 -0
@@ 0,0 1,4 @@
file [1, #0] [0:9]
  var: [0:9]
    ident [x] [4:5]
    bad expr [7:9]

A pkg/parser/testdata/var_paren_type.snow => pkg/parser/testdata/var_paren_type.snow +1 -0
@@ 0,0 1,1 @@
var x: (int)

A pkg/parser/testdata/var_paren_type.snow.err => pkg/parser/testdata/var_paren_type.snow.err +0 -0
A pkg/parser/testdata/var_paren_type.snow.want => pkg/parser/testdata/var_paren_type.snow.want +5 -0
@@ 0,0 1,5 @@
file [1, #0] [0:12]
  var: [0:12]
    ident [x] [4:5]
    paren [7:12]
      ident [int] [8:11]

A pkg/parser/testdata/var_paren_type_trail_comma.snow => pkg/parser/testdata/var_paren_type_trail_comma.snow +1 -0
@@ 0,0 1,1 @@
var x: (int, )

A pkg/parser/testdata/var_paren_type_trail_comma.snow.err => pkg/parser/testdata/var_paren_type_trail_comma.snow.err +0 -0
A pkg/parser/testdata/var_paren_type_trail_comma.snow.want => pkg/parser/testdata/var_paren_type_trail_comma.snow.want +5 -0
@@ 0,0 1,5 @@
file [1, #0] [0:14]
  var: [0:14]
    ident [x] [4:5]
    paren [7:14]
      ident [int] [8:11]

A pkg/parser/testdata/var_tuple.snow => pkg/parser/testdata/var_tuple.snow +1 -0
@@ 0,0 1,1 @@
var t = (1, "a", true,)

A pkg/parser/testdata/var_tuple.snow.err => pkg/parser/testdata/var_tuple.snow.err +0 -0
A pkg/parser/testdata/var_tuple.snow.want => pkg/parser/testdata/var_tuple.snow.want +10 -0
@@ 0,0 1,10 @@
file [1, #0] [0:23]
  var= [0:23]
    ident [t] [4:5]
    tuple value [3] [8:23]
      item [9:11]
        int [1] [9:10]
      item [12:16]
        string ["a"] [12:15]
      item [17:22]
        ident [true] [17:21]

A pkg/parser/testdata/var_tuple_type.snow => pkg/parser/testdata/var_tuple_type.snow +1 -0
@@ 0,0 1,1 @@
var x: (int, uint,)

A pkg/parser/testdata/var_tuple_type.snow.err => pkg/parser/testdata/var_tuple_type.snow.err +0 -0
A pkg/parser/testdata/var_tuple_type.snow.want => pkg/parser/testdata/var_tuple_type.snow.want +8 -0
@@ 0,0 1,8 @@
file [1, #0] [0:19]
  var: [0:19]
    ident [x] [4:5]
    tuple type [2] [7:19]
      param [8:12]
        ident [int] [8:11]
      param [13:18]
        ident [uint] [13:17]

M pkg/printer/printer.go => pkg/printer/printer.go +4 -0
@@ 142,6 142,10 @@ func (p *Printer) Visit(n ast.Node) ast.Visitor {
			ret = 1
		}
		p.printMsg(fmt.Sprintf("sig [%d->%d]", len(n.Params), ret), true)
	case *ast.TupleType:
		p.printMsg(fmt.Sprintf("tuple type [%d]", len(n.Types)), true)
	case *ast.TupleExpr:
		p.printMsg(fmt.Sprintf("tuple value [%d]", len(n.Values)), true)
	case *ast.ParamDef:
		p.printMsg("param", true)
	case *ast.Block:

M pkg/scanner/scanner.go => pkg/scanner/scanner.go +1 -1
@@ 203,7 203,7 @@ func (s *Scanner) Scan() (pos token.Pos, tok token.Token, lit string) {
		}
		insertSemi = (tok == token.Ident || tok == token.Return)

	case isDecimal(cur) || cur == '.' && isDecimal(rune(s.peek())):
	case isDecimal(cur):
		tok, lit = s.number()
		insertSemi = true


M pkg/typecheck/check_pass.go => pkg/typecheck/check_pass.go +44 -21
@@ 193,6 193,24 @@ func (c *checkPass) Visit(n ast.Node) ast.Visitor {
			return nil
		}

	case *ast.TupleType:
		for _, typ := range n.Types {
			ast.Walk(c, typ)
		}
		var tupt *TupleType
		if !c.expectTypeCtx(n, &tupt, Typ) {
			return nil
		}

	case *ast.TupleExpr: // *ast.ExprItem processed here directly
		for _, val := range n.Values {
			ast.Walk(c, val.Expr)
		}
		var tupt *TupleType
		if !c.expectTypeCtx(n, &tupt, Value) {
			return nil
		}

	case *ast.Block:
		return c



@@ 442,33 460,29 @@ func (c *checkPass) Visit(n ast.Node) ast.Visitor {
		ast.Walk(c, n.Left)
		ast.Walk(c, n.Sel)

		// type of n.Left must be a struct
		var st *StructType
		if !c.expectTypeCtx(n.Left, &st) {
			return nil
		}

		// Sel must exist in struct's scope, and must have the type of its symbol
		var selt Type
		if !c.expectTypeCtx(n.Sel, &selt) {
		ltyp, lctx := c.types.mustTypeOf(n.Left)
		lstyp, ok := ltyp.(SelectableType)
		if !ok {
			c.errHandler(n.Left.Pos(), fmt.Sprintf("invalid type %s for field selection", ltyp))
			return nil
		}

		field := st.scope.Lookup(n.Sel.Name)
		if field == nil {
		// Sel must exist and must have the type of its symbol
		selObj, selTyp := lstyp.Select(n.Sel)
		if selTyp == nil {
			// type would be unresolved and error added when walking n.Sel
			return nil
		}
		if !field.Type().IdenticalTo(selt) {
			c.errHandler(n.Sel.Pos(), fmt.Sprintf("invalid type of field %s: expected %s, got %s", n.Sel.Name, field.Type(), selt))
		if selObj != nil && !selObj.Type().IdenticalTo(selTyp) {
			c.errHandler(n.Sel.Pos(), fmt.Sprintf("invalid type of field %s: expected %s, got %s", n.Sel.Name, selObj.Type(), selTyp))
			return nil
		}

		var fno *FnObj
		if AsObj(field, &fno) && fno.IsRef {
		if AsObj(selObj, &fno) && fno.IsRef {
			// Left must be mutable
			if _, ctx := c.types.mustTypeOf(n.Left); ctx != Mutable {
				c.errHandler(n.Sel.Pos(), fmt.Sprintf("cannot access ref fn %s; left-hand side must be %s, is %s", n.Sel.Name, Mutable, ctx))
			if lctx != Mutable {
				c.errHandler(n.Sel.Pos(), fmt.Sprintf("cannot access ref fn %s; left-hand side must be %s, is %s", n.Sel.Name, Mutable, lctx))
			}
		}



@@ 560,18 574,27 @@ func (c *checkPass) Visit(n ast.Node) ast.Visitor {
	case *ast.Ident:
		// can only check that it has a valid type and context
		var idt Type
		if !c.expectTypeCtx(n, &idt, Typ, Mutable, Immutable) {
		ctxs := []TypeContext{Typ, Mutable, Immutable}
		// tuple field selector is an identifier but it can be in a value context, and not a Typ one
		if n.TupleIndex >= 0 {
			// replace Typ with Value
			ctxs[0] = Value
		}
		if !c.expectTypeCtx(n, &idt, ctxs...) {
			return nil
		}
		// TODO: surely other places should call Valid on the Type? All expressions?
		if !idt.Valid() {
			c.errHandler(n.Pos(), fmt.Sprintf("invalid type for identifier %s: %s", n.Name, idt))
			return nil
		}
		if c.fnInfo.methodMinDepth > 0 {
			// inside a method; if this is a variable, make sure it is in a scope >= minDepth
			// or in the top-level scope.
			if _, ctx := c.types.mustTypeOf(n); ctx == Mutable || ctx == Immutable {
				declScope := c.uses[n].ParentScope()
			// inside a method; if this is a use of a variable, make sure it is in a
			// scope >= minDepth or in the top-level scope.
			_, ctx := c.types.mustTypeOf(n)
			usedObj := c.uses[n]
			if usedObj != nil && ctx.isAnyOf(Mutable, Immutable) {
				declScope := usedObj.ParentScope()
				if declScope.Depth < c.fnInfo.methodMinDepth && !declScope.IsTopLevel() {
					c.errHandler(n.Pos(), fmt.Sprintf("%s is not a field on %s nor a top-level symbol", n.Name, c.fnInfo.methodOn.Type()))
				}

M pkg/typecheck/testdata/check/fn_access_invalid_struct_field.snow.want => pkg/typecheck/testdata/check/fn_access_invalid_struct_field.snow.want +3 -3
@@ 14,14 14,14 @@ file [2, #0]
      expr
        select [var: unresolved]
          ident [s] [var: struct S]
          ident [y] [let: unresolved]
          ident [y] [invalid: unresolved]
      assign
        select [var: unresolved]
          ident [s] [var: struct S]
          ident [y] [let: unresolved]
          ident [y] [invalid: unresolved]
        int [1] [const: int]
      var=
        ident [x] [var: unresolved]
        select [var: unresolved]
          ident [s] [var: struct S]
          ident [z] [let: unresolved]
          ident [z] [invalid: unresolved]

A pkg/typecheck/testdata/check/fn_complex_selectors.snow.err => pkg/typecheck/testdata/check/fn_complex_selectors.snow.err +0 -0
A pkg/typecheck/testdata/check/fn_complex_selectors.snow.want => pkg/typecheck/testdata/check/fn_complex_selectors.snow.want +79 -0
@@ 0,0 1,79 @@
file [3, #0]
  struct [3]
    ident [S] [type: struct S]
    var:
      ident [s1] [var: int]
      ident [int] [type: int]
    var:
      ident [s2] [var: struct T]
      ident [T] [type: struct T]
    struct [2]
      ident [T] [type: struct T]
      var:
        ident [t1] [var: string]
        ident [string] [type: string]
      fn
        ident [do] [let: () -> (int, () -> int)]
        sig [0->1]
          tuple type [2] [type: (int, () -> int)]
            param
              ident [int] [type: int]
            param
              sig [0->1] [type: () -> int]
                ident [int] [type: int]
        block [2]
          fn
            ident [func] [let: () -> int]
            sig [0->1]
              ident [int] [type: int]
            block [1]
              return
                int [1] [const: int]
          return
            tuple value [2] [value: (int, () -> int)]
              item
                int [3] [const: int]
              item
                ident [func] [let: () -> int]
  fn
    ident [create] [let: () -> struct S]
    sig [0->1]
      ident [S] [type: struct S]
    block [2]
      var:
        ident [s] [var: struct S]
        ident [S] [type: struct S]
      return
        ident [s] [var: struct S]
  fn
    ident [main] [let: () -> void]
    sig [0->0]
    block [3]
      expr
        select [value: string]
          select [value: struct T]
            call [0] [value: struct S]
              ident [create] [let: () -> struct S]
            ident [s2] [var: struct T]
          ident [t1] [var: string]
      expr
        select [value: int]
          call [0] [value: (int, () -> int)]
            select [value: () -> (int, () -> int)]
              select [value: struct T]
                call [0] [value: struct S]
                  ident [create] [let: () -> struct S]
                ident [s2] [var: struct T]
              ident [do] [let: () -> (int, () -> int)]
          ident [0] [value: int]
      expr
        call [0] [value: int]
          select [value: () -> int]
            call [0] [value: (int, () -> int)]
              select [value: () -> (int, () -> int)]
                select [value: struct T]
                  call [0] [value: struct S]
                    ident [create] [let: () -> struct S]
                  ident [s2] [var: struct T]
                ident [do] [let: () -> (int, () -> int)]
            ident [1] [value: () -> int]

A pkg/typecheck/testdata/check/fn_invalid_tuple_access.snow.err => pkg/typecheck/testdata/check/fn_invalid_tuple_access.snow.err +2 -0
@@ 0,0 1,2 @@
testdata/fn_invalid_tuple_access.snow:3:3: undefined in (int, string, (bool, uint)): 3
testdata/fn_invalid_tuple_access.snow:4:3: undefined in (bool, uint): 2

A pkg/typecheck/testdata/check/fn_invalid_tuple_access.snow.want => pkg/typecheck/testdata/check/fn_invalid_tuple_access.snow.want +28 -0
@@ 0,0 1,28 @@
file [1, #0]
  fn
    ident [main] [let: () -> void]
    sig [0->0]
    block [3]
      var:
        ident [x] [var: (int, string, (bool, uint))]
        tuple type [3] [type: (int, string, (bool, uint))]
          param
            ident [int] [type: int]
          param
            ident [string] [type: string]
          param
            tuple type [2] [type: (bool, uint)]
              param
                ident [bool] [type: bool]
              param
                ident [uint] [type: uint]
      expr
        select [var: unresolved]
          ident [x] [var: (int, string, (bool, uint))]
          ident [3] [invalid: unresolved]
      expr
        select [var: unresolved]
          select [var: (bool, uint)]
            ident [x] [var: (int, string, (bool, uint))]
            ident [2] [var: (bool, uint)]
          ident [2] [invalid: unresolved]

A pkg/typecheck/testdata/check/fn_nested_tuple_type.snow.err => pkg/typecheck/testdata/check/fn_nested_tuple_type.snow.err +0 -0
A pkg/typecheck/testdata/check/fn_nested_tuple_type.snow.want => pkg/typecheck/testdata/check/fn_nested_tuple_type.snow.want +16 -0
@@ 0,0 1,16 @@
file [1, #0]
  fn
    ident [main] [let: () -> void]
    sig [0->0]
    block [1]
      var:
        ident [x] [var: (int, (bool, string))]
        tuple type [2] [type: (int, (bool, string))]
          param
            ident [int] [type: int]
          param
            tuple type [2] [type: (bool, string)]
              param
                ident [bool] [type: bool]
              param
                ident [string] [type: string]

M pkg/typecheck/testdata/check/fn_non_type_selectors.snow.err => pkg/typecheck/testdata/check/fn_non_type_selectors.snow.err +6 -6
@@ 1,16 1,16 @@
testdata/fn_non_type_selectors.snow:12:7: invalid type for identifier v1: unresolved
testdata/fn_non_type_selectors.snow:12:11: expected type StructType; got int
testdata/fn_non_type_selectors.snow:12:11: expected type context to be type; got var
testdata/fn_non_type_selectors.snow:12:13: invalid type for identifier y: unresolved
testdata/fn_non_type_selectors.snow:12:11: invalid type int for field selection
testdata/fn_non_type_selectors.snow:12:13: expected type context to be one of type, var, let; got invalid
testdata/fn_non_type_selectors.snow:13:7: invalid type for identifier v2: unresolved
testdata/fn_non_type_selectors.snow:13:11: expected type StructType; got int
testdata/fn_non_type_selectors.snow:13:11: expected type context to be type; got var
testdata/fn_non_type_selectors.snow:13:13: invalid type for identifier S: unresolved
testdata/fn_non_type_selectors.snow:13:11: invalid type int for field selection
testdata/fn_non_type_selectors.snow:13:13: expected type context to be one of type, var, let; got invalid
testdata/fn_non_type_selectors.snow:14:11: expected type context to be type; got invalid
testdata/fn_non_type_selectors.snow:15:11: expected type context to be type; got invalid
testdata/fn_non_type_selectors.snow:16:7: invalid type for identifier v5: unresolved
testdata/fn_non_type_selectors.snow:16:11: expected type context to be type; got var
testdata/fn_non_type_selectors.snow:16:12: expected type StructType; got int
testdata/fn_non_type_selectors.snow:16:14: invalid type for identifier y: unresolved
testdata/fn_non_type_selectors.snow:16:12: invalid type int for field selection
testdata/fn_non_type_selectors.snow:16:14: expected type context to be one of type, var, let; got invalid
testdata/fn_non_type_selectors.snow:17:11: expected type context to be type; got invalid
testdata/fn_non_type_selectors.snow:18:11: expected type context to be type; got invalid

M pkg/typecheck/testdata/check/fn_non_type_selectors.snow.want => pkg/typecheck/testdata/check/fn_non_type_selectors.snow.want +3 -3
@@ 23,12 23,12 @@ file [4, #0]
        ident [v1] [var: unresolved]
        select [var: unresolved]
          ident [x] [var: int]
          ident [y] [let: unresolved]
          ident [y] [invalid: unresolved]
      var:
        ident [v2] [var: unresolved]
        select [var: unresolved]
          ident [x] [var: int]
          ident [S] [let: unresolved]
          ident [S] [invalid: unresolved]
      var:
        ident [v3] [var: int]
        select [invalid: int]


@@ 46,7 46,7 @@ file [4, #0]
        paren [var: unresolved]
          select [var: unresolved]
            ident [x] [var: int]
            ident [y] [let: unresolved]
            ident [y] [invalid: unresolved]
      var:
        ident [v6] [var: int]
        paren [invalid: int]

A pkg/typecheck/testdata/check/fn_tuple_assign_compatible_types.snow.err => pkg/typecheck/testdata/check/fn_tuple_assign_compatible_types.snow.err +0 -0
A pkg/typecheck/testdata/check/fn_tuple_assign_compatible_types.snow.want => pkg/typecheck/testdata/check/fn_tuple_assign_compatible_types.snow.want +29 -0
@@ 0,0 1,29 @@
file [1, #0]
  fn
    ident [main] [let: () -> void]
    sig [0->0]
    block [4]
      var:
        ident [i] [var: i16]
        ident [i16] [type: i16]
      var:
        ident [u] [var: u8]
        ident [u8] [type: u8]
      var:
        ident [t] [var: (i32, u16, bool)]
        tuple type [3] [type: (i32, u16, bool)]
          param
            ident [i32] [type: i32]
          param
            ident [u16] [type: u16]
          param
            ident [bool] [type: bool]
      assign
        ident [t] [var: (i32, u16, bool)]
        tuple value [3] [value: (i16, u8, bool)]
          item
            ident [i] [var: i16]
          item
            ident [u] [var: u8]
          item
            ident [true] [let: bool]

A pkg/typecheck/testdata/check/fn_tuple_assign_incompatible_types.snow.err => pkg/typecheck/testdata/check/fn_tuple_assign_incompatible_types.snow.err +1 -0
@@ 0,0 1,1 @@
testdata/fn_tuple_assign_incompatible_types.snow:6:7: cannot assign type (i64, u32, bool) to variable of type (i32, u16, bool)

A pkg/typecheck/testdata/check/fn_tuple_assign_incompatible_types.snow.want => pkg/typecheck/testdata/check/fn_tuple_assign_incompatible_types.snow.want +29 -0
@@ 0,0 1,29 @@
file [1, #0]
  fn
    ident [main] [let: () -> void]
    sig [0->0]
    block [4]
      var:
        ident [t] [var: (i32, u16, bool)]
        tuple type [3] [type: (i32, u16, bool)]
          param
            ident [i32] [type: i32]
          param
            ident [u16] [type: u16]
          param
            ident [bool] [type: bool]
      var:
        ident [a] [var: i64]
        ident [i64] [type: i64]
      var:
        ident [b] [var: u32]
        ident [u32] [type: u32]
      assign
        ident [t] [var: (i32, u16, bool)]
        tuple value [3] [value: (i64, u32, bool)]
          item
            ident [a] [var: i64]
          item
            ident [b] [var: u32]
          item
            ident [false] [let: bool]

A pkg/typecheck/testdata/check/fn_tuple_expr_select_field.snow.err => pkg/typecheck/testdata/check/fn_tuple_expr_select_field.snow.err +1 -0
@@ 0,0 1,1 @@
testdata/fn_tuple_expr_select_field.snow:6:3: expected type context to be var; got let

A pkg/typecheck/testdata/check/fn_tuple_expr_select_field.snow.want => pkg/typecheck/testdata/check/fn_tuple_expr_select_field.snow.want +31 -0
@@ 0,0 1,31 @@
file [1, #0]
  fn
    ident [main] [let: () -> void]
    sig [0->0]
    block [4]
      var=
        ident [t1] [var: (int, int, int)]
        tuple value [3] [value: (int, int, int)]
          item
            int [1] [const: int]
          item
            int [2] [const: int]
          item
            int [3] [const: int]
      let=
        ident [t2] [let: (string, string)]
        tuple value [2] [value: (string, string)]
          item
            string ["a"] [const: string]
          item
            string ["b"] [const: string]
      assign
        select [var: int]
          ident [t1] [var: (int, int, int)]
          ident [0] [var: int]
        int [4] [const: int]
      assign
        select [let: string]
          ident [t2] [let: (string, string)]
          ident [1] [let: string]
        string ["c"] [const: string]

A pkg/typecheck/testdata/check/let_invalid_tuple_type.snow.err => pkg/typecheck/testdata/check/let_invalid_tuple_type.snow.err +1 -0
@@ 0,0 1,1 @@
testdata/let_invalid_tuple_type.snow:1:14: expected type context to be type; got let

A pkg/typecheck/testdata/check/let_invalid_tuple_type.snow.want => pkg/typecheck/testdata/check/let_invalid_tuple_type.snow.want +8 -0
@@ 0,0 1,8 @@
file [1, #0]
  let:
    ident [x] [let: (int, bool)]
    tuple type [2] [type: (int, bool)]
      param
        ident [int] [type: int]
      param
        ident [true] [let: bool]

A pkg/typecheck/testdata/check/var_tuple_type.snow.err => pkg/typecheck/testdata/check/var_tuple_type.snow.err +0 -0
A pkg/typecheck/testdata/check/var_tuple_type.snow.want => pkg/typecheck/testdata/check/var_tuple_type.snow.want +10 -0
@@ 0,0 1,10 @@
file [1, #0]
  var:
    ident [x] [var: (int, string, bool)]
    tuple type [3] [type: (int, string, bool)]
      param
        ident [int] [type: int]
      param
        ident [string] [type: string]
      param
        ident [bool] [type: bool]

A pkg/typecheck/testdata/fn_complex_selectors.snow => pkg/typecheck/testdata/fn_complex_selectors.snow +26 -0
@@ 0,0 1,26 @@
struct S {
  var s1: int
  var s2: T

  struct T {
    var t1: string

    fn do() -> (int, ()->int) {
      fn func() -> int {
        return 1
      }
      return (3, func)
    }
  }
}

fn create() -> S {
  var s: S
  return s
}

fn main() {
  create().s2.t1
  create().s2.do().0
  create().s2.do().1()
}

A pkg/typecheck/testdata/fn_invalid_tuple_access.snow => pkg/typecheck/testdata/fn_invalid_tuple_access.snow +5 -0
@@ 0,0 1,5 @@
fn main() {
  var x: (int, string, (bool, uint))
  x.3
  x.2.2
}

A pkg/typecheck/testdata/fn_nested_tuple_type.snow => pkg/typecheck/testdata/fn_nested_tuple_type.snow +3 -0
@@ 0,0 1,3 @@
fn main() {
  var x: (int, (bool, string))
}

A pkg/typecheck/testdata/fn_tuple_assign_compatible_types.snow => pkg/typecheck/testdata/fn_tuple_assign_compatible_types.snow +7 -0
@@ 0,0 1,7 @@
fn main() {
  var i: i16
  var u: u8
  var t: (i32, u16, bool)

  t = (i, u, true)
}

A pkg/typecheck/testdata/fn_tuple_assign_incompatible_types.snow => pkg/typecheck/testdata/fn_tuple_assign_incompatible_types.snow +8 -0
@@ 0,0 1,8 @@
fn main() {
  var t: (i32, u16, bool)
  var a: i64 
  var b: u32

  t = (a, b, false)
}


A pkg/typecheck/testdata/fn_tuple_expr_select_field.snow => pkg/typecheck/testdata/fn_tuple_expr_select_field.snow +7 -0
@@ 0,0 1,7 @@
fn main() {
  var t1 = (1, 2, 3)
  let t2 = ("a", "b")

  t1.0 = 4
  t2.1 = "c"
}

A pkg/typecheck/testdata/let_invalid_tuple_type.snow => pkg/typecheck/testdata/let_invalid_tuple_type.snow +1 -0
@@ 0,0 1,1 @@
let x: (int, true)

A pkg/typecheck/testdata/scopes/fn_complex_selectors.snow.err => pkg/typecheck/testdata/scopes/fn_complex_selectors.snow.err +0 -0
A pkg/typecheck/testdata/scopes/fn_complex_selectors.snow.want => pkg/typecheck/testdata/scopes/fn_complex_selectors.snow.want +45 -0
@@ 0,0 1,45 @@
U <nil> {
.  bool
.  f32
.  f64
.  false
.  float
.  i16
.  i32
.  i64
.  i8
.  int
.  string
.  true
.  u16
.  u32
.  u64
.  u8
.  uint
.  void
.  U.0 *ast.File {
.  .  S
.  .  create
.  .  main
.  .  U.0.0 *ast.StructDecl {
.  .  .  T
.  .  .  s1
.  .  .  s2
.  .  .  U.0.0.0 *ast.StructDecl {
.  .  .  .  do
.  .  .  .  t1
.  .  .  .  U.0.0.0.0 *ast.FnDecl {
.  .  .  .  .  func
.  .  .  .  .  self
.  .  .  .  .  U.0.0.0.0.0 *ast.FnDecl {
.  .  .  .  .  }
.  .  .  .  }
.  .  .  }
.  .  }
.  .  U.0.1 *ast.FnDecl {
.  .  .  s
.  .  }
.  .  U.0.2 *ast.FnDecl {
.  .  }
.  }
}

A pkg/typecheck/testdata/scopes/fn_invalid_tuple_access.snow.err => pkg/typecheck/testdata/scopes/fn_invalid_tuple_access.snow.err +0 -0
A pkg/typecheck/testdata/scopes/fn_invalid_tuple_access.snow.want => pkg/typecheck/testdata/scopes/fn_invalid_tuple_access.snow.want +26 -0
@@ 0,0 1,26 @@
U <nil> {
.  bool
.  f32
.  f64
.  false
.  float
.  i16
.  i32
.  i64
.  i8
.  int
.  string
.  true
.  u16
.  u32
.  u64
.  u8
.  uint
.  void
.  U.0 *ast.File {
.  .  main
.  .  U.0.0 *ast.FnDecl {
.  .  .  x
.  .  }
.  }
}

A pkg/typecheck/testdata/scopes/fn_nested_tuple_type.snow.err => pkg/typecheck/testdata/scopes/fn_nested_tuple_type.snow.err +0 -0
A pkg/typecheck/testdata/scopes/fn_nested_tuple_type.snow.want => pkg/typecheck/testdata/scopes/fn_nested_tuple_type.snow.want +26 -0
@@ 0,0 1,26 @@
U <nil> {
.  bool
.  f32
.  f64
.  false
.  float
.  i16
.  i32
.  i64
.  i8
.  int
.  string
.  true
.  u16
.  u32
.  u64
.  u8
.  uint
.  void
.  U.0 *ast.File {
.  .  main
.  .  U.0.0 *ast.FnDecl {
.  .  .  x
.  .  }
.  }
}

A pkg/typecheck/testdata/scopes/fn_tuple_assign_compatible_types.snow.err => pkg/typecheck/testdata/scopes/fn_tuple_assign_compatible_types.snow.err +0 -0
A pkg/typecheck/testdata/scopes/fn_tuple_assign_compatible_types.snow.want => pkg/typecheck/testdata/scopes/fn_tuple_assign_compatible_types.snow.want +28 -0
@@ 0,0 1,28 @@
U <nil> {
.  bool
.  f32
.  f64
.  false
.  float
.  i16
.  i32
.  i64
.  i8
.  int
.  string
.  true
.  u16
.  u32
.  u64
.  u8
.  uint
.  void
.  U.0 *ast.File {
.  .  main
.  .  U.0.0 *ast.FnDecl {
.  .  .  i
.  .  .  t
.  .  .  u
.  .  }
.  }
}

A pkg/typecheck/testdata/scopes/fn_tuple_assign_incompatible_types.snow.err => pkg/typecheck/testdata/scopes/fn_tuple_assign_incompatible_types.snow.err +0 -0
A pkg/typecheck/testdata/scopes/fn_tuple_assign_incompatible_types.snow.want => pkg/typecheck/testdata/scopes/fn_tuple_assign_incompatible_types.snow.want +28 -0
@@ 0,0 1,28 @@
U <nil> {
.  bool
.  f32
.  f64
.  false
.  float
.  i16
.  i32
.  i64
.  i8
.  int
.  string
.  true
.  u16
.  u32
.  u64
.  u8
.  uint
.  void
.  U.0 *ast.File {
.  .  main
.  .  U.0.0 *ast.FnDecl {
.  .  .  a
.  .  .  b
.  .  .  t
.  .  }
.  }
}

A pkg/typecheck/testdata/scopes/fn_tuple_expr_select_field.snow.err => pkg/typecheck/testdata/scopes/fn_tuple_expr_select_field.snow.err +0 -0
A pkg/typecheck/testdata/scopes/fn_tuple_expr_select_field.snow.want => pkg/typecheck/testdata/scopes/fn_tuple_expr_select_field.snow.want +27 -0
@@ 0,0 1,27 @@
U <nil> {
.  bool
.  f32
.  f64
.  false
.  float
.  i16
.  i32
.  i64
.  i8
.  int
.  string
.  true
.  u16
.  u32
.  u64
.  u8
.  uint
.  void
.  U.0 *ast.File {
.  .  main
.  .  U.0.0 *ast.FnDecl {
.  .  .  t1
.  .  .  t2
.  .  }
.  }
}

A pkg/typecheck/testdata/scopes/let_invalid_tuple_type.snow.err => pkg/typecheck/testdata/scopes/let_invalid_tuple_type.snow.err +0 -0
A pkg/typecheck/testdata/scopes/let_invalid_tuple_type.snow.want => pkg/typecheck/testdata/scopes/let_invalid_tuple_type.snow.want +23 -0
@@ 0,0 1,23 @@
U <nil> {
.  bool
.  f32
.  f64
.  false
.  float
.  i16
.  i32
.  i64
.  i8
.  int
.  string
.  true
.  u16
.  u32
.  u64
.  u8
.  uint
.  void
.  U.0 *ast.File {
.  .  x
.  }
}

A pkg/typecheck/testdata/scopes/var_tuple_type.snow.err => pkg/typecheck/testdata/scopes/var_tuple_type.snow.err +0 -0
A pkg/typecheck/testdata/scopes/var_tuple_type.snow.want => pkg/typecheck/testdata/scopes/var_tuple_type.snow.want +23 -0
@@ 0,0 1,23 @@
U <nil> {
.  bool
.  f32
.  f64
.  false
.  float
.  i16
.  i32
.  i64
.  i8
.  int
.  string
.  true
.  u16
.  u32
.  u64
.  u8
.  uint
.  void
.  U.0 *ast.File {
.  .  x
.  }
}

A pkg/typecheck/testdata/static/fn_complex_selectors.snow.err => pkg/typecheck/testdata/static/fn_complex_selectors.snow.err +0 -0
A pkg/typecheck/testdata/static/fn_invalid_tuple_access.snow.err => pkg/typecheck/testdata/static/fn_invalid_tuple_access.snow.err +2 -0
@@ 0,0 1,2 @@
testdata/fn_invalid_tuple_access.snow:3:3: undefined in (int, string, (bool, uint)): 3
testdata/fn_invalid_tuple_access.snow:4:3: undefined in (bool, uint): 2

A pkg/typecheck/testdata/static/fn_nested_tuple_type.snow.err => pkg/typecheck/testdata/static/fn_nested_tuple_type.snow.err +0 -0
M pkg/typecheck/testdata/static/fn_non_type_selectors.snow.err => pkg/typecheck/testdata/static/fn_non_type_selectors.snow.err +6 -6
@@ 1,16 1,16 @@
testdata/fn_non_type_selectors.snow:12:7: invalid type for identifier v1: unresolved
testdata/fn_non_type_selectors.snow:12:11: expected type StructType; got int
testdata/fn_non_type_selectors.snow:12:11: expected type context to be type; got var
testdata/fn_non_type_selectors.snow:12:13: invalid type for identifier y: unresolved
testdata/fn_non_type_selectors.snow:12:11: invalid type int for field selection
testdata/fn_non_type_selectors.snow:12:13: expected type context to be one of type, var, let; got invalid
testdata/fn_non_type_selectors.snow:13:7: invalid type for identifier v2: unresolved
testdata/fn_non_type_selectors.snow:13:11: expected type StructType; got int
testdata/fn_non_type_selectors.snow:13:11: expected type context to be type; got var
testdata/fn_non_type_selectors.snow:13:13: invalid type for identifier S: unresolved
testdata/fn_non_type_selectors.snow:13:11: invalid type int for field selection
testdata/fn_non_type_selectors.snow:13:13: expected type context to be one of type, var, let; got invalid
testdata/fn_non_type_selectors.snow:14:11: expected type context to be type; got invalid
testdata/fn_non_type_selectors.snow:15:11: expected type context to be type; got invalid
testdata/fn_non_type_selectors.snow:16:7: invalid type for identifier v5: unresolved
testdata/fn_non_type_selectors.snow:16:11: expected type context to be type; got var
testdata/fn_non_type_selectors.snow:16:12: expected type StructType; got int
testdata/fn_non_type_selectors.snow:16:14: invalid type for identifier y: unresolved
testdata/fn_non_type_selectors.snow:16:12: invalid type int for field selection
testdata/fn_non_type_selectors.snow:16:14: expected type context to be one of type, var, let; got invalid
testdata/fn_non_type_selectors.snow:17:11: expected type context to be type; got invalid
testdata/fn_non_type_selectors.snow:18:11: expected type context to be type; got invalid

A pkg/typecheck/testdata/static/fn_tuple_assign_compatible_types.snow.err => pkg/typecheck/testdata/static/fn_tuple_assign_compatible_types.snow.err +0 -0
A pkg/typecheck/testdata/static/fn_tuple_assign_incompatible_types.snow.err => pkg/typecheck/testdata/static/fn_tuple_assign_incompatible_types.snow.err +1 -0
@@ 0,0 1,1 @@
testdata/fn_tuple_assign_incompatible_types.snow:6:7: cannot assign type (i64, u32, bool) to variable of type (i32, u16, bool)

A pkg/typecheck/testdata/static/fn_tuple_expr_select_field.snow.err => pkg/typecheck/testdata/static/fn_tuple_expr_select_field.snow.err +1 -0
@@ 0,0 1,1 @@
testdata/fn_tuple_expr_select_field.snow:6:3: expected type context to be var; got let

A pkg/typecheck/testdata/static/let_invalid_tuple_type.snow.err => pkg/typecheck/testdata/static/let_invalid_tuple_type.snow.err +1 -0
@@ 0,0 1,1 @@
testdata/let_invalid_tuple_type.snow:1:14: expected type context to be type; got let

A pkg/typecheck/testdata/static/var_tuple_type.snow.err => pkg/typecheck/testdata/static/var_tuple_type.snow.err +1 -0
@@ 0,0 1,1 @@
main function missing

M pkg/typecheck/testdata/types/fn_access_invalid_struct_field.snow.want => pkg/typecheck/testdata/types/fn_access_invalid_struct_field.snow.want +3 -3
@@ 14,14 14,14 @@ file [2, #0]
      expr
        select [var: unresolved]
          ident [s] [var: struct S]
          ident [y] [let: unresolved]
          ident [y] [invalid: unresolved]
      assign
        select [var: unresolved]
          ident [s] [var: struct S]
          ident [y] [let: unresolved]
          ident [y] [invalid: unresolved]
        int [1] [const: int]
      var=
        ident [x] [var: unresolved]
        select [var: unresolved]
          ident [s] [var: struct S]
          ident [z] [let: unresolved]
          ident [z] [invalid: unresolved]

A pkg/typecheck/testdata/types/fn_complex_selectors.snow.err => pkg/typecheck/testdata/types/fn_complex_selectors.snow.err +0 -0
A pkg/typecheck/testdata/types/fn_complex_selectors.snow.want => pkg/typecheck/testdata/types/fn_complex_selectors.snow.want +79 -0
@@ 0,0 1,79 @@
file [3, #0]
  struct [3]
    ident [S] [type: struct S]
    var:
      ident [s1] [var: int]
      ident [int] [type: int]
    var:
      ident [s2] [var: struct T]
      ident [T] [type: struct T]
    struct [2]
      ident [T] [type: struct T]
      var:
        ident [t1] [var: string]
        ident [string] [type: string]
      fn
        ident [do] [let: () -> (int, () -> int)]
        sig [0->1]
          tuple type [2] [type: (int, () -> int)]
            param
              ident [int] [type: int]
            param
              sig [0->1] [type: () -> int]
                ident [int] [type: int]
        block [2]
          fn
            ident [func] [let: () -> int]
            sig [0->1]
              ident [int] [type: int]
            block [1]
              return
                int [1] [const: int]
          return
            tuple value [2] [value: (int, () -> int)]
              item
                int [3] [const: int]
              item
                ident [func] [let: () -> int]
  fn
    ident [create] [let: () -> struct S]
    sig [0->1]
      ident [S] [type: struct S]
    block [2]
      var:
        ident [s] [var: struct S]
        ident [S] [type: struct S]
      return
        ident [s] [var: struct S]
  fn
    ident [main] [let: () -> void]
    sig [0->0]
    block [3]
      expr
        select [value: string]
          select [value: struct T]
            call [0] [value: struct S]
              ident [create] [let: () -> struct S]
            ident [s2] [var: struct T]
          ident [t1] [var: string]
      expr
        select [value: int]
          call [0] [value: (int, () -> int)]
            select [value: () -> (int, () -> int)]
              select [value: struct T]
                call [0] [value: struct S]
                  ident [create] [let: () -> struct S]
                ident [s2] [var: struct T]
              ident [do] [let: () -> (int, () -> int)]
          ident [0] [value: int]
      expr
        call [0] [value: int]
          select [value: () -> int]
            call [0] [value: (int, () -> int)]
              select [value: () -> (int, () -> int)]
                select [value: struct T]
                  call [0] [value: struct S]
                    ident [create] [let: () -> struct S]
                  ident [s2] [var: struct T]
                ident [do] [let: () -> (int, () -> int)]
            ident [1] [value: () -> int]

A pkg/typecheck/testdata/types/fn_invalid_tuple_access.snow.err => pkg/typecheck/testdata/types/fn_invalid_tuple_access.snow.err +2 -0
@@ 0,0 1,2 @@
testdata/fn_invalid_tuple_access.snow:3:3: undefined in (int, string, (bool, uint)): 3
testdata/fn_invalid_tuple_access.snow:4:3: undefined in (bool, uint): 2

A pkg/typecheck/testdata/types/fn_invalid_tuple_access.snow.want => pkg/typecheck/testdata/types/fn_invalid_tuple_access.snow.want +28 -0
@@ 0,0 1,28 @@
file [1, #0]
  fn
    ident [main] [let: () -> void]
    sig [0->0]
    block [3]
      var:
        ident [x] [var: (int, string, (bool, uint))]
        tuple type [3] [type: (int, string, (bool, uint))]
          param
            ident [int] [type: int]
          param
            ident [string] [type: string]
          param
            tuple type [2] [type: (bool, uint)]
              param
                ident [bool] [type: bool]
              param
                ident [uint] [type: uint]
      expr
        select [var: unresolved]
          ident [x] [var: (int, string, (bool, uint))]
          ident [3] [invalid: unresolved]
      expr
        select [var: unresolved]
          select [var: (bool, uint)]
            ident [x] [var: (int, string, (bool, uint))]
            ident [2] [var: (bool, uint)]
          ident [2] [invalid: unresolved]

A pkg/typecheck/testdata/types/fn_nested_tuple_type.snow.err => pkg/typecheck/testdata/types/fn_nested_tuple_type.snow.err +0 -0
A pkg/typecheck/testdata/types/fn_nested_tuple_type.snow.want => pkg/typecheck/testdata/types/fn_nested_tuple_type.snow.want +16 -0
@@ 0,0 1,16 @@
file [1, #0]
  fn
    ident [main] [let: () -> void]
    sig [0->0]
    block [1]
      var:
        ident [x] [var: (int, (bool, string))]
        tuple type [2] [type: (int, (bool, string))]
          param
            ident [int] [type: int]
          param
            tuple type [2] [type: (bool, string)]
              param
                ident [bool] [type: bool]
              param
                ident [string] [type: string]

M pkg/typecheck/testdata/types/fn_non_type_selectors.snow.want => pkg/typecheck/testdata/types/fn_non_type_selectors.snow.want +3 -3
@@ 23,12 23,12 @@ file [4, #0]
        ident [v1] [var: unresolved]
        select [var: unresolved]
          ident [x] [var: int]
          ident [y] [let: unresolved]
          ident [y] [invalid: unresolved]
      var:
        ident [v2] [var: unresolved]
        select [var: unresolved]
          ident [x] [var: int]
          ident [S] [let: unresolved]
          ident [S] [invalid: unresolved]
      var:
        ident [v3] [var: int]
        select [invalid: int]


@@ 46,7 46,7 @@ file [4, #0]
        paren [var: unresolved]
          select [var: unresolved]
            ident [x] [var: int]
            ident [y] [let: unresolved]
            ident [y] [invalid: unresolved]
      var:
        ident [v6] [var: int]
        paren [invalid: int]

A pkg/typecheck/testdata/types/fn_tuple_assign_compatible_types.snow.err => pkg/typecheck/testdata/types/fn_tuple_assign_compatible_types.snow.err +0 -0
A pkg/typecheck/testdata/types/fn_tuple_assign_compatible_types.snow.want => pkg/typecheck/testdata/types/fn_tuple_assign_compatible_types.snow.want +29 -0
@@ 0,0 1,29 @@
file [1, #0]
  fn
    ident [main] [let: () -> void]
    sig [0->0]
    block [4]
      var:
        ident [i] [var: i16]
        ident [i16] [type: i16]
      var:
        ident [u] [var: u8]
        ident [u8] [type: u8]
      var:
        ident [t] [var: (i32, u16, bool)]
        tuple type [3] [type: (i32, u16, bool)]
          param
            ident [i32] [type: i32]
          param
            ident [u16] [type: u16]
          param
            ident [bool] [type: bool]
      assign
        ident [t] [var: (i32, u16, bool)]
        tuple value [3] [value: (i16, u8, bool)]
          item
            ident [i] [var: i16]
          item
            ident [u] [var: u8]
          item
            ident [true] [let: bool]

A pkg/typecheck/testdata/types/fn_tuple_assign_incompatible_types.snow.err => pkg/typecheck/testdata/types/fn_tuple_assign_incompatible_types.snow.err +0 -0
A pkg/typecheck/testdata/types/fn_tuple_assign_incompatible_types.snow.want => pkg/typecheck/testdata/types/fn_tuple_assign_incompatible_types.snow.want +29 -0
@@ 0,0 1,29 @@
file [1, #0]
  fn
    ident [main] [let: () -> void]
    sig [0->0]
    block [4]
      var:
        ident [t] [var: (i32, u16, bool)]
        tuple type [3] [type: (i32, u16, bool)]
          param
            ident [i32] [type: i32]
          param
            ident [u16] [type: u16]
          param
            ident [bool] [type: bool]
      var:
        ident [a] [var: i64]
        ident [i64] [type: i64]
      var:
        ident [b] [var: u32]
        ident [u32] [type: u32]
      assign
        ident [t] [var: (i32, u16, bool)]
        tuple value [3] [value: (i64, u32, bool)]
          item
            ident [a] [var: i64]
          item
            ident [b] [var: u32]
          item
            ident [false] [let: bool]

A pkg/typecheck/testdata/types/fn_tuple_expr_select_field.snow.err => pkg/typecheck/testdata/types/fn_tuple_expr_select_field.snow.err +0 -0
A pkg/typecheck/testdata/types/fn_tuple_expr_select_field.snow.want => pkg/typecheck/testdata/types/fn_tuple_expr_select_field.snow.want +31 -0
@@ 0,0 1,31 @@
file [1, #0]
  fn
    ident [main] [let: () -> void]
    sig [0->0]
    block [4]
      var=
        ident [t1] [var: (int, int, int)]
        tuple value [3] [value: (int, int, int)]
          item
            int [1] [const: int]
          item
            int [2] [const: int]
          item
            int [3] [const: int]
      let=
        ident [t2] [let: (string, string)]
        tuple value [2] [value: (string, string)]
          item
            string ["a"] [const: string]
          item
            string ["b"] [const: string]
      assign
        select [var: int]
          ident [t1] [var: (int, int, int)]
          ident [0] [var: int]
        int [4] [const: int]
      assign
        select [let: string]
          ident [t2] [let: (string, string)]
          ident [1] [let: string]
        string ["c"] [const: string]

A pkg/typecheck/testdata/types/let_invalid_tuple_type.snow.err => pkg/typecheck/testdata/types/let_invalid_tuple_type.snow.err +0 -0
A pkg/typecheck/testdata/types/let_invalid_tuple_type.snow.want => pkg/typecheck/testdata/types/let_invalid_tuple_type.snow.want +8 -0
@@ 0,0 1,8 @@
file [1, #0]
  let:
    ident [x] [let: (int, bool)]
    tuple type [2] [type: (int, bool)]
      param
        ident [int] [type: int]
      param
        ident [true] [let: bool]

A pkg/typecheck/testdata/types/var_tuple_type.snow.err => pkg/typecheck/testdata/types/var_tuple_type.snow.err +0 -0
A pkg/typecheck/testdata/types/var_tuple_type.snow.want => pkg/typecheck/testdata/types/var_tuple_type.snow.want +10 -0
@@ 0,0 1,10 @@
file [1, #0]
  var:
    ident [x] [var: (int, string, bool)]
    tuple type [3] [type: (int, string, bool)]
      param
        ident [int] [type: int]
      param
        ident [string] [type: string]
      param
        ident [bool] [type: bool]

A pkg/typecheck/testdata/var_tuple_type.snow => pkg/typecheck/testdata/var_tuple_type.snow +1 -0
@@ 0,0 1,1 @@
var x: (int, string, bool)

M pkg/typecheck/type.go => pkg/typecheck/type.go +101 -2
@@ 21,14 21,26 @@ type Type interface {
	Valid() bool

	// Object returns the object that declared this type. It returns nil for
	// unnamed types such as function literals (e.g. (int, bool) -> int) and
	// unresolved types.
	// unnamed types such as function and tuple literals and unresolved types.
	Object() Object

	unresolvedCount() int
	fmt.Stringer
}

// SelectableType is an optional interface that is implemented by Types that
// support selection of inner fields (e.g. S.x for a struct, T.0 for tuples).
type SelectableType interface {
	Type

	// Select selects the symbol identified by the provided Identifier in the
	// type's fields or methods. It returns the matching Object and its Type
	// if found, or nil, nil if not found. Note that the Object may be nil if
	// the selection is not a declared identifier (e.g. for tuples, the fields
	// are unnamed and as such, have no corresponding declared object).
	Select(*ast.Ident) (Object, Type)
}

// List of types implementing the Type interface.
type (
	// BasicType represents a basic type such as int, bool or string. All basic types


@@ 45,6 57,11 @@ type (
		obj    Object
	}

	// TupleType represents the type of a tuple literal.
	TupleType struct {
		Fields []Type
	}

	// StructType represents a named struct type.
	StructType struct {
		// Name is the type name object related to this type.


@@ 200,6 217,80 @@ func (s *SignatureType) unresolvedCount() int {
	return count
}

// ========> implement Type for TupleType

func (t *TupleType) Object() Object { return nil }
func (t *TupleType) Valid() bool {
	for _, f := range t.Fields {
		if !f.Valid() {
			return false
		}
	}
	return true
}

func (t *TupleType) Select(id *ast.Ident) (Object, Type) {
	if id.TupleIndex < 0 || id.TupleIndex >= len(t.Fields) {
		return nil, nil
	}
	return nil, t.Fields[id.TupleIndex]
}

func (t *TupleType) String() string {
	var b strings.Builder
	b.WriteByte('(')
	for i, f := range t.Fields {
		if i > 0 {
			b.WriteString(", ")
		}
		b.WriteString(f.String())
	}
	b.WriteByte(')')
	return b.String()
}

func (t *TupleType) unresolvedCount() int {
	var count int
	for _, f := range t.Fields {
		count += f.unresolvedCount()
	}
	return count
}

func (t *TupleType) AssignableTo(T Type) bool {
	// tuple is assignable if each of the elements are assignable to
	// the target.
	var t2 *TupleType
	if !AsType(T, &t2) {
		return false
	}
	if len(t.Fields) != len(t2.Fields) {
		return false
	}
	for i, f := range t.Fields {
		if !f.AssignableTo(t2.Fields[i]) {
			return false
		}
	}
	return true
}

func (t *TupleType) IdenticalTo(T Type) bool {
	var t2 *TupleType
	if !AsType(T, &t2) {
		return false
	}
	if len(t.Fields) != len(t2.Fields) {
		return false
	}
	for i, f := range t.Fields {
		if !f.IdenticalTo(t2.Fields[i]) {
			return false
		}
	}
	return true
}

// ========> implement Type for StructType

func (s *StructType) AssignableTo(T Type) bool { return s.IdenticalTo(T) }


@@ 214,6 305,14 @@ func (s *StructType) IdenticalTo(T Type) bool {
	}
	return s.scope == s2.scope && s.Name == s2.Name
}
func (s *StructType) Select(id *ast.Ident) (Object, Type) {
	var T Type
	obj := s.scope.Lookup(id.Name)
	if obj != nil {
		T = obj.Type()
	}
	return obj, T
}

// ========> BasicKind


M pkg/typecheck/type_pass.go => pkg/typecheck/type_pass.go +42 -18
@@ 135,25 135,43 @@ func (t *typePass) Visit(n ast.Node) ast.Visitor {
		// a function type literal, with none of the argument-in-body-scope tweaks of a
		// normal FnDecl, so not much code is shared/reused, though more could probably
		// be done.
		scope := t.scope.Innermost(n.Pos())

		// build the signature type of this function literal
		sigt := &SignatureType{
			Params: make([]Type, len(n.Params)),
		}
		tt := t.clone(scope)
		for i, param := range n.Params {
			ast.Walk(tt, param.Type)
			ast.Walk(t, param.Type)
			sigt.Params[i] = t.types.typeOnly(t.types.typeOf(param.Type))
		}
		if n.RetType != nil {
			ast.Walk(tt, n.RetType)
			ast.Walk(t, n.RetType)
			sigt.Return = t.types.typeOnly(t.types.typeOf(n.RetType))
		} else {
			sigt.Return = t.scope.BuiltinType(Void)
		}
		t.types.assign(n, sigt, Typ)

	case *ast.TupleType:
		tupt := &TupleType{
			Fields: make([]Type, len(n.Types)),
		}
		for i, typ := range n.Types {
			ast.Walk(t, typ.Type)
			tupt.Fields[i] = t.types.typeOnly(t.types.typeOf(typ.Type))
		}
		t.types.assign(n, tupt, Typ)

	case *ast.TupleExpr:
		tupt := &TupleType{
			Fields: make([]Type, len(n.Values)),
		}
		for i, val := range n.Values {
			ast.Walk(t, val)
			tupt.Fields[i] = t.types.typeOnly(t.types.typeOf(val.Expr))
		}
		t.types.assign(n, tupt, Value)

	case *ast.Block:
		scope := t.scope.Innermost(n.Pos())
		tt := t.clone(scope)


@@ 301,32 319,38 @@ func (t *typePass) Visit(n ast.Node) ast.Visitor {
	case *ast.SelectorExpr:
		ast.Walk(t, n.Left)

		// must not call walk on n.Sel, as it might not find the right symbol in the
		// lookup chain - must search only in the struct's scope, done below. Assign
		// must not call walk on n.Sel, as it might not find the right symbol in
		// the lookup chain - must ask the Left Type's Select method. Assign
		// unresolved for now, until we can do more for it.
		t.types.assign(n.Sel, unresolvedType{}, Immutable)
		t.types.assign(n.Sel, unresolvedType{}, Invalid)

		ltyp, lctx := t.types.typeOf(n.Left)
		var str *StructType
		if !AsType(ltyp, &str) {
		lstyp, ok := ltyp.(SelectableType)
		if !ok {
			// set the selector expression to unresolved, will cause error in the check pass
			t.types.assign(n, unresolvedType{}, lctx)
			return nil
		}

		// look up the Sel in the scope of the Left expression
		field := str.scope.Lookup(n.Sel.Name)
		if field == nil {
			t.errHandler(n.Pos(), fmt.Sprintf("undefined in %s: %s", str, n.Sel.Name))
		selObj, selTyp := lstyp.Select(n.Sel)
		if selTyp == nil {
			t.errHandler(n.Pos(), fmt.Sprintf("undefined in %s: %s", ltyp, n.Sel.Name))
			t.types.assign(n, unresolvedType{}, lctx)
			return nil
		}

		// the type context of n.Sel is the same as the one for n.Left for tuples
		rctx := lctx
		// ... but if we have a non-nil selObj, get its type context (e.g. for struct
		// fields, the field has its own let/var context)
		if selObj != nil {
			idn := selObj.IdentNode()
			_, rctx = t.types.typeOf(idn)
			t.uses[n.Sel] = selObj
		}

		// because we didn't Walk the n.Sel identifier, register it by hand.
		idn := field.IdentNode()
		rtyp, rctx := t.types.typeOf(idn)
		t.types.assign(n.Sel, rtyp, rctx)
		t.uses[n.Sel] = field
		t.types.assign(n.Sel, selTyp, rctx)

		// logic for the type context:
		// - if both are types, type


@@ 352,7 376,7 @@ func (t *typePass) Visit(n ast.Node) ast.Visitor {
		case lctx == Mutable && rctx == Mutable:
			ctx = Mutable
		}
		t.types.assign(n, rtyp, ctx)
		t.types.assign(n, selTyp, ctx)

	case *ast.BadExpr:
		t.types.assign(n, unresolvedType{}, Invalid)