Skip to content

feat: Protocol checking#1765

Open
croyzor wants to merge 14 commits into
mainfrom
cr/proto-check
Open

feat: Protocol checking#1765
croyzor wants to merge 14 commits into
mainfrom
cr/proto-check

Conversation

@croyzor
Copy link
Copy Markdown
Collaborator

@croyzor croyzor commented May 22, 2026

Closes #1396, closes #1210, closes #1653

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

🐰 Bencher Report

Branchcr/proto-check
TestbedLinux
Click to view all benchmark results
Benchmarkhugr_bytesBenchmark Result
bytes x 1e3
(Result Δ%)
Upper Boundary
bytes x 1e3
(Limit %)
hugr_nodesBenchmark Result
nodes
(Result Δ%)
Upper Boundary
nodes
(Limit %)
tests/benchmarks/test_big_array.py::test_big_array_compile📈 view plot
🚷 view threshold
158.77 x 1e3
(0.00%)Baseline: 158.77 x 1e3
160.36 x 1e3
(99.01%)
📈 view plot
🚷 view threshold
6,641.00
(0.00%)Baseline: 6,641.00
6,707.41
(99.01%)
tests/benchmarks/test_ctrl_flow.py::test_many_ctrl_flow_compile📈 view plot
🚷 view threshold
27.53 x 1e3
(0.00%)Baseline: 27.53 x 1e3
27.81 x 1e3
(99.01%)
📈 view plot
🚷 view threshold
1,074.00
(0.00%)Baseline: 1,074.00
1,084.74
(99.01%)
tests/benchmarks/test_queue_push_pop.py::test_queue_push_benchmark_compile📈 view plot
🚷 view threshold
10.91 x 1e3
(0.00%)Baseline: 10.91 x 1e3
11.02 x 1e3
(99.01%)
📈 view plot
🚷 view threshold
308.00
(0.00%)Baseline: 308.00
311.08
(99.01%)
tests/benchmarks/test_queue_push_pop.py::test_queue_push_pop_benchmark_compile📈 view plot
🚷 view threshold
14.84 x 1e3
(0.00%)Baseline: 14.84 x 1e3
14.98 x 1e3
(99.01%)
📈 view plot
🚷 view threshold
435.00
(0.00%)Baseline: 435.00
439.35
(99.01%)
🐰 View full continuous benchmarking report in Bencher

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 22, 2026

Codecov Report

❌ Patch coverage is 85.53719% with 35 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.30%. Comparing base (2dc589a) to head (2083883).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...rc/guppylang_internals/checker/protocol_checker.py 83.14% 15 Missing ⚠️
...ls/src/guppylang_internals/checker/func_checker.py 42.85% 8 Missing ⚠️
...pylang-internals/src/guppylang_internals/tys/ty.py 46.66% 8 Missing ⚠️
...ls/src/guppylang_internals/checker/expr_checker.py 91.30% 2 Missing ⚠️
...rnals/src/guppylang_internals/definition/struct.py 92.30% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1765      +/-   ##
==========================================
+ Coverage   92.98%   93.30%   +0.32%     
==========================================
  Files         135      136       +1     
  Lines       13053    13278     +225     
==========================================
+ Hits        12137    12389     +252     
+ Misses        916      889      -27     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 22, 2026

Merging this PR will not alter performance

✅ 9 untouched benchmarks


Comparing cr/proto-check (2083883) with main (2dc589a)

Open in CodSpeed

@croyzor croyzor changed the title feat: Protocol checking feat!: Protocol checking May 26, 2026
@croyzor croyzor requested a review from mark-koch May 26, 2026 15:57
@croyzor croyzor marked this pull request as ready for review May 26, 2026 16:33
@croyzor croyzor requested a review from a team as a code owner May 26, 2026 16:33
@croyzor croyzor changed the title feat!: Protocol checking feat: Protocol checking May 27, 2026
Copy link
Copy Markdown
Collaborator

@mark-koch mark-koch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥 🔥 🔥

Sorry I didn't have the time to look through everything in detail yet, but here are some comments from a first read through. Will have a closer look next week

Comment on lines 338 to +340
if isinstance(defn, ParsedProtocolDef):
assert isinstance(func_ty, FunctionType)
raise GuppyError(
UnsupportedError(
node.func, "Checking protocol method calls", singular=True
)
)
raise RequiresMonomorphizationError
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would defn ever be a ParsedProtocolDef?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see because attribute accesses are turned into Global names with the protocol id.

I think it would be better to add a new node type that holds a protocol id and the name of the method that is used. Then we wouldn't need to cop out here

member_ty,
with_loc(
node,
GlobalName(id=member_name, def_id=proto.def_id),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning a global name with the id of the full protocol feels a bit weird. Don't we want some sort of identifier for wich method of the protocol is used?

Comment on lines -388 to +387
"Self", copyable=self_ty_head.copyable, droppable=self_ty_head.droppable
"Self", copyable=True, droppable=True
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?

)


def handle_implicit_proto_self_arg(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a docstring explaining what this function does



@dataclass(frozen=True)
class ImplProofBase(ABC):
Copy link
Copy Markdown
Collaborator

@mark-koch mark-koch May 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also docstrings for the things in this file

Comment on lines +5 to +7
6 | def foo(self) -> None: ...
| ^^^^ Inference of type for `self` is not supported in protocol
| methods
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo better to use the regular MissingArgAnnotationError error (maybe with a note that self inference on protcols is not supported)

5 | class Foo:
6 | @guppy.require
7 | def foo(n: nat) -> None: ...
| ^^^ `n` must be of type `self`
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self (captitalised) or Foo?

23 | @guppy
24 | def baz(f: FooInt) -> None:
25 | return eat_foostr(f)
| ^ Type `FooInt` does not implement protocol `Foo`
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldnt it be "does not implement protocol Foo[str]"?

5 | class Proto[T]:
6 | @guppy.require
7 | def foo[T](t: T) -> T: ...
| ^^^^ Type `T` does not implement protocol `Proto`
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A nice future improvement would be to add notes explaining why the protocol is not implemented

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, created #1779

Comment on lines +40 to +41
## TODO: See if this can work in light of the monomorphisation changes assuming
## check and compile are called on entrypoints
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these TODOs resolved?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants