Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pattern support #2082

Open
6 of 31 tasks
goar5670 opened this issue Apr 1, 2023 · 5 comments
Open
6 of 31 tasks

Pattern support #2082

goar5670 opened this issue Apr 1, 2023 · 5 comments

Comments

@goar5670
Copy link
Contributor

goar5670 commented Apr 1, 2023

Exhaustive list of what needs to be supported for patterns.

match:

  • LiteralPattern 1, 'a'
  • IdentifierPattern e
  • WildcardPattern _
  • PathPattern
  • TuplePattern (done for multiple item type, remaining range item type (1, 2, .., 10, 11))
  • StructPattern
  • TupleStructPattern
  • SlicePattern [a, b, _]
  • RangePattern 1..=5
  • ReferencePattern
  • RestPattern ..
  • AltPattern Foo::A | Foo::B

Match to-dos (done items not listed)

if let and while let are not yet implemented but they'll follow the same logic as match.
Once the refactor in #2004 is done, we'll be able to add support for if let and while let.

function params and let statements (refutable patterns omitted):

  • IdentifierPattern e
  • WildcardPattern _
  • PathPattern
  • TuplePattern
  • StructPattern (TUPLE_PAT and IDENT_PAT item types remaining)
  • TupleStructPattern (range items type remaining)
  • SlicePattern [a, b, _]
  • ReferencePattern
  • RestPattern ..
  • AltPattern Foo::A | Foo::B

Params and Vars to-dos:

  • Irrefutability checks.
  • what else?

patterns in for loops cannot be added until #869 is finished.

additional to-dos:

  • identifier pattern binding
  • ref mut identifier pattern handling
  • exhaustiveness/usefulness checks

Anything I forgot?

@goar5670
Copy link
Contributor Author

goar5670 commented Apr 2, 2023

basic identifiers in match expressions:

match f { // f: i32
	a => {}
}

the gimple output here should be something like:

if (1 != 0) {
	i32 a = f;
}

the idea here is that an identifier matches against any expression (hence, the 1 != 0 part)

@goar5670
Copy link
Contributor Author

goar5670 commented Apr 2, 2023

Extending for TuplePattern:

match f { // f: (i32, i32, i32, i32)
	(1, _, 2, a) => {}
	(3, .., a) => {}
	(a, .., b) => {}
}

gimple:

if (f.0 == 1 && 1 != 0 && f.2 == 2 && 1 != 0) {
	i32 a = f.3
}

else {
	if (f.0 == 1 && 1 != 0) {
		i32 a = f.1;
	}
	else {
		if (1 != 0 && 1 != 0)
		{
			i32 a = f.0;
			i32 b = f.3;
		}
	}
}

For StructPattern, TupleStructPattern, and SlicePattern the gimple output would follow the same idea.

@goar5670
Copy link
Contributor Author

goar5670 commented Apr 4, 2023

for ReferencePattern, we'll need to dereference the scrutinee and check against the inner pattern of the arm:

match f { // f: &(i32, i32)
	&(1, a) => {}
	&(b, 5) => {}
	&c => {}
}

gimple:

_1 = *x;
if (_1.0 == 1 && 1 != 0) {
	i32 a = _1.0
}

else {
	if (1 != 0 && _1.1 == 5) {
		i32 b = _1.1;
	}
	else {
		if (1 != 0) {
			struct
			{
				i32 __0;
				i32 __1;
			} c;
			c.__0 = _1.0;
			c.__1 = _1.1;
		}
	}
}

For RestPattern in slices (e.g. [head, ..]), we can return a true boolean expression like we do for normal identifiers.

For AltPattern compilation in general we'll have to bind identifiers at runtime, other than that, matching will go largely the same as other patterns (with || instead of && in the appropriate places):

match f { // f: (i32, i32, i32)
	(1, a, _) | (2, _, a) => {
		// do something with a here
	}
	_ => {}
}

gimple:

if ((f.0 == 1 && 1 != 0 && 1 != 0) ||
	(f.0 == 2 && 1 != 0 && 1 != 0)) {
	i32 a;

	if (f.0 == 1 && 1 != 0 && 1 != 0) // matched on first alternative
	{
		a = f.1;
	}
	else {
		if (f.0 == 2 && 1 != 0 && 1 != 0) // matched on second alternative {
			a = f.2;
		}
	}

	// do something with a here
}

@goar5670
Copy link
Contributor Author

goar5670 commented Apr 4, 2023

a compile test for the unsupported patterns in function parameters.

enum E {
    A(i32, i32, i32, i32),
    B { x: i32, y: i32 },
}
fn foo(
    (a, .., b): (i32, i32, i32, i32, i32),
    (E::A {0: c, 1: d, 2: 1, 3: 1} | E::A(.., c, d) | E::B { x: c, y: d }): E,
    &([1, .., e, f] | [e, f, ..]): &[i32; 6],
) {
    // use a, b, c, d, e, f here
}

the same test can be used with let statement.

A corner case which should compile successfully:

fn foo(0..: u8) {}

@narpfel
Copy link

narpfel commented Aug 16, 2023

IdentifierPatterns have a special case that should probably be included in this list: When the identifier is already bound as a const or a dataless enum variant, using it in pattern matching doesn’t introduce a new name but rather matches angainst the const’s value, e. g.:

enum AnEnum {
    VariantA,
    VariantB { a: i32 },
}

use AnEnum::VariantA;
const X: u32 = 42;

fn main() {
    match AnEnum::VariantA {
        VariantA => (),
    }
    let VariantA = AnEnum::VariantA;

    match 27 {
        X => (),
    }
    let X = 27;
}

(Godbolt link)

All of these should fail to compile.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants