Sitelet https://github.com/starkware-libs/cairo/commit/0e631f31bd4e1929f355ac6d6b6cb781feb75195
Skip to content

Commit 0e631f3

Browse files
committed
bugfix(semantic): Reject phantom types used as values.
Phantom types have no runtime representation, but a `#[phantom]` type - or a type that transitively contains one, e.g. `Option<Ph>` - could still appear as a value: a function parameter or return type, a constructed value, or any expression. Reaching sierra generation it caused an ICE - the type was lowered to `never`, whose shape did not match the phantom's declared variants/members, so the emitted `match`/`struct_deconstruct` mismatched the libfunc signature. - `TypeId::is_phantom` now reports whether a type is, or transitively contains, a phantom type (through struct members, enum variants, tuple and fixed-size-array elements, or a snapshot), guarded against self-referential types. - Function parameter and return types, and every expression, are now checked; a phantom one is reported as `CannotCreateInstancesOfPhantomTypes` ("Phantom types cannot be instantiated."). - The sierra-generator `never` workaround is removed; phantom types now use their regular representation. This is correct for the only case the front end cannot see - a phantom that becomes concrete only after generic substitution - which now compiles instead of panicking.
1 parent 7f8d4af commit 0e631f3

15 files changed

Lines changed: 592 additions & 292 deletions

File tree

‎crates/cairo-lang-runner/src/profiling_test_data/circuit‎

Lines changed: 11 additions & 11 deletions
Large diffs are not rendered by default.

‎crates/cairo-lang-semantic/src/diagnostic.rs‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,8 +1066,8 @@ impl<'db> DiagnosticEntry<'db> for SemanticDiagnostic<'db> {
10661066
SemanticDiagnosticKind::EmptyPathAfterResolverModifier => {
10671067
"Expected path after modifier.".into()
10681068
}
1069-
SemanticDiagnosticKind::CannotCreateInstancesOfPhantomTypes => {
1070-
"Can not create instances of phantom types.".into()
1069+
SemanticDiagnosticKind::InstancesOfPhantomTypes => {
1070+
"Phantom types cannot be instantiated.".into()
10711071
}
10721072
SemanticDiagnosticKind::NonPhantomTypeContainingPhantomType => {
10731073
"Non-phantom type containing phantom type.".into()
@@ -1280,7 +1280,7 @@ impl<'db> DiagnosticEntry<'db> for SemanticDiagnostic<'db> {
12801280
SemanticDiagnosticKind::GenericsNotSupportedInItem { .. } => error_code!(E2016),
12811281
SemanticDiagnosticKind::UnexpectedGenericArgs => error_code!(E2017),
12821282
SemanticDiagnosticKind::UnknownMember => error_code!(E2018),
1283-
SemanticDiagnosticKind::CannotCreateInstancesOfPhantomTypes => error_code!(E2019),
1283+
SemanticDiagnosticKind::InstancesOfPhantomTypes => error_code!(E2019),
12841284
SemanticDiagnosticKind::NonPhantomTypeContainingPhantomType => error_code!(E2020),
12851285
SemanticDiagnosticKind::MemberSpecifiedMoreThanOnce => error_code!(E2021),
12861286
SemanticDiagnosticKind::StructBaseStructExpressionNotLast => error_code!(E2022),
@@ -1535,7 +1535,7 @@ pub enum SemanticDiagnosticKind<'db> {
15351535
},
15361536
UnexpectedGenericArgs,
15371537
UnknownMember,
1538-
CannotCreateInstancesOfPhantomTypes,
1538+
InstancesOfPhantomTypes,
15391539
NonPhantomTypeContainingPhantomType,
15401540
MemberSpecifiedMoreThanOnce,
15411541
StructBaseStructExpressionNotLast,

‎crates/cairo-lang-semantic/src/expr/compute.rs‎

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,9 @@ impl<'ctx, 'mt> ComputationContext<'ctx, 'mt> {
457457
// Adding an error only once per type.
458458
if analyzed_types.insert(expr.ty()) {
459459
add_type_based_diagnostics(self.db, self.diagnostics, expr.ty(), &*expr);
460+
if expr.ty().is_phantom(self.db) {
461+
self.diagnostics.report(&*expr, InstancesOfPhantomTypes);
462+
}
460463
}
461464
}
462465
for (_id, pattern) in &mut self.arenas.patterns {
@@ -1603,7 +1606,7 @@ fn compute_expr_function_call_semantic<'db>(
16031606
let concrete_enum_type =
16041607
TypeLongId::Concrete(ConcreteTypeId::Enum(variant.concrete_enum_id)).intern(db);
16051608
if concrete_enum_type.is_phantom(db) {
1606-
ctx.diagnostics.report(syntax.stable_ptr(db), CannotCreateInstancesOfPhantomTypes);
1609+
ctx.diagnostics.report(syntax.stable_ptr(db), InstancesOfPhantomTypes);
16071610
}
16081611

16091612
let named_args: Vec<_> = args_syntax
@@ -2458,10 +2461,14 @@ fn compute_expr_closure_semantic<'db>(
24582461
syntax.stable_ptr(db).untyped(),
24592462
);
24602463
}
2461-
2462-
params.iter().filter(|param| param.mutability == Mutability::Reference).for_each(|param| {
2463-
new_ctx.diagnostics.report(param.stable_ptr(ctx.db), RefClosureParam);
2464-
});
2464+
for param in &params {
2465+
if param.mutability == Mutability::Reference {
2466+
new_ctx.diagnostics.report(param.stable_ptr(ctx.db), RefClosureParam);
2467+
}
2468+
if param.ty.is_phantom(ctx.db) {
2469+
new_ctx.diagnostics.report(param.stable_ptr(ctx.db), InstancesOfPhantomTypes);
2470+
}
2471+
}
24652472

24662473
new_ctx.variable_tracker.extend_from_environment(&new_ctx.environment);
24672474

@@ -2477,6 +2484,11 @@ fn compute_expr_closure_semantic<'db>(
24772484
new_ctx.resolver.inference().new_type_var(Some(missing.stable_ptr(db).untyped()))
24782485
}
24792486
};
2487+
if let OptionReturnTypeClause::ReturnTypeClause(ty_syntax) = syntax.ret_ty(db)
2488+
&& return_type.is_phantom(ctx.db)
2489+
{
2490+
new_ctx.diagnostics.report(ty_syntax.ty(db).stable_ptr(db), InstancesOfPhantomTypes);
2491+
}
24802492

24812493
let old_inner_ctx = new_ctx
24822494
.inner_ctx
@@ -3559,7 +3571,7 @@ fn struct_ctor_expr<'db>(
35593571
.ok_or_else(|| ctx.diagnostics.report(path.stable_ptr(db), NotAStruct))?;
35603572

35613573
if ty.is_phantom(db) {
3562-
ctx.diagnostics.report(ctor_syntax.stable_ptr(db), CannotCreateInstancesOfPhantomTypes);
3574+
ctx.diagnostics.report(ctor_syntax.stable_ptr(db), InstancesOfPhantomTypes);
35633575
}
35643576

35653577
let members = db.concrete_struct_members(concrete_struct_id)?;

‎crates/cairo-lang-semantic/src/expr/test_data/attributes‎

Lines changed: 190 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ enum MyEnum {
2222
}
2323

2424
//! > expected_diagnostics
25-
error[E2019]: Can not create instances of phantom types.
25+
error[E2019]: Phantom types cannot be instantiated.
2626
--> lib.cairo:9:5
2727
MyStruct {};
2828
^^^^^^^^^^^
2929

30-
error[E2019]: Can not create instances of phantom types.
30+
error[E2019]: Phantom types cannot be instantiated.
3131
--> lib.cairo:10:5
3232
MyEnum::a(3);
3333
^^^^^^^^^^^^
@@ -50,3 +50,191 @@ foo
5050
fn f() {}
5151

5252
//! > expected_diagnostics
53+
54+
//! > ==========================================================================
55+
56+
//! > Phantom enum inside a generic container in a function signature.
57+
58+
//! > test_runner_name
59+
test_function_diagnostics(expect_diagnostics: true)
60+
61+
//! > function_code
62+
fn foo(x: Option<Ph>) -> Option<Ph> {
63+
x
64+
}
65+
66+
//! > function_name
67+
foo
68+
69+
//! > module_code
70+
#[phantom]
71+
enum Ph {
72+
A: felt252,
73+
B: (felt252, felt252),
74+
}
75+
76+
//! > expected_diagnostics
77+
error[E2019]: Phantom types cannot be instantiated.
78+
--> lib.cairo:6:8
79+
fn foo(x: Option<Ph>) -> Option<Ph> {
80+
^^^^^^^^^^^^^
81+
82+
error[E2019]: Phantom types cannot be instantiated.
83+
--> lib.cairo:6:26
84+
fn foo(x: Option<Ph>) -> Option<Ph> {
85+
^^^^^^^^^^
86+
87+
error[E2019]: Phantom types cannot be instantiated.
88+
--> lib.cairo:7:5
89+
x
90+
^
91+
92+
//! > ==========================================================================
93+
94+
//! > Phantom struct inside a generic container in a function signature.
95+
96+
//! > test_runner_name
97+
test_function_diagnostics(expect_diagnostics: true)
98+
99+
//! > function_code
100+
fn foo(x: Option<Ph>) -> Option<Ph> {
101+
x
102+
}
103+
104+
//! > function_name
105+
foo
106+
107+
//! > module_code
108+
#[phantom]
109+
struct Ph {
110+
a: felt252,
111+
b: felt252,
112+
}
113+
114+
//! > expected_diagnostics
115+
error[E2019]: Phantom types cannot be instantiated.
116+
--> lib.cairo:6:8
117+
fn foo(x: Option<Ph>) -> Option<Ph> {
118+
^^^^^^^^^^^^^
119+
120+
error[E2019]: Phantom types cannot be instantiated.
121+
--> lib.cairo:6:26
122+
fn foo(x: Option<Ph>) -> Option<Ph> {
123+
^^^^^^^^^^
124+
125+
error[E2019]: Phantom types cannot be instantiated.
126+
--> lib.cairo:7:5
127+
x
128+
^
129+
130+
//! > ==========================================================================
131+
132+
//! > Phantom type reached through a non-phantom generic struct's member in a signature.
133+
134+
//! > test_runner_name
135+
test_function_diagnostics(expect_diagnostics: true)
136+
137+
//! > function_code
138+
fn foo(x: Wrapper<Ph>) -> Wrapper<Ph> {
139+
x
140+
}
141+
142+
//! > function_name
143+
foo
144+
145+
//! > module_code
146+
#[phantom]
147+
struct Ph {
148+
a: felt252,
149+
}
150+
151+
struct Wrapper<T> {
152+
value: T,
153+
}
154+
155+
//! > expected_diagnostics
156+
error[E2019]: Phantom types cannot be instantiated.
157+
--> lib.cairo:9:8
158+
fn foo(x: Wrapper<Ph>) -> Wrapper<Ph> {
159+
^^^^^^^^^^^^^^
160+
161+
error[E2019]: Phantom types cannot be instantiated.
162+
--> lib.cairo:9:27
163+
fn foo(x: Wrapper<Ph>) -> Wrapper<Ph> {
164+
^^^^^^^^^^^
165+
166+
error[E2019]: Phantom types cannot be instantiated.
167+
--> lib.cairo:10:5
168+
x
169+
^
170+
171+
//! > ==========================================================================
172+
173+
//! > Phantom type behind a snapshot in a function signature.
174+
175+
//! > test_runner_name
176+
test_function_diagnostics(expect_diagnostics: true)
177+
178+
//! > function_code
179+
fn foo(x: @Ph) {}
180+
181+
//! > function_name
182+
foo
183+
184+
//! > module_code
185+
#[phantom]
186+
enum Ph {}
187+
188+
//! > expected_diagnostics
189+
error[E2019]: Phantom types cannot be instantiated.
190+
--> lib.cairo:3:8
191+
fn foo(x: @Ph) {}
192+
^^^^^^
193+
194+
//! > ==========================================================================
195+
196+
//! > Phantom type as an implicit parameter.
197+
198+
//! > test_runner_name
199+
test_function_diagnostics(expect_diagnostics: true)
200+
201+
//! > function_code
202+
fn foo() implicits(Ph) {}
203+
204+
//! > function_name
205+
foo
206+
207+
//! > module_code
208+
#[phantom]
209+
enum Ph {}
210+
211+
//! > expected_diagnostics
212+
error[E2019]: Phantom types cannot be instantiated.
213+
--> lib.cairo:3:20
214+
fn foo() implicits(Ph) {}
215+
^^
216+
217+
//! > ==========================================================================
218+
219+
//! > Phantom type in a closure parameter.
220+
221+
//! > test_runner_name
222+
test_function_diagnostics(expect_diagnostics: true)
223+
224+
//! > function_code
225+
fn foo() {
226+
let _f = |_x: Option<Ph>| {};
227+
}
228+
229+
//! > function_name
230+
foo
231+
232+
//! > module_code
233+
#[phantom]
234+
enum Ph {}
235+
236+
//! > expected_diagnostics
237+
error[E2019]: Phantom types cannot be instantiated.
238+
--> lib.cairo:4:15
239+
let _f = |_x: Option<Ph>| {};
240+
^^^^^^^^^^^^^^

‎crates/cairo-lang-semantic/src/items/functions.rs‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,23 @@ impl<'db> Signature<'db> {
751751
);
752752
let return_type =
753753
function_signature_return_type(diagnostics, db, resolver, &signature_syntax);
754+
// Phantom types (and types transitively containing one through a struct member, enum
755+
// variant or tuple/array element, e.g. `Option<Ph>`) have no runtime representation and
756+
// cannot be used as values, so they may not appear in a function signature.
757+
for param in &params {
758+
if param.ty.is_phantom(db) {
759+
diagnostics
760+
.report(param.stable_ptr(db), SemanticDiagnosticKind::InstancesOfPhantomTypes);
761+
}
762+
}
763+
if let ast::OptionReturnTypeClause::ReturnTypeClause(syntax) = signature_syntax.ret_ty(db)
764+
&& return_type.is_phantom(db)
765+
{
766+
diagnostics.report(
767+
syntax.ty(db).stable_ptr(db),
768+
SemanticDiagnosticKind::InstancesOfPhantomTypes,
769+
);
770+
}
754771
let implicits =
755772
function_signature_implicit_parameters(diagnostics, db, resolver, &signature_syntax);
756773
let panicable = match signature_syntax.optional_no_panic(db) {
@@ -796,7 +813,16 @@ pub fn function_signature_implicit_parameters<'db>(
796813
};
797814
let ast_implicits = implicits.elements(db);
798815
ast_implicits
799-
.map(|implicit| resolve_type(db, diagnostics, resolver, &ast::Expr::Path(implicit)))
816+
.map(|implicit| {
817+
let stable_ptr = implicit.stable_ptr(db);
818+
let ty = resolve_type(db, diagnostics, resolver, &ast::Expr::Path(implicit));
819+
// An implicit of a phantom type would be a hidden runtime parameter with no
820+
// representation, just like an explicit phantom parameter.
821+
if ty.is_phantom(db) {
822+
diagnostics.report(stable_ptr, SemanticDiagnosticKind::InstancesOfPhantomTypes);
823+
}
824+
ty
825+
})
800826
.collect()
801827
}
802828

0 commit comments

Comments
 (0)