Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,21 @@ pub fn create_node_for_patterns<'db>(
create_node_for_enum(params, input_var, concrete_enum_id, wrapping_info)
}
TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) => {
// Check if any non-any pattern is a FixedSizeArray (i.e. Span destructure).
// Span destructuring in match/if-let is not yet supported in lowering.
let has_fixed_size_array_pattern = patterns
.iter()
.flatten()
.any(|p| matches!(p, semantic::Pattern::FixedSizeArray(..)));
if has_fixed_size_array_pattern {
return graph.report_with_missing_node(
first_non_any_pattern.stable_ptr(),
LoweringDiagnosticKind::MatchError(MatchError {
kind: graph.kind(),
error: MatchDiagnostic::UnsupportedMatchedType(long_ty.format(ctx.db)),
}),
);
}
create_node_for_struct(params, input_var, concrete_struct_id, wrapping_info)
}
TypeLongId::Tuple(types) => create_node_for_tuple(params, input_var, &types, wrapping_info),
Expand Down
2 changes: 1 addition & 1 deletion crates/cairo-lang-semantic/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,7 @@ impl<'db> DiagnosticEntry<'db> for SemanticDiagnostic<'db> {
SemanticDiagnosticKind::UnexpectedFixedSizeArrayPattern(ty) => {
format!(
"Unexpected type for fixed size array pattern. \"{}\" is not a fixed size \
array.",
array or a span.",
ty.format(db),
)
}
Expand Down
99 changes: 75 additions & 24 deletions crates/cairo-lang-semantic/src/expr/compute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3082,6 +3082,50 @@ fn maybe_compute_pattern_semantic<'db>(
Ok(pattern)
}

/// Tries to get the long type of the matched expression for a tuple-like pattern.
/// Returns `None` if the pattern and type are incompatible.
fn try_get_match_expr_long_ty<'db>(
ctx: &mut ComputationContext<'db, '_>,
pattern_syntax: &ast::Pattern<'db>,
long_ty: &TypeLongId<'db>,
ty: TypeId<'db>,
num_patterns: usize,
) -> Option<TypeLongId<'db>> {
let db = ctx.db;
match (pattern_syntax, long_ty) {
(_, TypeLongId::Var(_) | TypeLongId::Missing(_))
| (ast::Pattern::Tuple(_), TypeLongId::Tuple(_))
| (ast::Pattern::FixedSizeArray(_), TypeLongId::FixedSizeArray { .. }) => {
Some(long_ty.clone())
}
(
ast::Pattern::FixedSizeArray(_),
TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)),
) => {
let [GenericArgumentId::Type(inner_ty)] = concrete_struct_id.long(db).generic_args[..]
else {
return None;
};
let span_ty = try_get_core_ty_by_name(
db,
SmolStrId::from(db, "Span"),
vec![GenericArgumentId::Type(inner_ty)],
)
.ok()?;
if let Err(err_set) = ctx.resolver.inference().conform_ty(ty, span_ty) {
// The caller is going to report a more accurate error message.
ctx.resolver.inference().consume_error_without_reporting(err_set);
return None;
}
Comment thread
cursor[bot] marked this conversation as resolved.
Some(TypeLongId::FixedSizeArray {
type_id: wrap_in_snapshots(db, inner_ty, 1),
size: ConstValue::Int(num_patterns.into(), get_usize_ty(db)).intern(db),
})
}
_ => None,
}
}

/// Computes the semantic model of a pattern of a tuple or a fixed size array. Assumes that the
/// pattern is one of these types.
fn compute_tuple_like_pattern_semantic<'db>(
Expand All @@ -3093,32 +3137,35 @@ fn compute_tuple_like_pattern_semantic<'db>(
wrong_number_of_elements: fn(usize, usize) -> SemanticDiagnosticKind<'db>,
) -> Pattern<'db> {
let db = ctx.db;
let (wrapping_info, mut long_ty) =
let (wrapping_info, long_ty) =
match extract_tuple_like_from_pattern_and_validate(ctx, pattern_syntax, ty) {
Ok(value) => value,
Err(diag_added) => (
PatternWrappingInfo { n_outer_snapshots: 0, n_boxed_inner_snapshots: None },
TypeLongId::Missing(diag_added),
),
};
// Assert that the pattern is of the same type as the expr.
match (pattern_syntax, &long_ty) {
(_, TypeLongId::Var(_) | TypeLongId::Missing(_))
| (ast::Pattern::Tuple(_), TypeLongId::Tuple(_))
| (ast::Pattern::FixedSizeArray(_), TypeLongId::FixedSizeArray { .. }) => {}
_ => {
long_ty = TypeLongId::Missing(
ctx.diagnostics.report(pattern_syntax.stable_ptr(db), unexpected_pattern(ty)),
);
}
};
let patterns_syntax = match pattern_syntax {
let patterns_syntax_elements = match pattern_syntax {
ast::Pattern::Tuple(pattern_tuple) => pattern_tuple.patterns(db).elements_vec(db),
ast::Pattern::FixedSizeArray(pattern_fixed_size_array) => {
pattern_fixed_size_array.patterns(db).elements_vec(db)
}
_ => unreachable!(),
};

// Assert that the pattern is of the same type as the expr.
let long_ty = try_get_match_expr_long_ty(
ctx,
pattern_syntax,
&long_ty,
ty,
patterns_syntax_elements.len(),
)
.unwrap_or_else(|| {
TypeLongId::Missing(
ctx.diagnostics.report(pattern_syntax.stable_ptr(db), unexpected_pattern(ty)),
)
});
let mut inner_tys = match long_ty {
TypeLongId::Tuple(inner_tys) => inner_tys,
TypeLongId::FixedSizeArray { type_id: inner_ty, size } => {
Expand All @@ -3127,35 +3174,39 @@ fn compute_tuple_like_pattern_semantic<'db>(
} else {
let inference = &mut ctx.resolver.inference();
let expected_size =
ConstValue::Int(patterns_syntax.len().into(), get_usize_ty(db)).intern(db);
ConstValue::Int(patterns_syntax_elements.len().into(), get_usize_ty(db))
.intern(db);
if let Err(err) = inference.conform_const(size, expected_size) {
let _ = inference.report_on_pending_error(
err,
ctx.diagnostics,
pattern_syntax.stable_ptr(db).untyped(),
);
}
patterns_syntax.len()
patterns_syntax_elements.len()
};

[inner_ty].repeat(size)
}
TypeLongId::Var(_) => {
let inference = &mut ctx.resolver.inference();
let (inner_tys, tuple_like_ty) = if matches!(pattern_syntax, ast::Pattern::Tuple(_)) {
let inner_tys: Vec<_> = patterns_syntax
let inner_tys: Vec<_> = patterns_syntax_elements
.iter()
.map(|e| inference.new_type_var(Some(e.stable_ptr(db).untyped())))
.collect();
(inner_tys.clone(), TypeLongId::Tuple(inner_tys))
} else {
let var = inference.new_type_var(Some(pattern_syntax.stable_ptr(db).untyped()));
(
vec![var; patterns_syntax.len()],
vec![var; patterns_syntax_elements.len()],
TypeLongId::FixedSizeArray {
type_id: var,
size: ConstValue::Int(patterns_syntax.len().into(), get_usize_ty(db))
.intern(db),
size: ConstValue::Int(
patterns_syntax_elements.len().into(),
get_usize_ty(db),
)
.intern(db),
},
)
};
Expand All @@ -3167,21 +3218,21 @@ fn compute_tuple_like_pattern_semantic<'db>(
}
TypeLongId::Missing(diag_added) => {
let missing = TypeId::missing(db, diag_added);
vec![missing; patterns_syntax.len()]
vec![missing; patterns_syntax_elements.len()]
}
_ => unreachable!(),
};
let size = inner_tys.len();
if size != patterns_syntax.len() {
if size != patterns_syntax_elements.len() {
let diag_added = ctx.diagnostics.report(
pattern_syntax.stable_ptr(db),
wrong_number_of_elements(size, patterns_syntax.len()),
wrong_number_of_elements(size, patterns_syntax_elements.len()),
);
let missing = TypeId::missing(db, diag_added);

inner_tys = vec![missing; patterns_syntax.len()];
inner_tys = vec![missing; patterns_syntax_elements.len()];
}
let subpatterns = zip_eq(patterns_syntax, inner_tys)
let subpatterns = zip_eq(patterns_syntax_elements, inner_tys)
.map(|(pattern_ast, ty)| {
let ty = wrapping_info.wrap(db, ty);
compute_pattern_semantic(ctx, &pattern_ast, ty, or_pattern_variables_map).id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ error[E0006]: Function not found.
let (a, b, c) = undefined();
^^^^^^^^^

error[E2106]: Unexpected type for fixed size array pattern. "(<missing>, <missing>, <missing>)" is not a fixed size array.
error[E2106]: Unexpected type for fixed size array pattern. "(<missing>, <missing>, <missing>)" is not a fixed size array or a span.
--> lib.cairo:3:9
let [d, e, f] = (a, b, c);
^^^^^^^^^
Expand Down
128 changes: 127 additions & 1 deletion crates/cairo-lang-semantic/src/expr/test_data/pattern
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ fn foo(s: (felt252, felt252, felt252)) {
foo

//! > expected_diagnostics
error[E2106]: Unexpected type for fixed size array pattern. "(core::felt252, core::felt252, core::felt252)" is not a fixed size array.
error[E2106]: Unexpected type for fixed size array pattern. "(core::felt252, core::felt252, core::felt252)" is not a fixed size array or a span.
--> lib.cairo:2:9
let [_a, _b, _c] = s;
^^^^^^^^^^^^
Expand Down Expand Up @@ -313,3 +313,129 @@ error[E2049]: Redefinition of variable name "_b" in pattern.
--> lib.cairo:7:19
((_b, _), _b) => (),
^^

//! > ==========================================================================

//! > Test span into fixed size array pattern.

//! > test_runner_name
test_function_diagnostics(expect_diagnostics: false)

//! > function_code
fn foo(s: Span<u32>) -> u32 {
let [a, b] = s else {
return 0;
};
*a + *b
}

//! > function_name
foo

//! > module_code

//! > expected_diagnostics

//! > ==========================================================================

//! > Test type mismatch in span pattern arm surfaces as confusing ToSpanTrait error.

//! > test_runner_name
test_function_diagnostics(expect_diagnostics: true)

//! > function_code
fn foo() {
let a: Array<u8> = array![1, 2, 3];
let c: u16 = 0;
match a.span() {
[x, y, z] => c + *x + *y + *z,
_ => 0,
};
}

//! > function_name
foo

//! > module_code

//! > expected_diagnostics
error[E2311]: Trait has no implementation in context: core::array::ToSpanTrait::<core::array::Array::<core::integer::u8>, core::integer::u16>.
--> lib.cairo:4:13
match a.span() {
^^^^

//! > ==========================================================================

//! > Test array pattern on non-Span generic struct.

//! > test_runner_name
test_function_diagnostics(expect_diagnostics: true)

//! > function_code
fn foo(s: MyStruct<u8>) {
let [_x, _y] = s;
}

//! > function_name
foo

//! > module_code
struct MyStruct<T> {
value: T,
}

//! > expected_diagnostics
error[E2106]: Unexpected type for fixed size array pattern. "test::MyStruct::<core::integer::u8>" is not a fixed size array or a span.
--> lib.cairo:5:9
let [_x, _y] = s;
^^^^^^^^

//! > ==========================================================================

//! > Test array pattern on Array instead of Span.

//! > test_runner_name
test_function_diagnostics(expect_diagnostics: true)

//! > function_code
fn foo() {
let a: Array<u8> = array![1, 2, 3];
match a {
[_x, _y, _z] => {},
_ => {},
};
}

//! > function_name
foo

//! > module_code

//! > expected_diagnostics
error[E2106]: Unexpected type for fixed size array pattern. "core::array::Array::<core::integer::u8>" is not a fixed size array or a span.
--> lib.cairo:4:9
[_x, _y, _z] => {},
^^^^^^^^^^^^

//! > ==========================================================================

//! > Test array pattern on fixed size array value.

//! > test_runner_name
test_function_diagnostics(expect_diagnostics: false)

//! > function_code
fn foo() {
let a: [u8; 3] = [1, 2, 3];
match a {
[_x, _y, _z] => {},
_ => {},
};
}

//! > function_name
foo

//! > module_code

//! > expected_diagnostics
Loading