Forbid const generics on abis (#7483)
Some checks failed
CI / forc-fmt-check-sway-lib-std (push) Has been cancelled
CI / forc-fmt-check-sway-examples (push) Has been cancelled
CI / cargo-clippy (push) Has been cancelled
CI / forc-unit-tests (push) Has been cancelled
CI / build-sway-lib-std (push) Has been cancelled
CI / build-sway-examples (push) Has been cancelled
CI / build-reference-examples (push) Has been cancelled
Codspeed Benchmarks / benchmarks (push) Has been cancelled
CI / check-dependency-version-formats (push) Has been cancelled
CI / check-forc-manifest-version (push) Has been cancelled
CI / get-fuel-core-version (push) Has been cancelled
CI / forc-fmt-check-panic (push) Has been cancelled
CI / check-sdk-harness-test-suite-compatibility (push) Has been cancelled
CI / build-mdbook (push) Has been cancelled
CI / build-forc-doc-sway-lib-std (push) Has been cancelled
CI / build-forc-test-project (push) Has been cancelled
CI / cargo-build-workspace (push) Has been cancelled
CI / cargo-toml-fmt-check (push) Has been cancelled
CI / cargo-fmt-check (push) Has been cancelled
CI / cargo-test-lib-std (push) Has been cancelled
CI / forc-run-benchmarks (push) Has been cancelled
CI / forc-pkg-fuels-deps-check (push) Has been cancelled
CI / cargo-unused-deps-check (push) Has been cancelled
CI / pre-publish-check (push) Has been cancelled
github pages / deploy (push) Has been cancelled
CI / verifications-complete (push) Has been cancelled
CI / cargo-run-e2e-test (push) Has been cancelled
CI / cargo-run-e2e-test-release (push) Has been cancelled
CI / Build and test various forc tools (push) Has been cancelled
CI / notify-slack-on-failure (push) Has been cancelled
CI / publish (push) Has been cancelled
CI / publish-sway-lib-std (push) Has been cancelled
CI / Build and upload forc binaries to release (push) Has been cancelled

## Description

This PR forbids "const generics" on `abi`s. The reason is that to
correctly support it, we will need to introduce changes to the ABI json.

This will be reserved to the future.

## Checklist

- [ ] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [ ] If my change requires substantial documentation changes, I have
[requested support from the DevRel
team](https://github.com/FuelLabs/devrel-requests/issues/new/choose)
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [ ] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> <sup>[Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) is
generating a summary for commit
1bf3210a09. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This commit is contained in:
Daniel Frederico Lins Leite 2025-11-12 14:16:20 -03:00 committed by GitHub
parent 886becbca0
commit fb10b2fcb1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 329 additions and 74 deletions

View file

@ -1,5 +1,7 @@
use super::{TyDeclParsedType, TyTraitInterfaceItem, TyTraitItem};
use crate::{
ast_elements::type_parameter::ConstGenericExpr,
decl_engine::DeclEngineGet as _,
engine_threading::*,
language::parsed::{self, AbiDeclaration},
transform,
@ -7,6 +9,10 @@ use crate::{
};
use serde::{Deserialize, Serialize};
use std::hash::{Hash, Hasher};
use sway_error::{
error::CompileError,
handler::{ErrorEmitted, Handler},
};
use sway_types::{Ident, Named, Span, Spanned};
/// A [TyAbiDecl] contains the type-checked version of the parse tree's
@ -23,6 +29,133 @@ pub struct TyAbiDecl {
pub attributes: transform::Attributes,
}
fn has_const_generics(type_id: TypeId, engines: &Engines) -> bool {
let types = type_id.extract_any_including_self(engines, &|_| true, vec![], 0);
for (t, _) in types {
let t = engines.te().get(t);
match &*t {
TypeInfo::StringArray(length) => match length.expr() {
ConstGenericExpr::Literal { .. } => {}
ConstGenericExpr::AmbiguousVariableExpression { .. } => return true,
},
TypeInfo::Enum(decl_id) => {
let decl = engines.de().get(decl_id);
let any_const_generics = decl
.generic_parameters
.iter()
.any(|x| x.as_const_parameter().is_some());
if any_const_generics {
return true;
}
}
TypeInfo::Struct(decl_id) => {
let decl = engines.de().get(decl_id);
let any_const_generics = decl
.generic_parameters
.iter()
.any(|x| x.as_const_parameter().is_some());
if any_const_generics {
return true;
}
for field in decl.fields.iter() {
let any_const_generics =
has_const_generics(field.type_argument.type_id, engines);
if any_const_generics {
return true;
}
}
}
TypeInfo::Tuple(items) => {
for item in items {
let any_const_generics = has_const_generics(item.type_id, engines);
if any_const_generics {
return true;
}
}
}
TypeInfo::Array(item, length) => {
let any_const_generics = has_const_generics(item.type_id, engines);
if any_const_generics {
return true;
}
match length.expr() {
ConstGenericExpr::Literal { .. } => {}
ConstGenericExpr::AmbiguousVariableExpression { .. } => return true,
}
}
_ => {}
}
}
false
}
impl TyAbiDecl {
pub(crate) fn forbid_const_generics(
&self,
handler: &Handler,
engines: &Engines,
) -> Result<(), ErrorEmitted> {
for item in self.interface_surface.iter() {
match item {
TyTraitInterfaceItem::TraitFn(decl_ref) => {
let decl = engines.de().get(decl_ref.id());
if has_const_generics(decl.return_type.type_id, engines) {
let err = handler.emit_err(CompileError::ConstGenericNotSupportedHere {
span: decl.return_type.span(),
});
return Err(err);
}
for arg in decl.parameters.iter() {
if has_const_generics(arg.type_argument.type_id, engines) {
let err =
handler.emit_err(CompileError::ConstGenericNotSupportedHere {
span: arg.type_argument.span.clone(),
});
return Err(err);
}
}
}
TyTraitInterfaceItem::Constant(_) => {}
TyTraitInterfaceItem::Type(_) => {}
}
}
for item in self.items.iter() {
match item {
TyTraitItem::Fn(decl_ref) => {
let decl = engines.de().get(decl_ref.id());
if has_const_generics(decl.return_type.type_id, engines) {
let err = handler.emit_err(CompileError::ConstGenericNotSupportedHere {
span: decl.return_type.span(),
});
return Err(err);
}
for arg in decl.parameters.iter() {
if has_const_generics(arg.type_argument.type_id, engines) {
let err =
handler.emit_err(CompileError::ConstGenericNotSupportedHere {
span: arg.type_argument.span.clone(),
});
return Err(err);
}
}
}
TyTraitItem::Constant(_) => {}
TyTraitItem::Type(_) => {}
}
}
Ok(())
}
}
impl TyDeclParsedType for TyAbiDecl {
type ParsedType = AbiDeclaration;
}

View file

@ -233,6 +233,9 @@ impl ty::TyAbiDecl {
span,
attributes,
};
abi_decl.forbid_const_generics(handler, engines)?;
Ok(abi_decl)
})
}

View file

@ -198,18 +198,18 @@ impl TypeSubstMap {
(TypeInfo::Enum(decl_ref_params), TypeInfo::Enum(decl_ref_args)) => {
let decl_params = decl_engine.get_enum(decl_ref_params);
let decl_args = decl_engine.get_enum(decl_ref_args);
let type_parameters = decl_params.generic_parameters.iter().map(|x| {
let x = x
.as_type_parameter()
.expect("will only work with type parameters");
x.type_id
});
let type_arguments = decl_args.generic_parameters.iter().map(|x| {
let x = x
.as_type_parameter()
.expect("will only work with type parameters");
x.type_id
});
let type_parameters = decl_params
.generic_parameters
.iter()
.filter_map(|x| x.as_type_parameter())
.map(|x| x.type_id);
let type_arguments = decl_args
.generic_parameters
.iter()
.filter_map(|x| x.as_type_parameter())
.map(|x| x.type_id);
TypeSubstMap::from_superset_and_subset_helper(
engines,
type_parameters,
@ -220,18 +220,17 @@ impl TypeSubstMap {
let decl_params = decl_engine.get_struct(decl_ref_params);
let decl_args = decl_engine.get_struct(decl_ref_args);
let type_parameters = decl_params.generic_parameters.iter().map(|x| {
let x = x
.as_type_parameter()
.expect("only works with type parameters");
x.type_id
});
let type_arguments = decl_args.generic_parameters.iter().map(|x| {
let x = x
.as_type_parameter()
.expect("only works with type parameters");
x.type_id
});
let type_parameters = decl_params
.generic_parameters
.iter()
.filter_map(|x| x.as_type_parameter())
.map(|x| x.type_id);
let type_arguments = decl_args
.generic_parameters
.iter()
.filter_map(|x| x.as_type_parameter())
.map(|x| x.type_id);
TypeSubstMap::from_superset_and_subset_helper(
engines,
type_parameters,

View file

@ -1,8 +1,8 @@
[[package]]
name = "const_generics"
source = "member"
dependencies = ["std"]
[[package]]
name = "std"
source = "path+from-root-04B19932ACF7269D"
source = "path+from-root-73072EFA61E741F9"
[[package]]
name = "unsupported_const_generics"
source = "member"
dependencies = ["std"]

View file

@ -2,7 +2,7 @@
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "const_generics"
name = "unsupported_const_generics"
[dependencies]
std = { path = "../../../../../../sway-lib-std" }

View file

@ -42,6 +42,30 @@ impl<const N: u64> A for CrazyEnum<N> {
}
}
abi NoConstGenericsOnArgs {
fn f(a: CrazyStruct<1>);
}
abi NoConstGenericsOnReturn {
fn f() -> CrazyEnum<1>;
}
struct StructWithConstGenericInside {
a: CrazyStruct<1>,
}
abi NoConstGenericsIndirectStruct {
fn f() -> StructWithConstGenericInside;
}
enum EnumWithConstGenericInside {
A: CrazyStruct<1>,
}
abi NoConstGenericsIndirectEnum {
fn f() -> EnumWithConstGenericInside;
}
fn main() {
let _: CrazyStruct<UNKNOWN> = CrazyStruct { };
let _: CrazyEnum<UNKNOWN> = CrazyEnum::A;

View file

@ -1,14 +1,26 @@
---
source: test/src/snapshot/mod.rs
---
> forc build --path test/src/e2e_vm_tests/test_programs/should_fail/const_generics --experimental const_generics
> forc build --path test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics --experimental const_generics
exit status: 1
output:
Building test/src/e2e_vm_tests/test_programs/should_fail/const_generics
Building test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics
Compiling library std (sway-lib-std)
Compiling script const_generics (test/src/e2e_vm_tests/test_programs/should_fail/const_generics)
Compiling script unsupported_const_generics (test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics)
warning
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:24:5
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:62:5
|
60 |
61 | enum EnumWithConstGenericInside {
62 | A: CrazyStruct<1>,
| - Enum variant A is never constructed.
63 | }
64 |
|
____
warning
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:24:5
|
22 |
23 | impl<const N: u64> A for CrazyStruct<N> {
@ -22,7 +34,7 @@ warning
____
warning
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:41:5
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:41:5
|
39 |
40 | impl<const N: u64> A for CrazyEnum<N> {
@ -35,8 +47,44 @@ warning
|
____
warning
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:53:8
|
51 | }
52 |
53 | struct StructWithConstGenericInside {
| ---------------------------- This struct is never used.
54 | a: CrazyStruct<1>,
55 | }
|
____
warning
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:54:5
|
52 |
53 | struct StructWithConstGenericInside {
54 | a: CrazyStruct<1>,
| - This struct field is never accessed.
55 | }
56 |
|
____
warning
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:61:6
|
59 | }
60 |
61 | enum EnumWithConstGenericInside {
| -------------------------- This enum is never used.
62 | A: CrazyStruct<1>,
63 | }
|
____
error: Multiple definitions of constant
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:6:63
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:6:63
|
...
6 | struct RepeatedConstGenericsNameInStructs<const N: u64, const N: u64> { }
@ -46,7 +94,7 @@ error: Multiple definitions of constant
____
error: Multiple definitions of constant
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:7:59
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:7:59
|
...
7 | enum RepeatedConstGenericsNameInEnums<const N: u64, const N: u64> { }
@ -56,7 +104,7 @@ error: Multiple definitions of constant
____
error: Multiple definitions of constant
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:8:61
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:8:61
|
...
8 | trait RepeatedConstGenericsNameInTraits<const N: u64, const N: u64> { }
@ -66,7 +114,7 @@ error: Multiple definitions of constant
____
error: Multiple definitions of constant
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:10:53
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:10:53
|
...
10 | fn repeated_const_generics_name<const N: u64, const N: u64>() {
@ -76,7 +124,7 @@ error: Multiple definitions of constant
____
error: Multiple definitions of constant
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:15:26
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:15:26
|
...
15 | impl<const N: u64, const N: u64> CrazyStruct<N> {
@ -86,7 +134,7 @@ error: Multiple definitions of constant
____
error: Multiple definitions of constant
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:19:71
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:19:71
|
...
19 | fn repeated_const_generics_name<const N: u64, const A: u64, const A: u64>() {
@ -96,7 +144,7 @@ error: Multiple definitions of constant
____
error: Multiple definitions of constant
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:19:43
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:19:43
|
...
18 | impl<const N: u64> CrazyStruct<N> {
@ -107,7 +155,7 @@ error: Multiple definitions of constant
____
error: Multiple definitions of constant
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:24:73
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:24:73
|
...
24 | fn repeated_const_generics_name_2<const N: u64, const A: u64, const A: u64>() {
@ -117,7 +165,7 @@ error: Multiple definitions of constant
____
error: Multiple definitions of constant
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:24:45
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:24:45
|
...
23 | impl<const N: u64> A for CrazyStruct<N> {
@ -128,7 +176,7 @@ error: Multiple definitions of constant
____
error: Multiple definitions of constant
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:32:26
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:32:26
|
...
32 | impl<const N: u64, const N: u64> CrazyEnum<N> {
@ -138,7 +186,7 @@ error: Multiple definitions of constant
____
error: Multiple definitions of constant
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:36:71
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:36:71
|
...
36 | fn repeated_const_generics_name<const N: u64, const A: u64, const A: u64>() {
@ -148,7 +196,7 @@ error: Multiple definitions of constant
____
error: Multiple definitions of constant
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:36:43
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:36:43
|
...
35 | impl<const N: u64> CrazyEnum<N> {
@ -159,7 +207,7 @@ error: Multiple definitions of constant
____
error: Multiple definitions of constant
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:41:73
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:41:73
|
...
41 | fn repeated_const_generics_name_2<const N: u64, const A: u64, const A: u64>() {
@ -169,7 +217,7 @@ error: Multiple definitions of constant
____
error: Multiple definitions of constant
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:41:45
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:41:45
|
...
40 | impl<const N: u64> A for CrazyEnum<N> {
@ -180,51 +228,99 @@ error: Multiple definitions of constant
____
error
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:46:24
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:46:13
|
44 |
45 | fn main() {
46 | let _: CrazyStruct<UNKNOWN> = CrazyStruct { };
45 | abi NoConstGenericsOnArgs {
46 | fn f(a: CrazyStruct<1>);
| ^^^^^^^^^^^^^^ "const generics" are not supported here.
47 | }
48 |
|
____
error
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:50:15
|
48 |
49 | abi NoConstGenericsOnReturn {
50 | fn f() -> CrazyEnum<1>;
| ^^^^^^^^^^^^ "const generics" are not supported here.
51 | }
52 |
|
____
error
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:58:15
|
56 |
57 | abi NoConstGenericsIndirectStruct {
58 | fn f() -> StructWithConstGenericInside;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "const generics" are not supported here.
59 | }
60 |
|
____
error
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:66:15
|
64 |
65 | abi NoConstGenericsIndirectEnum {
66 | fn f() -> EnumWithConstGenericInside;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ "const generics" are not supported here.
67 | }
68 |
|
____
error
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:70:24
|
68 |
69 | fn main() {
70 | let _: CrazyStruct<UNKNOWN> = CrazyStruct { };
| ^^^^^^^ Could not find symbol "UNKNOWN" in this scope.
47 | let _: CrazyEnum<UNKNOWN> = CrazyEnum::A;
48 | let _: [u8; UNKNOWN] = [1u8];
71 | let _: CrazyEnum<UNKNOWN> = CrazyEnum::A;
72 | let _: [u8; UNKNOWN] = [1u8];
|
____
error
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:47:22
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:71:22
|
45 | fn main() {
46 | let _: CrazyStruct<UNKNOWN> = CrazyStruct { };
47 | let _: CrazyEnum<UNKNOWN> = CrazyEnum::A;
69 | fn main() {
70 | let _: CrazyStruct<UNKNOWN> = CrazyStruct { };
71 | let _: CrazyEnum<UNKNOWN> = CrazyEnum::A;
| ^^^^^^^ Could not find symbol "UNKNOWN" in this scope.
48 | let _: [u8; UNKNOWN] = [1u8];
49 | let _: str[UNKNOWN] = __to_str_array("abc");
72 | let _: [u8; UNKNOWN] = [1u8];
73 | let _: str[UNKNOWN] = __to_str_array("abc");
|
____
error
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:48:17
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:72:17
|
46 | let _: CrazyStruct<UNKNOWN> = CrazyStruct { };
47 | let _: CrazyEnum<UNKNOWN> = CrazyEnum::A;
48 | let _: [u8; UNKNOWN] = [1u8];
70 | let _: CrazyStruct<UNKNOWN> = CrazyStruct { };
71 | let _: CrazyEnum<UNKNOWN> = CrazyEnum::A;
72 | let _: [u8; UNKNOWN] = [1u8];
| ^^^^^^^ Could not find symbol "UNKNOWN" in this scope.
49 | let _: str[UNKNOWN] = __to_str_array("abc");
50 | }
73 | let _: str[UNKNOWN] = __to_str_array("abc");
74 | }
|
____
error
--> test/src/e2e_vm_tests/test_programs/should_fail/const_generics/src/main.sw:49:16
--> test/src/e2e_vm_tests/test_programs/should_fail/unsupported_const_generics/src/main.sw:73:16
|
47 | let _: CrazyEnum<UNKNOWN> = CrazyEnum::A;
48 | let _: [u8; UNKNOWN] = [1u8];
49 | let _: str[UNKNOWN] = __to_str_array("abc");
71 | let _: CrazyEnum<UNKNOWN> = CrazyEnum::A;
72 | let _: [u8; UNKNOWN] = [1u8];
73 | let _: str[UNKNOWN] = __to_str_array("abc");
| ^^^^^^^ Could not find symbol "UNKNOWN" in this scope.
50 | }
74 | }
|
____
Aborting due to 18 errors.
error: Failed to compile const_generics
Aborting due to 22 errors.
error: Failed to compile unsupported_const_generics