diff --git a/examples/demo.ipynb b/examples/demo.ipynb index bafce326..6a11aeae 100644 --- a/examples/demo.ipynb +++ b/examples/demo.ipynb @@ -306,13 +306,18 @@ "name": "stderr", "output_type": "stream", "text": [ - "Guppy compilation failed. Error in file :6\n", + "Error: Linearity violation (at :6:10)\n", + " | \n", + "4 | @guppy(bad_module)\n", + "5 | def bad(q: qubit @owned) -> qubit:\n", + "6 | cx(q, q)\n", + " | ^ Variable `q` with linear type `qubit` cannot be borrowed\n", + " | ...\n", + " | \n", + "6 | cx(q, q)\n", + " | - since it was already borrowed here\n", "\n", - "4: @guppy(bad_module)\n", - "5: def bad(q: qubit @owned) -> qubit:\n", - "6: cx(q, q)\n", - " ^\n", - "GuppyError: Variable `q` with linear type `qubit` was already used (at 6:7)\n" + "Guppy compilation failed due to 1 previous error\n" ] } ], @@ -350,13 +355,16 @@ "name": "stderr", "output_type": "stream", "text": [ - "Guppy compilation failed. Error in file :7\n", + "Error: Linearity violation (at :7:7)\n", + " | \n", + "5 | def bad(q: qubit @owned) -> qubit:\n", + "6 | tmp = qubit()\n", + "7 | cx(tmp, q)\n", + " | ^^^ Variable `tmp` with linear type `qubit` is leaked\n", + "\n", + "Help: Make sure that `tmp` is consumed or returned to avoid the leak\n", "\n", - "5: def bad(q: qubit @owned) -> qubit:\n", - "6: tmp = qubit()\n", - "7: cx(tmp, q)\n", - " ^^^\n", - "GuppyError: Variable `tmp` with linear type `qubit` is not used on all control-flow paths\n" + "Guppy compilation failed due to 1 previous error\n" ] } ], diff --git a/guppylang/checker/cfg_checker.py b/guppylang/checker/cfg_checker.py index fbd748b9..909d1d5b 100644 --- a/guppylang/checker/cfg_checker.py +++ b/guppylang/checker/cfg_checker.py @@ -60,7 +60,7 @@ def __init__(self, input_tys: list[Type], output_ty: Type) -> None: def check_cfg( - cfg: CFG, inputs: Row[Variable], return_ty: Type, globals: Globals + cfg: CFG, inputs: Row[Variable], return_ty: Type, func_name: str, globals: Globals ) -> CheckedCFG[Place]: """Type checks a control-flow graph. @@ -126,7 +126,7 @@ def check_cfg( # Finally, run the linearity check from guppylang.checker.linearity_checker import check_cfg_linearity - linearity_checked_cfg = check_cfg_linearity(checked_cfg, globals) + linearity_checked_cfg = check_cfg_linearity(checked_cfg, func_name, globals) return linearity_checked_cfg diff --git a/guppylang/checker/core.py b/guppylang/checker/core.py index 4f732b13..d647dc49 100644 --- a/guppylang/checker/core.py +++ b/guppylang/checker/core.py @@ -85,6 +85,11 @@ def id(self) -> "Variable.Id": """The unique `PlaceId` identifier for this place.""" return Variable.Id(self.name) + @cached_property + def root(self) -> "Variable": + """The root variable of this place.""" + return self + @property def describe(self) -> str: """A human-readable description of this place for error messages.""" @@ -123,6 +128,11 @@ def id(self) -> "FieldAccess.Id": """The unique `PlaceId` identifier for this place.""" return FieldAccess.Id(self.parent.id, self.field.name) + @cached_property + def root(self) -> "Variable": + """The root variable of this place.""" + return self.parent.root + @property def ty(self) -> Type: """The type of this place.""" @@ -182,6 +192,11 @@ def defined_at(self) -> AstNode | None: """Optional location where this place was last assigned to.""" return self.parent.defined_at + @cached_property + def root(self) -> "Variable": + """The root variable of this place.""" + return self.parent.root + @property def describe(self) -> str: """A human-readable description of this place for error messages.""" diff --git a/guppylang/checker/errors/__init__.py b/guppylang/checker/errors/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/guppylang/checker/errors/linearity.py b/guppylang/checker/errors/linearity.py new file mode 100644 index 00000000..68638ac9 --- /dev/null +++ b/guppylang/checker/errors/linearity.py @@ -0,0 +1,259 @@ +"""Collection of error messages emitted during linearity checking.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING, ClassVar + +from guppylang.diagnostic import Error, Help, Note + +if TYPE_CHECKING: + from guppylang.checker.core import ( + Place, + Variable, + ) + from guppylang.checker.linearity_checker import UseKind + from guppylang.definition.struct import StructField + from guppylang.tys.ty import ( + StructType, + Type, + ) + + +@dataclass(frozen=True) +class AlreadyUsedError(Error): + title: ClassVar[str] = "Linearity violation" + span_label: ClassVar[str] = ( + "{place.describe} with linear type `{place.ty}` cannot be {kind.subjunctive} " + "..." + ) + place: Place + kind: UseKind + + @dataclass(frozen=True) + class PrevUse(Note): + span_label: ClassVar[str] = "since it was already {prev_kind.subjunctive} here" + prev_kind: UseKind + + +@dataclass(frozen=True) +class ComprAlreadyUsedError(Error): + title: ClassVar[str] = "Linearity violation" + span_label: ClassVar[str] = ( + "{place.describe} with linear type `{place.ty}` would be {kind.subjunctive} " + "multiple times when evaluating this comprehension" + ) + place: Place + kind: UseKind + + @dataclass(frozen=True) + class PrevUse(Note): + span_label: ClassVar[str] = "since it was already {prev_kind.subjunctive} here" + prev_kind: UseKind + + +@dataclass(frozen=True) +class PlaceNotUsedError(Error): + title: ClassVar[str] = "Linearity violation" + place: Place + + @property + def rendered_span_label(self) -> str: + s = f"{self.place.describe} with linear type `{self.place.ty}` " + match self.children: + case [PlaceNotUsedError.Branch(), *_]: + return s + "may be leaked ..." + case _: + return s + "is leaked" + + @dataclass(frozen=True) + class Branch(Note): + span_label: ClassVar[str] = "if this expression is `{truth_value}`" + truth_value: bool + + @dataclass(frozen=True) + class Fix(Help): + message: ClassVar[str] = ( + "Make sure that `{place}` is consumed or returned to avoid the leak" + ) + + +@dataclass(frozen=True) +class UnnamedExprNotUsedError(Error): + title: ClassVar[str] = "Linearity violation" + span_label: ClassVar[str] = "Expression with linear type `{ty}` is leaked" + ty: Type + + @dataclass(frozen=True) + class Fix(Help): + message: ClassVar[str] = "Consider assigning this value to a local variable" + + +@dataclass(frozen=True) +class UnnamedFieldNotUsedError(Error): + title: ClassVar[str] = "Linearity violation" + span_label: ClassVar[str] = ( + "Linear field `{field.name}` of expression with type `{struct_ty}` is leaked" + ) + field: StructField + struct_ty: StructType + + @dataclass(frozen=True) + class Fix(Help): + message: ClassVar[str] = ( + "Consider assigning this value to a local variable before accessing the " + "field `{used_field.name}`" + ) + used_field: StructField + + +@dataclass(frozen=True) +class UnnamedSubscriptNotUsedError(Error): + title: ClassVar[str] = "Linearity violation" + span_label: ClassVar[str] = ( + "Linear items of expression with type `{container_ty}` are leaked ..." + ) + container_ty: Type + + @dataclass(frozen=True) + class SubscriptHint(Note): + span_label: ClassVar[str] = "since only this subscript is used" + + @dataclass(frozen=True) + class Fix(Help): + message: ClassVar[str] = ( + "Consider assigning this value to a local variable before subscripting it" + ) + + +@dataclass(frozen=True) +class NotOwnedError(Error): + title: ClassVar[str] = "Not owned" + place: Place + kind: UseKind + is_call_arg: bool + func_name: str | None + calling_func_name: str + + @property + def rendered_span_label(self) -> str: + if self.is_call_arg: + f = f"Function `{self.func_name}`" if self.func_name else "Function" + return ( + f"{f} wants to take ownership of this argument, but " + f"`{self.calling_func_name}` doesn't own `{self.place}`" + ) + return ( + f"Cannot {self.kind.indicative} `{self.place}` since " + f"`{self.calling_func_name}` doesn't own it" + ) + + @dataclass(frozen=True) + class MakeOwned(Help): + span_label: ClassVar[str] = ( + "Argument `{place.root.name}` is only borrowed. Consider taking ownership: " + "`{place.root.name}: {place.root.ty} @owned`" + ) + + +@dataclass(frozen=True) +class MoveOutOfSubscriptError(Error): + title: ClassVar[str] = "Subscript {kind.subjunctive}" + span_label: ClassVar[str] = ( + "Cannot {kind.indicative} a subscript of `{parent}` with linear type " + "`{parent.ty}`" + ) + kind: UseKind + parent: Place + + @dataclass(frozen=True) + class Explanation(Note): + message: ClassVar[str] = ( + "Subscripts on linear types are only allowed to be borrowed, not " + "{kind.subjunctive}" + ) + + +@dataclass(frozen=True) +class BorrowShadowedError(Error): + title: ClassVar[str] = "Borrow shadowed" + span_label: ClassVar[str] = "Assignment shadows borrowed argument `{place}`" + place: Place + + @dataclass(frozen=True) + class Rename(Help): + message: ClassVar[str] = "Consider assigning to a different name" + + +@dataclass(frozen=True) +class BorrowSubPlaceUsedError(Error): + title: ClassVar[str] = "Linearity violation" + span_label: ClassVar[str] = ( + "Borrowed argument {borrowed_var} cannot be returned to the caller ..." + ) + borrowed_var: Variable + sub_place: Place + + @dataclass(frozen=True) + class PrevUse(Note): + span_label: ClassVar[str] = ( + "since `{sub_place}` with linear type `{sub_place.ty}` was already " + "{kind.subjunctive} here" + ) + kind: UseKind + + @dataclass(frozen=True) + class Fix(Help): + message: ClassVar[str] = ( + "Consider writing a value back into `{sub_place}` before returning" + ) + + +@dataclass(frozen=True) +class DropAfterCallError(Error): + title: ClassVar[str] = "Linearity violation" + span_label: ClassVar[str] = ( + "Value with linear type `{ty}` would be leaked after {func} returns" + ) + ty: Type + func_name: str | None + + @property + def func(self) -> str: + return f"`{self.func_name}`" if self.func_name else "the function" + + @dataclass(frozen=True) + class Assign(Help): + message: ClassVar[str] = ( + "Consider assigning the value to a local variable before passing it to " + "{func}" + ) + + +@dataclass(frozen=True) +class LinearCaptureError(Error): + title: ClassVar[str] = "Linearity violation" + span_label: ClassVar[str] = ( + "{var.describe} with linear type {var.ty} cannot be used here since `{var}` is " + "captured from an outer scope" + ) + var: Variable + + @dataclass(frozen=True) + class DefinedHere(Note): + span_label: ClassVar[str] = "`{var}` defined here" + + +@dataclass(frozen=True) +class LinearPartialApplyError(Error): + title: ClassVar[str] = "Linearity violation" + span_label: ClassVar[str] = ( + "This expression implicitly constructs a closure that captures a linear value" + ) + + @dataclass(frozen=True) + class Captured(Note): + span_label: ClassVar[str] = ( + "This expression with linear type `{ty}` is implicitly captured" + ) + ty: Type diff --git a/guppylang/checker/expr_checker.py b/guppylang/checker/expr_checker.py index 6bdfe386..b36ca20a 100644 --- a/guppylang/checker/expr_checker.py +++ b/guppylang/checker/expr_checker.py @@ -586,7 +586,10 @@ def visit_Subscript(self, node: ast.Subscript) -> tuple[ast.expr, Type]: # other indices after this one has been projected out (e.g. `f()[0]` makes # you loose access to all elements besides 0). expr = SubscriptAccessAndDrop( - item=item, item_expr=item_expr, getitem_expr=getitem_expr + item=item, + item_expr=item_expr, + getitem_expr=getitem_expr, + original_expr=node, ) return with_loc(node, expr), result_ty diff --git a/guppylang/checker/func_checker.py b/guppylang/checker/func_checker.py index 47d59b2a..85117c01 100644 --- a/guppylang/checker/func_checker.py +++ b/guppylang/checker/func_checker.py @@ -36,7 +36,7 @@ def check_global_func_def( Variable(x, inp.ty, loc, inp.flags) for x, inp, loc in zip(ty.input_names, ty.inputs, args, strict=True) ] - return check_cfg(cfg, inputs, ty.output, globals) + return check_cfg(cfg, inputs, ty.output, func_def.name, globals) def check_nested_func_def( @@ -102,7 +102,7 @@ def check_nested_func_def( # Otherwise, we treat it like a local name inputs.append(Variable(func_def.name, func_def.ty, func_def)) - checked_cfg = check_cfg(cfg, inputs, func_ty.output, globals) + checked_cfg = check_cfg(cfg, inputs, func_ty.output, func_def.name, globals) checked_def = CheckedNestedFunctionDef( def_id, checked_cfg, diff --git a/guppylang/checker/linearity_checker.py b/guppylang/checker/linearity_checker.py index a057b900..17add223 100644 --- a/guppylang/checker/linearity_checker.py +++ b/guppylang/checker/linearity_checker.py @@ -6,7 +6,8 @@ import ast from collections.abc import Generator, Iterator from contextlib import contextmanager -from typing import TypeGuard +from enum import Enum, auto +from typing import TYPE_CHECKING, NamedTuple, TypeGuard from guppylang.ast_util import AstNode, find_nodes, get_type from guppylang.cfg.analysis import LivenessAnalysis @@ -21,10 +22,26 @@ SubscriptAccess, Variable, ) +from guppylang.checker.errors.linearity import ( + AlreadyUsedError, + BorrowShadowedError, + BorrowSubPlaceUsedError, + ComprAlreadyUsedError, + DropAfterCallError, + LinearCaptureError, + LinearPartialApplyError, + MoveOutOfSubscriptError, + NotOwnedError, + PlaceNotUsedError, + UnnamedExprNotUsedError, + UnnamedFieldNotUsedError, + UnnamedSubscriptNotUsedError, +) from guppylang.definition.custom import CustomFunctionDef from guppylang.definition.value import CallableDef from guppylang.error import GuppyError, GuppyTypeError from guppylang.nodes import ( + AnyCall, CheckedNestedFunctionDef, DesugaredGenerator, DesugaredListComp, @@ -44,6 +61,64 @@ StructType, ) +if TYPE_CHECKING: + from guppylang.diagnostic import Error + + +class UseKind(Enum): + """The different ways places can be used.""" + + #: A classical value is copied + COPY = auto() + + #: A value is borrowed when passing it to a function + BORROW = auto() + + #: Ownership of an owned value is transferred by passing it to a function + CONSUME = auto() + + #: Ownership of an owned value is transferred by returning it + RETURN = auto() + + #: An owned value is renamed or stored in a tuple/list + MOVE = auto() + + @property + def indicative(self) -> str: + """Describes a use in an indicative mood. + + For example: "You cannot *consume* this qubit." + """ + return self.name.lower() + + @property + def subjunctive(self) -> str: + """Describes a use in a subjunctive mood. + + For example: "This qubit cannot be *consumed*" + """ + match self: + case UseKind.COPY: + return "copied" + case UseKind.BORROW: + return "borrowed" + case UseKind.CONSUME: + return "consumed" + case UseKind.RETURN: + return "returned" + case UseKind.MOVE: + return "moved" + + +class Use(NamedTuple): + """Records data associated with a use of a place.""" + + #: The AST node corresponding to the use + node: AstNode + + #: The kind of use, i.e. is the value consumed, borrowed, returned, ...? + kind: UseKind + class Scope(Locals[PlaceId, Place]): """Scoped collection of assigned places indexed by their id. @@ -52,33 +127,33 @@ class Scope(Locals[PlaceId, Place]): """ parent_scope: "Scope | None" - used_local: dict[PlaceId, AstNode] - used_parent: dict[PlaceId, AstNode] + used_local: dict[PlaceId, Use] + used_parent: dict[PlaceId, Use] def __init__(self, parent: "Scope | None" = None): self.used_local = {} self.used_parent = {} super().__init__({}, parent) - def used(self, x: PlaceId) -> AstNode | None: + def used(self, x: PlaceId) -> Use | None: """Checks whether a place has already been used.""" if x in self.vars: return self.used_local.get(x, None) assert self.parent_scope is not None return self.parent_scope.used(x) - def use(self, x: PlaceId, node: AstNode) -> None: + def use(self, x: PlaceId, node: AstNode, kind: UseKind) -> None: """Records a use of a place. Works for places in the current scope as well as places in any parent scope. """ if x in self.vars: - self.used_local[x] = node + self.used_local[x] = Use(node, kind) else: assert self.parent_scope is not None assert x in self.parent_scope - self.used_parent[x] = node - self.parent_scope.use(x, node) + self.used_parent[x] = Use(node, kind) + self.parent_scope.use(x, node, kind) def assign(self, place: Place) -> None: """Records an assignment of a place.""" @@ -93,7 +168,8 @@ def stats(self) -> VariableStats[PlaceId]: for x, place in self.vars.items(): assert place.defined_at is not None assigned[x] = place.defined_at - return VariableStats(assigned, self.used_parent) + used = {x: use.node for x, use in self.used_parent.items()} + return VariableStats(assigned, used) class BBLinearityChecker(ast.NodeVisitor): @@ -101,6 +177,7 @@ class BBLinearityChecker(ast.NodeVisitor): scope: Scope stats: VariableStats[PlaceId] + func_name: str func_inputs: dict[PlaceId, Variable] globals: Globals @@ -108,6 +185,7 @@ def check( self, bb: "CheckedBB[Variable]", is_entry: bool, + func_name: str, func_inputs: dict[PlaceId, Variable], globals: Globals, ) -> Scope: @@ -117,6 +195,7 @@ def check( for var in bb.sig.input_row: for place in leaf_places(var): input_scope.assign(place) + self.func_name = func_name self.func_inputs = func_inputs self.globals = globals @@ -139,26 +218,36 @@ def new_scope(self) -> Generator[Scope, None, None]: yield new_scope self.scope = scope - def visit_PlaceNode(self, node: PlaceNode, /, is_inout_arg: bool = False) -> None: + def visit_PlaceNode( + self, + node: PlaceNode, + /, + use_kind: UseKind = UseKind.MOVE, + is_call_arg: AnyCall | None = None, + ) -> None: # Usage of borrowed variables is generally forbidden. The only exception is - # letting them be borrowed by another function call. In that case, our - # `_visit_call_args` helper will set `is_inout_arg=True`. + # letting them be reborrowed by another function call. In that case, our + # `_visit_call_args` helper will set `use_kind=UseKind.BORROW`. + is_inout_arg = use_kind == UseKind.BORROW if is_inout_var(node.place) and not is_inout_arg: - raise GuppyError( - f"{node.place.describe} may not be used in an `@owned` position since " - "it isn't owned. Consider adding a `@owned` annotation to get " - "ownership of the value.", + err: Error = NotOwnedError( node, + node.place, + use_kind, + is_call_arg is not None, + self._call_name(is_call_arg), + self.func_name, ) + arg_span = self.func_inputs[node.place.root.id].defined_at + err.add_sub_diagnostic(NotOwnedError.MakeOwned(arg_span)) + raise GuppyError(err) # Places involving subscripts are handled differently since we ignore everything # after the subscript for the purposes of linearity checking if subscript := contains_subscript(node.place): if not is_inout_arg and subscript.parent.ty.linear: - raise GuppyError( - "Subscripting on expression with linear type " - f"`{subscript.parent.ty}` is not allowed in `@owned` position", - node, - ) + err = MoveOutOfSubscriptError(node, use_kind, subscript.parent) + err.add_sub_diagnostic(MoveOutOfSubscriptError.Explanation(None)) + raise GuppyError(err) self.visit(subscript.item_expr) self.scope.assign(subscript.item) # Visiting the `__getitem__(place.parent, place.item)` call ensures that we @@ -168,14 +257,13 @@ def visit_PlaceNode(self, node: PlaceNode, /, is_inout_arg: bool = False) -> Non else: for place in leaf_places(node.place): x = place.id - if (use := self.scope.used(x)) and place.ty.linear: - raise GuppyError( - f"{place.describe} with linear type `{place.ty}` was already " - "used (at {0})", - node, - [use], + if (prev_use := self.scope.used(x)) and place.ty.linear: + err = AlreadyUsedError(node, place, use_kind) + err.add_sub_diagnostic( + AlreadyUsedError.PrevUse(prev_use.node, prev_use.kind) ) - self.scope.use(x, node) + raise GuppyError(err) + self.scope.use(x, node, use_kind) def visit_Assign(self, node: ast.Assign) -> None: self.visit(node.value) @@ -189,39 +277,50 @@ def visit_Assign(self, node: ast.Assign) -> None: if tgt.place.id in self.func_inputs: entry_place = self.func_inputs[tgt.place.id] if is_inout_var(entry_place): - raise GuppyError( - f"Assignment shadows borrowed argument `{entry_place}`. " - "Consider assigning to a different name.", - tgt.place.defined_at, - ) - - def _visit_call_args(self, func_ty: FunctionType, args: list[ast.expr]) -> None: + err = BorrowShadowedError(tgt.place.defined_at, entry_place) + err.add_sub_diagnostic(BorrowShadowedError.Rename(None)) + raise GuppyError(err) + + def visit_Return(self, node: ast.Return) -> None: + # Intercept returns of places, so we can set the appropriate `use_kind` to get + # nicer error messages + if isinstance(node.value, PlaceNode): + self.visit_PlaceNode(node.value, use_kind=UseKind.RETURN) + elif isinstance(node.value, ast.Tuple): + for elt in node.value.elts: + if isinstance(elt, PlaceNode): + self.visit_PlaceNode(elt, use_kind=UseKind.RETURN) + else: + self.visit(elt) + elif node.value: + self.visit(node.value) + + def _visit_call_args(self, func_ty: FunctionType, call: AnyCall) -> None: """Helper function to check the arguments of a function call. - Populates the `is_inout_arg` kwarg of `visit_PlaceNode` in case some of the + Populates the `use_kind` kwarg of `visit_PlaceNode` in case some of the arguments are places. """ - for inp, arg in zip(func_ty.inputs, args, strict=True): + for inp, arg in zip(func_ty.inputs, call.args, strict=True): if isinstance(arg, PlaceNode): - self.visit_PlaceNode(arg, is_inout_arg=InputFlags.Inout in inp.flags) + use_kind = ( + UseKind.BORROW if InputFlags.Inout in inp.flags else UseKind.CONSUME + ) + self.visit_PlaceNode(arg, use_kind=use_kind, is_call_arg=call) else: self.visit(arg) - def _reassign_inout_args(self, func_ty: FunctionType, args: list[ast.expr]) -> None: + def _reassign_inout_args(self, func_ty: FunctionType, call: AnyCall) -> None: """Helper function to reassign the borrowed arguments after a function call.""" - for inp, arg in zip(func_ty.inputs, args, strict=True): + for inp, arg in zip(func_ty.inputs, call.args, strict=True): if InputFlags.Inout in inp.flags: match arg: case PlaceNode(place=place): self._reassign_single_inout_arg(place, arg) case arg if inp.ty.linear: - raise GuppyError( - f"Borrowed argument with linear type `{inp.ty}` would be " - "dropped after this function call. Consider assigning the " - "expression to a local variable before passing it to the " - "function.", - arg, - ) + err = DropAfterCallError(arg, inp.ty, self._call_name(call)) + err.add_sub_diagnostic(DropAfterCallError.Assign(None)) + raise GuppyError(err) def _reassign_single_inout_arg(self, place: Place, node: ast.expr) -> None: """Helper function to reassign a single borrowed argument after a function @@ -237,6 +336,14 @@ def _reassign_single_inout_arg(self, place: Place, node: ast.expr) -> None: leaf = leaf.replace_defined_at(node) self.scope.assign(leaf) + def _call_name(self, node: AnyCall | None) -> str | None: + """Tries to extract the name of a called function from a call AST node.""" + if isinstance(node, LocalCall): + return node.func.id if isinstance(node.func, ast.Name) else None + elif isinstance(node, GlobalCall): + return self.globals[node.def_id].name + return None + def visit_GlobalCall(self, node: GlobalCall) -> None: func = self.globals[node.def_id] assert isinstance(func, CallableDef) @@ -247,32 +354,29 @@ def visit_GlobalCall(self, node: GlobalCall) -> None: ) else: func_ty = func.ty.instantiate(node.type_args) - self._visit_call_args(func_ty, node.args) - self._reassign_inout_args(func_ty, node.args) + self._visit_call_args(func_ty, node) + self._reassign_inout_args(func_ty, node) def visit_LocalCall(self, node: LocalCall) -> None: func_ty = get_type(node.func) assert isinstance(func_ty, FunctionType) self.visit(node.func) - self._visit_call_args(func_ty, node.args) - self._reassign_inout_args(func_ty, node.args) + self._visit_call_args(func_ty, node) + self._reassign_inout_args(func_ty, node) def visit_TensorCall(self, node: TensorCall) -> None: for arg in node.args: self.visit(arg) - self._reassign_inout_args(node.tensor_ty, node.args) + self._reassign_inout_args(node.tensor_ty, node) def visit_PartialApply(self, node: PartialApply) -> None: self.visit(node.func) for arg in node.args: ty = get_type(arg) if ty.linear: - raise GuppyError( - f"Capturing a value with linear type `{ty}` in a closure is not " - "allowed. Try calling the function directly instead of using it as " - "a higher-order value.", - node, - ) + err = LinearPartialApplyError(node) + err.add_sub_diagnostic(LinearPartialApplyError.Captured(arg, ty)) + raise GuppyError(err) self.visit(arg) def visit_FieldAccessAndDrop(self, node: FieldAccessAndDrop) -> None: @@ -282,11 +386,9 @@ def visit_FieldAccessAndDrop(self, node: FieldAccessAndDrop) -> None: self.visit(node.value) for field in node.struct_ty.fields: if field.name != node.field.name and field.ty.linear: - raise GuppyTypeError( - f"Linear field `{field.name}` of expression with type " - f"`{node.struct_ty}` is not used", - node.value, - ) + err = UnnamedFieldNotUsedError(node.value, field, node.struct_ty) + err.add_sub_diagnostic(UnnamedFieldNotUsedError.Fix(None, node.field)) + raise GuppyError(err) def visit_SubscriptAccessAndDrop(self, node: SubscriptAccessAndDrop) -> None: # A subscript access on a value that is not a place. This means the value can no @@ -294,9 +396,13 @@ def visit_SubscriptAccessAndDrop(self, node: SubscriptAccessAndDrop) -> None: # legal if the items in the container are not linear elem_ty = get_type(node.getitem_expr) if elem_ty.linear: - raise GuppyTypeError( - f"Remaining linear items with type `{elem_ty}` are not used", node + value = node.original_expr.value + err = UnnamedSubscriptNotUsedError(value, get_type(value)) + err.add_sub_diagnostic( + UnnamedSubscriptNotUsedError.SubscriptHint(node.item_expr) ) + err.add_sub_diagnostic(UnnamedSubscriptNotUsedError.Fix(None)) + raise GuppyTypeError(err) self.visit(node.item_expr) self.scope.assign(node.item) self.visit(node.getitem_expr) @@ -306,7 +412,9 @@ def visit_Expr(self, node: ast.Expr) -> None: self.visit(node.value) ty = get_type(node.value) if ty.linear: - raise GuppyTypeError(f"Value with linear type `{ty}` is not used", node) + err = UnnamedExprNotUsedError(node, ty) + err.add_sub_diagnostic(UnnamedExprNotUsedError.Fix(None)) + raise GuppyTypeError(err) def visit_DesugaredListComp(self, node: DesugaredListComp) -> None: self._check_comprehension(node, node.generators) @@ -317,14 +425,11 @@ def visit_CheckedNestedFunctionDef(self, node: CheckedNestedFunctionDef) -> None # TODO: In the future, we could support capturing of non-linear subplaces for var, use in node.captured.values(): if var.ty.linear: - raise GuppyError( - f"{var.describe} with linear type `{var.ty}` may not be used here " - f"because it was defined in an outer scope (at {{0}})", - use, - [var.defined_at], - ) + err = LinearCaptureError(use, var) + err.add_sub_diagnostic(LinearCaptureError.DefinedHere(var.defined_at)) + raise GuppyError(err) for place in leaf_places(var): - self.scope.use(place.id, use) + self.scope.use(place.id, use, UseKind.COPY) self.scope.assign(Variable(node.name, node.ty, node)) def _check_assign_targets(self, targets: list[ast.expr]) -> None: @@ -336,11 +441,9 @@ def _check_assign_targets(self, targets: list[ast.expr]) -> None: # Special error message for shadowing of borrowed vars x = tgt.place.id if x in self.scope.vars and is_inout_var(self.scope[x]): - raise GuppyError( - f"Assignment shadows borrowed argument `{tgt.place}`. " - "Consider assigning to a different name.", - tgt, - ) + err: Error = BorrowShadowedError(tgt, tgt.place) + err.add_sub_diagnostic(BorrowShadowedError.Rename(None)) + raise GuppyError(err) for tgt_place in leaf_places(tgt.place): x = tgt_place.id # Only check for overrides of places locally defined in this BB. Global @@ -348,11 +451,9 @@ def _check_assign_targets(self, targets: list[ast.expr]) -> None: if x in self.scope.vars and x not in self.scope.used_local: place = self.scope[x] if place.ty.linear: - raise GuppyError( - f"{place.describe} with linear type `{place.ty}` is not " - "used", - place.defined_at, - ) + err = PlaceNotUsedError(place.defined_at, place) + err.add_sub_diagnostic(PlaceNotUsedError.Fix(None)) + raise GuppyError(err) self.scope.assign(tgt_place) def _check_comprehension( @@ -392,12 +493,11 @@ def _check_comprehension( for leaf in leaf_places(place): x = leaf.id if not self.scope.used(x) and place.ty.linear: - raise GuppyTypeError( - f"{place.describe} with linear type `{place.ty}` is " - "not used on all control-flow paths of the list " - "comprehension", - place.defined_at, + err = PlaceNotUsedError(place.defined_at, place) + err.add_sub_diagnostic( + PlaceNotUsedError.Branch(first_if, False) ) + raise GuppyTypeError(err) for expr in other_ifs: self.visit(expr) @@ -413,10 +513,7 @@ def _check_comprehension( for leaf in leaf_places(place): x = leaf.id if leaf.ty.linear and not inner_scope.used(x): - raise GuppyTypeError( - f"{leaf.describe} with linear type `{leaf.ty}` is not used", - leaf.defined_at, - ) + raise GuppyTypeError(PlaceNotUsedError(leaf.defined_at, leaf)) # On the other hand, we have to ensure that no linear places from the # outer scope have been used inside the comprehension (they would be used @@ -425,9 +522,7 @@ def _check_comprehension( place = inner_scope[x] if place.ty.linear: raise GuppyTypeError( - f"{place.describe} with linear type `{place.ty}` would be used " - "multiple times when evaluating this comprehension", - use, + ComprAlreadyUsedError(use.node, place, use.kind) ) @@ -459,7 +554,7 @@ def is_inout_var(place: Place) -> TypeGuard[Variable]: def check_cfg_linearity( - cfg: "CheckedCFG[Variable]", globals: Globals + cfg: "CheckedCFG[Variable]", func_name: str, globals: Globals ) -> "CheckedCFG[Place]": """Checks whether a CFG satisfies the linearity requirements. @@ -472,7 +567,11 @@ def check_cfg_linearity( func_inputs: dict[PlaceId, Variable] = {v.id: v for v in cfg.entry_bb.sig.input_row} scopes: dict[BB, Scope] = { bb: bb_checker.check( - bb, is_entry=bb == cfg.entry_bb, func_inputs=func_inputs, globals=globals + bb, + is_entry=bb == cfg.entry_bb, + func_name=func_name, + func_inputs=func_inputs, + globals=globals, ) for bb in cfg.bbs } @@ -482,7 +581,7 @@ def check_cfg_linearity( for var in cfg.entry_bb.sig.input_row: if InputFlags.Inout in var.flags: for leaf in leaf_places(var): - exit_scope.use(leaf.id, InoutReturnSentinel(var=var)) + exit_scope.use(leaf.id, InoutReturnSentinel(var=var), UseKind.RETURN) # Run liveness analysis stats = {bb: scope.stats() for bb, scope in scopes.items()} @@ -505,46 +604,51 @@ def check_cfg_linearity( use = use_scope.used_parent[x] # Special case if this is a use arising from the implicit returning # of a borrowed argument - if isinstance(use, InoutReturnSentinel): - assert isinstance(use.var, Variable) - assert InputFlags.Inout in use.var.flags - raise GuppyError( - f"Borrowed argument `{use.var}` cannot be returned " - f"to the caller since `{place}` is used at {{0}}. " - f"Consider writing a value back into `{place}` before " - "returning.", - use.var.defined_at, - [prev_use], + if isinstance(use.node, InoutReturnSentinel): + assert isinstance(use.node.var, Variable) + assert InputFlags.Inout in use.node.var.flags + err: Error = BorrowSubPlaceUsedError( + use.node.var.defined_at, use.node.var, place + ) + err.add_sub_diagnostic( + BorrowSubPlaceUsedError.PrevUse( + prev_use.node, prev_use.kind + ) ) - raise GuppyError( - f"{place.describe} with linear type `{place.ty}` was " - "already used (at {0})", - use, - [prev_use], + err.add_sub_diagnostic(BorrowSubPlaceUsedError.Fix(None)) + raise GuppyError(err) + err = AlreadyUsedError(use.node, place, use.kind) + err.add_sub_diagnostic( + AlreadyUsedError.PrevUse(prev_use.node, prev_use.kind) ) + raise GuppyError(err) - # On the other hand, unused linear variables *must* be outputted - for place in scope.values(): - for leaf in leaf_places(place): - x = leaf.id - # Some values are just in scope because the type checker determined - # them as live in the first (less precises) dataflow analysis. It - # might be the case that x is actually not live when considering - # the second, more fine-grained, analysis based on places. - if x not in live_before_bb and x not in scope.vars: - continue - used_later = x in live - if leaf.ty.linear and not scope.used(x) and not used_later: - # TODO: This should be "Variable x with linear type ty is not - # used in {bb}". But for this we need a way to associate BBs - # with source locations. - raise GuppyError( - f"{leaf.describe} with linear type `{leaf.ty}` is " - "not used on all control-flow paths", - # Re-lookup defined_at in scope because we might have a - # more precise location - scope[x].defined_at, + # On the other hand, unused linear variables *must* be outputted + for place in scope.values(): + for leaf in leaf_places(place): + x = leaf.id + # Some values are just in scope because the type checker determined + # them as live in the first (less precises) dataflow analysis. It + # might be the case that x is actually not live when considering + # the second, more fine-grained, analysis based on places. + if x not in live_before_bb and x not in scope.vars: + continue + used_later = all(x in live_before[succ] for succ in bb.successors) + if leaf.ty.linear and not scope.used(x) and not used_later: + err = PlaceNotUsedError(scope[x].defined_at, leaf) + # If there are some paths that lead to a consumption, we can give + # a nicer error message by highlighting the branch that leads to + # the leak + if any(x in live_before[succ] for succ in bb.successors): + assert bb.branch_pred is not None + [left_succ, _] = bb.successors + err.add_sub_diagnostic( + PlaceNotUsedError.Branch( + bb.branch_pred, x in live_before[left_succ] + ) ) + err.add_sub_diagnostic(PlaceNotUsedError.Fix(None)) + raise GuppyError(err) def live_places_row(bb: BB, original_row: Row[Variable]) -> Row[Place]: """Construct a row of all places that are live at the start of a given BB. diff --git a/guppylang/checker/stmt_checker.py b/guppylang/checker/stmt_checker.py index b4f8db48..321ec3cf 100644 --- a/guppylang/checker/stmt_checker.py +++ b/guppylang/checker/stmt_checker.py @@ -148,9 +148,7 @@ def visit_AugAssign(self, node: ast.AugAssign) -> ast.stmt: def visit_Expr(self, node: ast.Expr) -> ast.stmt: # An expression statement where the return value is discarded - node.value, ty = self._synth_expr(node.value) - if ty.linear: - raise GuppyTypeError(f"Value with linear type `{ty}` is not used", node) + node.value, _ = self._synth_expr(node.value) return node def visit_Return(self, node: ast.Return) -> ast.stmt: diff --git a/guppylang/diagnostic.py b/guppylang/diagnostic.py index e076956f..0983a31b 100644 --- a/guppylang/diagnostic.py +++ b/guppylang/diagnostic.py @@ -1,7 +1,17 @@ +import string import textwrap -from dataclasses import dataclass, field, fields +from collections.abc import Mapping, Sequence +from dataclasses import dataclass, field from enum import Enum, auto -from typing import ClassVar, Final, Literal, Protocol, overload, runtime_checkable +from typing import ( + Any, + ClassVar, + Final, + Literal, + Protocol, + overload, + runtime_checkable, +) from typing_extensions import Self @@ -54,6 +64,9 @@ class SubDiagnostic(Protocol): #: Message that is printed if no span is provided. message: ClassVar[str | None] = None + #: The parent main diagnostic this sub-diagnostic is attached to. + _parent: "Diagnostic | None" = field(default=None, init=False) + def __post_init__(self) -> None: if self.span_label and self.span is None: raise InternalGuppyError("SubDiagnostic: Span label provided without span") @@ -78,8 +91,17 @@ def _render(self, s: str | None) -> str | None: """Helper method to fill in placeholder values in strings with fields of this diagnostic. """ - values = {f.name: getattr(self, f.name) for f in fields(self)} - return s.format(**values) if s is not None else None + + class CustomFormatter(string.Formatter): + def get_value( + _self, key: int | str, args: Sequence[Any], kwargs: Mapping[str, Any] + ) -> Any: + assert isinstance(key, str) + if hasattr(self, key): + return getattr(self, key) + return getattr(self._parent, key) + + return CustomFormatter().format(s) if s is not None else None @runtime_checkable @@ -114,6 +136,7 @@ def add_sub_diagnostic(self, sub: "SubDiagnostic") -> Self: raise InternalGuppyError( "Diagnostic: Cross-file sub-diagnostics are not supported" ) + object.__setattr__(sub, "_parent", self) self.children.append(sub) return self diff --git a/guppylang/nodes.py b/guppylang/nodes.py index ec916433..8b18ea47 100644 --- a/guppylang/nodes.py +++ b/guppylang/nodes.py @@ -70,6 +70,9 @@ class TensorCall(ast.expr): ) +AnyCall = LocalCall | GlobalCall | TensorCall + + class TypeApply(ast.expr): value: ast.expr inst: Inst @@ -116,12 +119,9 @@ class SubscriptAccessAndDrop(ast.expr): item: "Variable" item_expr: ast.expr getitem_expr: ast.expr + original_expr: ast.Subscript - _fields = ( - "item", - "item_expr", - "getitem_expr", - ) + _fields = ("item", "item_expr", "getitem_expr", "original_expr") class MakeIter(ast.expr): diff --git a/tests/diagnostics/snapshots/test_advanced_formatting.txt b/tests/diagnostics/snapshots/test_advanced_formatting.txt new file mode 100644 index 00000000..4d2bd537 --- /dev/null +++ b/tests/diagnostics/snapshots/test_advanced_formatting.txt @@ -0,0 +1,7 @@ +Error: Can't compare apples with oranges (at :1:0) + | +1 | apple == orange + | ^^^^^ This is an apple + | +1 | apple == orange + | ------ This is not an apple \ No newline at end of file diff --git a/tests/diagnostics/test_diagnostics_rendering.py b/tests/diagnostics/test_diagnostics_rendering.py index 90188cba..639a883e 100644 --- a/tests/diagnostics/test_diagnostics_rendering.py +++ b/tests/diagnostics/test_diagnostics_rendering.py @@ -96,6 +96,38 @@ class MySubDiagnostic(Note): run_test(source, diagnostic, snapshot, request) +def test_advanced_formatting(snapshot, request): + @dataclass(frozen=True) + class MyDiagnostic(Error): + title: ClassVar[str] = "Can't compare apples with oranges" + span_label: ClassVar[str] = "This is an {a}{pp}{le}" + a: str + + @property + def pp(self) -> str: + return "pp" + + @property + def le(self) -> str: + return "le" + + @dataclass(frozen=True) + class MySubDiagnostic(Note): + span_label: ClassVar[str] = "This is not an {a}{pp}{p}{le}" + p: str + + @property + def pp(self) -> str: + return "p" + + source = "apple == orange" + span_apple = Span(Loc(file, 1, 0), Loc(file, 1, 5)) + span_orange = Span(Loc(file, 1, 9), Loc(file, 1, 15)) + diagnostic = MyDiagnostic(span_apple, "a") + diagnostic.add_sub_diagnostic(MySubDiagnostic(span_orange, "p")) + run_test(source, diagnostic, snapshot, request) + + def test_long_label(snapshot, request): @dataclass(frozen=True) class MyDiagnostic(Error): diff --git a/tests/error/array_errors/subscript_after_use.err b/tests/error/array_errors/subscript_after_use.err index b234e2c0..458df5bb 100644 --- a/tests/error/array_errors/subscript_after_use.err +++ b/tests/error/array_errors/subscript_after_use.err @@ -1,7 +1,12 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:19) + | +16 | @guppy(module) +17 | def main(qs: array[qubit, 42] @owned) -> array[qubit, 42]: +18 | return foo(qs, qs[0]) + | ^^ Variable `qs` with linear type `array[qubit, 42]` cannot be + | borrowed ... + | +18 | return foo(qs, qs[0]) + | -- since it was already consumed here -16: @guppy(module) -17: def main(qs: array[qubit, 42] @owned) -> array[qubit, 42]: -18: return foo(qs, qs[0]) - ^^ -GuppyError: Variable `qs` with linear type `array[qubit, 42]` was already used (at 18:15) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/array_errors/subscript_drop.err b/tests/error/array_errors/subscript_drop.err index 448abbbc..c7398e0e 100644 --- a/tests/error/array_errors/subscript_drop.err +++ b/tests/error/array_errors/subscript_drop.err @@ -1,7 +1,14 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:11) + | +16 | @guppy(module) +17 | def main() -> qubit: +18 | return foo()[0] + | ^^^^^ Linear items of expression with type `array[qubit, 10]` are + | leaked ... + | +18 | return foo()[0] + | - since only this subscript is used -16: @guppy(module) -17: def main() -> qubit: -18: return foo()[0] - ^^^^^^^^ -GuppyTypeError: Remaining linear items with type `qubit` are not used +Help: Consider assigning this value to a local variable before subscripting it + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/array_errors/subscript_non_inout.err b/tests/error/array_errors/subscript_non_inout.err index f71f8125..0a35dce5 100644 --- a/tests/error/array_errors/subscript_non_inout.err +++ b/tests/error/array_errors/subscript_non_inout.err @@ -1,7 +1,11 @@ -Guppy compilation failed. Error in file $FILE:14 +Error: Subscript moved (at $FILE:14:8) + | +12 | @guppy(module) +13 | def main(qs: array[qubit, 42]) -> tuple[qubit, array[qubit, 42]]: +14 | q = qs[0] + | ^^^^^ Cannot move a subscript of `qs` with linear type + | `array[qubit, 42]` -12: @guppy(module) -13: def main(qs: array[qubit, 42]) -> tuple[qubit, array[qubit, 42]]: -14: q = qs[0] - ^^^^^ -GuppyError: Subscripting on expression with linear type `array[qubit, 42]` is not allowed in `@owned` position +Note: Subscripts on linear types are only allowed to be borrowed, not moved + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/array_errors/use_after_subscript.err b/tests/error/array_errors/use_after_subscript.err index 8ebdec96..d170f6b2 100644 --- a/tests/error/array_errors/use_after_subscript.err +++ b/tests/error/array_errors/use_after_subscript.err @@ -1,7 +1,12 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:15) + | +16 | @guppy(module) +17 | def main(qs: array[qubit, 42] @owned) -> array[qubit, 42]: +18 | return foo(qs[0], qs) + | ^^^^^ Variable `qs` with linear type `array[qubit, 42]` cannot be + | borrowed ... + | +18 | return foo(qs[0], qs) + | -- since it was already consumed here -16: @guppy(module) -17: def main(qs: array[qubit, 42] @owned) -> array[qubit, 42]: -18: return foo(qs[0], qs) - ^^^^^ -GuppyError: Variable `qs` with linear type `array[qubit, 42]` was already used (at 18:22) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/capture1.err b/tests/error/comprehension_errors/capture1.err index d5494927..3469936f 100644 --- a/tests/error/comprehension_errors/capture1.err +++ b/tests/error/comprehension_errors/capture1.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:13 +Error: Linearity violation (at $FILE:13:12) + | +11 | @guppy(module) +12 | def foo(xs: list[int], q: qubit @owned) -> list[qubit]: +13 | return [q for x in xs] + | ^ Variable `q` with linear type `qubit` would be moved + | multiple times when evaluating this + | comprehension -11: @guppy(module) -12: def foo(xs: list[int], q: qubit @owned) -> list[qubit]: -13: return [q for x in xs] - ^ -GuppyTypeError: Variable `q` with linear type `qubit` would be used multiple times when evaluating this comprehension +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/capture2.err b/tests/error/comprehension_errors/capture2.err index 3d0a6dea..dc9a56eb 100644 --- a/tests/error/comprehension_errors/capture2.err +++ b/tests/error/comprehension_errors/capture2.err @@ -1,7 +1,11 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:33) + | +16 | @guppy(module) +17 | def foo(xs: list[int], q: qubit @owned) -> list[int]: +18 | return [x for x in xs if bar(q)] + | ^ Variable `q` with linear type `qubit` would be consumed + | multiple times when + | evaluating this + | comprehension -16: @guppy(module) -17: def foo(xs: list[int], q: qubit @owned) -> list[int]: -18: return [x for x in xs if bar(q)] - ^ -GuppyTypeError: Variable `q` with linear type `qubit` would be used multiple times when evaluating this comprehension +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/capture3.err b/tests/error/comprehension_errors/capture3.err index fd0d42ad..46b68539 100644 --- a/tests/error/comprehension_errors/capture3.err +++ b/tests/error/comprehension_errors/capture3.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:12) + | +16 | @guppy(module) +17 | def foo(xs: list[int], s: MyStruct @owned) -> list[MyStruct]: +18 | return [s for x in xs] + | ^ Field `s.q` with linear type `qubit` would be moved + | multiple times when evaluating this + | comprehension -16: @guppy(module) -17: def foo(xs: list[int], s: MyStruct @owned) -> list[MyStruct]: -18: return [s for x in xs] - ^ -GuppyTypeError: Field `s.q` with linear type `qubit` would be used multiple times when evaluating this comprehension +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/capture4.err b/tests/error/comprehension_errors/capture4.err index f516e7ba..6f80ac54 100644 --- a/tests/error/comprehension_errors/capture4.err +++ b/tests/error/comprehension_errors/capture4.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:12) + | +16 | @guppy(module) +17 | def foo(xs: list[int], s: MyStruct @owned) -> list[qubit]: +18 | return [s.q for x in xs] + | ^^^ Field `s.q` with linear type `qubit` would be moved + | multiple times when evaluating this + | comprehension -16: @guppy(module) -17: def foo(xs: list[int], s: MyStruct @owned) -> list[qubit]: -18: return [s.q for x in xs] - ^^^ -GuppyTypeError: Field `s.q` with linear type `qubit` would be used multiple times when evaluating this comprehension +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/guarded1.err b/tests/error/comprehension_errors/guarded1.err index d1b16c92..36f40b3c 100644 --- a/tests/error/comprehension_errors/guarded1.err +++ b/tests/error/comprehension_errors/guarded1.err @@ -1,7 +1,11 @@ -Guppy compilation failed. Error in file $FILE:13 +Error: Linearity violation (at $FILE:13:21) + | +11 | @guppy(module) +12 | def foo(qs: list[tuple[bool, qubit]] @owned) -> list[qubit]: +13 | return [q for b, q in qs if b] + | ^ Variable `q` with linear type `qubit` may be leaked ... + | +13 | return [q for b, q in qs if b] + | - if this expression is `False` -11: @guppy(module) -12: def foo(qs: list[tuple[bool, qubit]] @owned) -> list[qubit]: -13: return [q for b, q in qs if b] - ^ -GuppyTypeError: Variable `q` with linear type `qubit` is not used on all control-flow paths of the list comprehension +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/guarded2.err b/tests/error/comprehension_errors/guarded2.err index 482814e6..26a02d87 100644 --- a/tests/error/comprehension_errors/guarded2.err +++ b/tests/error/comprehension_errors/guarded2.err @@ -1,7 +1,11 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:22) + | +16 | @guppy(module) +17 | def foo(qs: list[tuple[bool, qubit]] @owned) -> list[int]: +18 | return [42 for b, q in qs if b if bar(q)] + | ^ Variable `q` with linear type `qubit` may be leaked ... + | +18 | return [42 for b, q in qs if b if bar(q)] + | - if this expression is `False` -16: @guppy(module) -17: def foo(qs: list[tuple[bool, qubit]] @owned) -> list[int]: -18: return [42 for b, q in qs if b if bar(q)] - ^ -GuppyTypeError: Variable `q` with linear type `qubit` is not used on all control-flow paths of the list comprehension +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/guarded3.err b/tests/error/comprehension_errors/guarded3.err index 54933a77..f7751900 100644 --- a/tests/error/comprehension_errors/guarded3.err +++ b/tests/error/comprehension_errors/guarded3.err @@ -1,7 +1,11 @@ -Guppy compilation failed. Error in file $FILE:24 +Error: Linearity violation (at $FILE:24:21) + | +22 | @guppy(module) +23 | def foo(qs: list[MyStruct] @owned) -> list[qubit]: +24 | return [s.q2 for s in qs if bar(s.q1)] + | ^ Field `s.q2` with linear type `qubit` may be leaked ... + | +24 | return [s.q2 for s in qs if bar(s.q1)] + | --------- if this expression is `False` -22: @guppy(module) -23: def foo(qs: list[MyStruct] @owned) -> list[qubit]: -24: return [s.q2 for s in qs if bar(s.q1)] - ^ -GuppyTypeError: Field `s.q2` with linear type `qubit` is not used on all control-flow paths of the list comprehension +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/multi_use1.err b/tests/error/comprehension_errors/multi_use1.err index d5d69eb2..23fff47f 100644 --- a/tests/error/comprehension_errors/multi_use1.err +++ b/tests/error/comprehension_errors/multi_use1.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:13 +Error: Linearity violation (at $FILE:13:12) + | +11 | @guppy(module) +12 | def foo(qs: list[qubit] @owned, xs: list[int]) -> list[qubit]: +13 | return [q for q in qs for x in xs] + | ^ Variable `q` with linear type `qubit` would be moved + | multiple times when evaluating this + | comprehension -11: @guppy(module) -12: def foo(qs: list[qubit] @owned, xs: list[int]) -> list[qubit]: -13: return [q for q in qs for x in xs] - ^ -GuppyTypeError: Variable `q` with linear type `qubit` would be used multiple times when evaluating this comprehension +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/multi_use2.err b/tests/error/comprehension_errors/multi_use2.err index 190b5e1e..abf07cbe 100644 --- a/tests/error/comprehension_errors/multi_use2.err +++ b/tests/error/comprehension_errors/multi_use2.err @@ -1,7 +1,11 @@ -Guppy compilation failed. Error in file $FILE:13 +Error: Linearity violation (at $FILE:13:35) + | +11 | @guppy(module) +12 | def foo(qs: list[qubit] @owned, xs: list[int]) -> list[qubit]: +13 | return [q for x in xs for q in qs] + | ^^ Variable `qs` with linear type `list[qubit]` would be + | consumed multiple + | times when evaluating + | this comprehension -11: @guppy(module) -12: def foo(qs: list[qubit] @owned, xs: list[int]) -> list[qubit]: -13: return [q for x in xs for q in qs] - ^^ -GuppyTypeError: Variable `qs` with linear type `list[qubit]` would be used multiple times when evaluating this comprehension +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/multi_use3.err b/tests/error/comprehension_errors/multi_use3.err index 5f073292..243cf92f 100644 --- a/tests/error/comprehension_errors/multi_use3.err +++ b/tests/error/comprehension_errors/multi_use3.err @@ -1,7 +1,13 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:45) + | +16 | @guppy(module) +17 | def foo(qs: list[qubit] @owned, xs: list[int]) -> list[int]: +18 | return [x for q in qs for x in xs if bar(q)] + | ^ Variable `q` with linear type `qubit` would be consumed + | multiple + | times when + | evaluating + | this + | comprehension -16: @guppy(module) -17: def foo(qs: list[qubit] @owned, xs: list[int]) -> list[int]: -18: return [x for q in qs for x in xs if bar(q)] - ^ -GuppyTypeError: Variable `q` with linear type `qubit` would be used multiple times when evaluating this comprehension +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/not_used1.err b/tests/error/comprehension_errors/not_used1.err index 3eb2c80f..1eb44896 100644 --- a/tests/error/comprehension_errors/not_used1.err +++ b/tests/error/comprehension_errors/not_used1.err @@ -1,7 +1,8 @@ -Guppy compilation failed. Error in file $FILE:13 +Error: Linearity violation (at $FILE:13:19) + | +11 | @guppy(module) +12 | def foo(qs: list[qubit] @owned) -> list[int]: +13 | return [42 for q in qs] + | ^ Variable `q` with linear type `qubit` is leaked -11: @guppy(module) -12: def foo(qs: list[qubit] @owned) -> list[int]: -13: return [42 for q in qs] - ^ -GuppyTypeError: Variable `q` with linear type `qubit` is not used +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/not_used2.err b/tests/error/comprehension_errors/not_used2.err index e7ffd446..a0e69fed 100644 --- a/tests/error/comprehension_errors/not_used2.err +++ b/tests/error/comprehension_errors/not_used2.err @@ -1,7 +1,8 @@ -Guppy compilation failed. Error in file $FILE:19 +Error: Linearity violation (at $FILE:19:21) + | +17 | @guppy(module) +18 | def foo(ss: list[MyStruct] @owned) -> list[qubit]: +19 | return [s.q1 for s in ss] + | ^ Field `s.q2` with linear type `qubit` is leaked -17: @guppy(module) -18: def foo(ss: list[MyStruct] @owned) -> list[qubit]: -19: return [s.q1 for s in ss] - ^ -GuppyTypeError: Field `s.q2` with linear type `qubit` is not used +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/pattern_override1.err b/tests/error/comprehension_errors/pattern_override1.err index 58a8b594..3e110617 100644 --- a/tests/error/comprehension_errors/pattern_override1.err +++ b/tests/error/comprehension_errors/pattern_override1.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:13 +Error: Linearity violation (at $FILE:13:18) + | +11 | @guppy(module) +12 | def foo(qs: list[tuple[qubit, qubit]] @owned) -> list[qubit]: +13 | return [q for q, q in qs] + | ^ Variable `q` with linear type `qubit` is leaked -11: @guppy(module) -12: def foo(qs: list[tuple[qubit, qubit]] @owned) -> list[qubit]: -13: return [q for q, q in qs] - ^ -GuppyError: Variable `q` with linear type `qubit` is not used +Help: Make sure that `q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/pattern_override2.err b/tests/error/comprehension_errors/pattern_override2.err index acf2e0cd..15f4f509 100644 --- a/tests/error/comprehension_errors/pattern_override2.err +++ b/tests/error/comprehension_errors/pattern_override2.err @@ -1,7 +1,8 @@ -Guppy compilation failed. Error in file $FILE:13 +Error: Linearity violation (at $FILE:13:18) + | +11 | @guppy(module) +12 | def foo(qs: list[qubit] @owned, xs: list[int]) -> list[int]: +13 | return [q for q in qs for q in xs] + | ^ Variable `q` with linear type `qubit` is leaked -11: @guppy(module) -12: def foo(qs: list[qubit] @owned, xs: list[int]) -> list[int]: -13: return [q for q in qs for q in xs] - ^ -GuppyTypeError: Variable `q` with linear type `qubit` is not used +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/used_twice1.err b/tests/error/comprehension_errors/used_twice1.err index 142b7f85..6cb18ca1 100644 --- a/tests/error/comprehension_errors/used_twice1.err +++ b/tests/error/comprehension_errors/used_twice1.err @@ -1,7 +1,11 @@ -Guppy compilation failed. Error in file $FILE:13 +Error: Linearity violation (at $FILE:13:16) + | +11 | @guppy(module) +12 | def foo(qs: list[qubit] @owned) -> list[tuple[qubit, qubit]]: +13 | return [(q, q) for q in qs] + | ^ Variable `q` with linear type `qubit` cannot be moved ... + | +13 | return [(q, q) for q in qs] + | - since it was already moved here -11: @guppy(module) -12: def foo(qs: list[qubit] @owned) -> list[tuple[qubit, qubit]]: -13: return [(q, q) for q in qs] - ^ -GuppyError: Variable `q` with linear type `qubit` was already used (at 13:13) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/used_twice2.err b/tests/error/comprehension_errors/used_twice2.err index 7d8d0237..d58543db 100644 --- a/tests/error/comprehension_errors/used_twice2.err +++ b/tests/error/comprehension_errors/used_twice2.err @@ -1,7 +1,11 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:12) + | +16 | @guppy(module) +17 | def foo(qs: list[qubit] @owned) -> list[qubit]: +18 | return [q for q in qs if bar(q)] + | ^ Variable `q` with linear type `qubit` cannot be moved ... + | +18 | return [q for q in qs if bar(q)] + | - since it was already consumed here -16: @guppy(module) -17: def foo(qs: list[qubit] @owned) -> list[qubit]: -18: return [q for q in qs if bar(q)] - ^ -GuppyError: Variable `q` with linear type `qubit` was already used (at 18:33) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/comprehension_errors/used_twice3.err b/tests/error/comprehension_errors/used_twice3.err index 0cc3f964..0e253e52 100644 --- a/tests/error/comprehension_errors/used_twice3.err +++ b/tests/error/comprehension_errors/used_twice3.err @@ -1,7 +1,11 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:12) + | +16 | @guppy(module) +17 | def foo(qs: list[qubit] @owned) -> list[qubit]: +18 | return [q for q in qs for x in bar(q)] + | ^ Variable `q` with linear type `qubit` cannot be moved ... + | +18 | return [q for q in qs for x in bar(q)] + | - since it was already consumed here -16: @guppy(module) -17: def foo(qs: list[qubit] @owned) -> list[qubit]: -18: return [q for q in qs for x in bar(q)] - ^ -GuppyError: Variable `q` with linear type `qubit` was already used (at 18:39) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/inout_errors/already_used.err b/tests/error/inout_errors/already_used.err index 22f6506b..470df68c 100644 --- a/tests/error/inout_errors/already_used.err +++ b/tests/error/inout_errors/already_used.err @@ -1,7 +1,12 @@ -Guppy compilation failed. Error in file $FILE:21 +Error: Linearity violation (at $FILE:21:8) + | +19 | def test(q: qubit @owned) -> None: +20 | use(q) +21 | foo(q) + | ^ Variable `q` with linear type `qubit` cannot be borrowed + | ... + | +20 | use(q) + | - since it was already consumed here -19: def test(q: qubit @owned) -> None: -20: use(q) -21: foo(q) - ^ -GuppyError: Variable `q` with linear type `qubit` was already used (at 20:8) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/inout_errors/drop_after_call.err b/tests/error/inout_errors/drop_after_call.err index 9a3c6aa7..ad4ad6ed 100644 --- a/tests/error/inout_errors/drop_after_call.err +++ b/tests/error/inout_errors/drop_after_call.err @@ -1,7 +1,12 @@ -Guppy compilation failed. Error in file $FILE:15 +Error: Linearity violation (at $FILE:15:7) + | +13 | @guppy(module) +14 | def test() -> None: +15 | foo(qubit()) + | ^^^^^^^ Value with linear type `qubit` would be leaked after `foo` + | returns -13: @guppy(module) -14: def test() -> None: -15: foo(qubit()) - ^^^^^^^ -GuppyError: Borrowed argument with linear type `qubit` would be dropped after this function call. Consider assigning the expression to a local variable before passing it to the function. +Help: Consider assigning the value to a local variable before passing it to +`foo` + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/inout_errors/moved.err b/tests/error/inout_errors/moved.err index 8c22bff4..962c4484 100644 --- a/tests/error/inout_errors/moved.err +++ b/tests/error/inout_errors/moved.err @@ -1,7 +1,13 @@ -Guppy compilation failed. Error in file $FILE:21 +Error: Not owned (at $FILE:21:8) + | +19 | def test(q: qubit) -> None: +20 | foo(q) +21 | use(q) + | ^ Function `use` wants to take ownership of this argument, + | but `test` doesn't own `q` + | +19 | def test(q: qubit) -> None: + | -------- Argument `q` is only borrowed. Consider taking ownership: + | `q: qubit @owned` -19: def test(q: qubit) -> None: -20: foo(q) -21: use(q) - ^ -GuppyError: Variable `q` may not be used in an `@owned` position since it isn't owned. Consider adding a `@owned` annotation to get ownership of the value. +Guppy compilation failed due to 1 previous error diff --git a/tests/error/inout_errors/moved_assign.err b/tests/error/inout_errors/moved_assign.err index 1ef88bf8..f2c78aa0 100644 --- a/tests/error/inout_errors/moved_assign.err +++ b/tests/error/inout_errors/moved_assign.err @@ -1,7 +1,12 @@ -Guppy compilation failed. Error in file $FILE:11 +Error: Not owned (at $FILE:11:8) + | + 9 | @guppy(module) +10 | def test(q: qubit) -> qubit: +11 | r = q + | ^ Cannot move `q` since `test` doesn't own it + | +10 | def test(q: qubit) -> qubit: + | -------- Argument `q` is only borrowed. Consider taking ownership: + | `q: qubit @owned` -9: @guppy(module) -10: def test(q: qubit) -> qubit: -11: r = q - ^ -GuppyError: Variable `q` may not be used in an `@owned` position since it isn't owned. Consider adding a `@owned` annotation to get ownership of the value. +Guppy compilation failed due to 1 previous error diff --git a/tests/error/inout_errors/moved_if.err b/tests/error/inout_errors/moved_if.err index 42c6c8c4..007da48e 100644 --- a/tests/error/inout_errors/moved_if.err +++ b/tests/error/inout_errors/moved_if.err @@ -1,7 +1,13 @@ -Guppy compilation failed. Error in file $FILE:17 +Error: Not owned (at $FILE:17:12) + | +15 | def test(q: qubit, b: bool) -> None: +16 | if b: +17 | use(q) + | ^ Function `use` wants to take ownership of this argument, + | but `test` doesn't own `q` + | +15 | def test(q: qubit, b: bool) -> None: + | -------- Argument `q` is only borrowed. Consider taking ownership: + | `q: qubit @owned` -15: def test(q: qubit, b: bool) -> None: -16: if b: -17: use(q) - ^ -GuppyError: Variable `q` may not be used in an `@owned` position since it isn't owned. Consider adding a `@owned` annotation to get ownership of the value. +Guppy compilation failed due to 1 previous error diff --git a/tests/error/inout_errors/moved_out.err b/tests/error/inout_errors/moved_out.err index c376dd5b..0aff5a67 100644 --- a/tests/error/inout_errors/moved_out.err +++ b/tests/error/inout_errors/moved_out.err @@ -1,6 +1,14 @@ -Guppy compilation failed. Error in file $FILE:20 +Error: Linearity violation (at $FILE:20:9) + | +18 | +19 | @guppy(module) +20 | def test(s: MyStruct) -> None: + | ^^^^^^^^^^^ Borrowed argument s cannot be returned to the caller ... + | +21 | use(s.q) + | --- since `s.q` with linear type `qubit` was already consumed + | here -18: @guppy(module) -19: def test(s: MyStruct) -> None: - ^^^^^^^^^^^ -GuppyError: Borrowed argument `s` cannot be returned to the caller since `s.q` is used at 21:8. Consider writing a value back into `s.q` before returning. +Help: Consider writing a value back into `s.q` before returning + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/inout_errors/moved_out_if.err b/tests/error/inout_errors/moved_out_if.err index e8fe017b..202f2d35 100644 --- a/tests/error/inout_errors/moved_out_if.err +++ b/tests/error/inout_errors/moved_out_if.err @@ -1,6 +1,14 @@ -Guppy compilation failed. Error in file $FILE:20 +Error: Linearity violation (at $FILE:20:9) + | +18 | +19 | @guppy(module) +20 | def test(s: MyStruct, b: bool) -> None: + | ^^^^^^^^^^^ Borrowed argument s cannot be returned to the caller ... + | +22 | use(s.q) + | --- since `s.q` with linear type `qubit` was already consumed + | here -18: @guppy(module) -19: def test(s: MyStruct, b: bool) -> None: - ^^^^^^^^^^^ -GuppyError: Borrowed argument `s` cannot be returned to the caller since `s.q` is used at 22:12. Consider writing a value back into `s.q` before returning. +Help: Consider writing a value back into `s.q` before returning + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/inout_errors/nested_call_right_to_left.err b/tests/error/inout_errors/nested_call_right_to_left.err index 1d58cccc..2a64baf8 100644 --- a/tests/error/inout_errors/nested_call_right_to_left.err +++ b/tests/error/inout_errors/nested_call_right_to_left.err @@ -1,7 +1,12 @@ -Guppy compilation failed. Error in file $FILE:17 +Error: Linearity violation (at $FILE:17:22) + | +15 | def test(q: qubit @owned) -> tuple[int, qubit]: +16 | # This doesn't work since arguments are evaluated from left to right +17 | return foo(q, foo(q, foo(q, 0))), q + | ^ Variable `q` with linear type `qubit` cannot be borrowed + | ... + | +17 | return foo(q, foo(q, foo(q, 0))), q + | - since it was already borrowed here -15: def test(q: qubit @owned) -> tuple[int, qubit]: -16: # This doesn't work since arguments are evaluated from left to right -17: return foo(q, foo(q, foo(q, 0))), q - ^ -GuppyError: Variable `q` with linear type `qubit` was already used (at 17:15) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/inout_errors/override_after_call.err b/tests/error/inout_errors/override_after_call.err index 710bee33..3b7b5290 100644 --- a/tests/error/inout_errors/override_after_call.err +++ b/tests/error/inout_errors/override_after_call.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:16 +Error: Linearity violation (at $FILE:16:13) + | +14 | @guppy(module) +15 | def test(q1: qubit @owned, q2: qubit @owned) -> tuple[qubit, qubit]: +16 | q1 = foo(q1, q2) + | ^^ Variable `q1` with linear type `qubit` is leaked -14: @guppy(module) -15: def test(q1: qubit @owned, q2: qubit @owned) -> tuple[qubit, qubit]: -16: q1 = foo(q1, q2) - ^^ -GuppyError: Variable `q1` with linear type `qubit` is not used +Help: Make sure that `q1` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/inout_errors/shadow.err b/tests/error/inout_errors/shadow.err index c94a0743..74cb9032 100644 --- a/tests/error/inout_errors/shadow.err +++ b/tests/error/inout_errors/shadow.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:12 +Error: Borrow shadowed (at $FILE:12:4) + | +10 | @guppy(module) +11 | def test(q: qubit) -> None: +12 | q = qubit() + | ^ Assignment shadows borrowed argument `q` -10: @guppy(module) -11: def test(q: qubit) -> None: -12: q = qubit() - ^ -GuppyError: Assignment shadows borrowed argument `q`. Consider assigning to a different name. +Help: Consider assigning to a different name + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/inout_errors/shadow_if.err b/tests/error/inout_errors/shadow_if.err index 7bb390a5..0dac24da 100644 --- a/tests/error/inout_errors/shadow_if.err +++ b/tests/error/inout_errors/shadow_if.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:13 +Error: Borrow shadowed (at $FILE:13:8) + | +11 | def test(q: qubit, b: bool) -> None: +12 | if b: +13 | q = qubit() + | ^ Assignment shadows borrowed argument `q` -11: def test(q: qubit, b: bool) -> None: -12: if b: -13: q = qubit() - ^ -GuppyError: Assignment shadows borrowed argument `q`. Consider assigning to a different name. +Help: Consider assigning to a different name + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/inout_errors/struct_constructor.err b/tests/error/inout_errors/struct_constructor.err index 9daecd30..c6d7a7fc 100644 --- a/tests/error/inout_errors/struct_constructor.err +++ b/tests/error/inout_errors/struct_constructor.err @@ -1,7 +1,13 @@ -Guppy compilation failed. Error in file $FILE:15 +Error: Not owned (at $FILE:15:20) + | +13 | @guppy(module) +14 | def test(q: qubit) -> MyStruct: +15 | return MyStruct(q) + | ^ Function `__new__` wants to take ownership of this + | argument, but `test` doesn't own `q` + | +14 | def test(q: qubit) -> MyStruct: + | -------- Argument `q` is only borrowed. Consider taking ownership: + | `q: qubit @owned` -13: @guppy(module) -14: def test(q: qubit) -> MyStruct: -15: return MyStruct(q) - ^ -GuppyError: Variable `q` may not be used in an `@owned` position since it isn't owned. Consider adding a `@owned` annotation to get ownership of the value. +Guppy compilation failed due to 1 previous error diff --git a/tests/error/inout_errors/unused_after_call.err b/tests/error/inout_errors/unused_after_call.err index 04e57abe..e2bc014c 100644 --- a/tests/error/inout_errors/unused_after_call.err +++ b/tests/error/inout_errors/unused_after_call.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:16 +Error: Linearity violation (at $FILE:16:7) + | +14 | @guppy(module) +15 | def test(q: qubit @owned) -> None: +16 | foo(q) + | ^ Variable `q` with linear type `qubit` is leaked -14: @guppy(module) -15: def test(q: qubit @owned) -> None: -16: foo(q) - ^ -GuppyError: Variable `q` with linear type `qubit` is not used on all control-flow paths +Help: Make sure that `q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/branch_use.err b/tests/error/linear_errors/branch_use.err index d37d2b32..18bf91fa 100644 --- a/tests/error/linear_errors/branch_use.err +++ b/tests/error/linear_errors/branch_use.err @@ -1,7 +1,13 @@ -Guppy compilation failed. Error in file $FILE:24 +Error: Linearity violation (at $FILE:24:4) + | +22 | @guppy(module) +23 | def foo(b: bool) -> bool: +24 | q = new_qubit() + | ^ Variable `q` with linear type `qubit` may be leaked ... + | +25 | if b: + | - if this expression is `False` -22: @guppy(module) -23: def foo(b: bool) -> bool: -24: q = new_qubit() - ^ -GuppyError: Variable `q` with linear type `qubit` is not used on all control-flow paths +Help: Make sure that `q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/branch_use_field1.err b/tests/error/linear_errors/branch_use_field1.err index 4e29eb90..e2247936 100644 --- a/tests/error/linear_errors/branch_use_field1.err +++ b/tests/error/linear_errors/branch_use_field1.err @@ -1,7 +1,13 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:4) + | +16 | @guppy(module) +17 | def foo(b: bool) -> bool: +18 | s = MyStruct(qubit()) + | ^ Field `s.q` with linear type `qubit` may be leaked ... + | +19 | if b: + | - if this expression is `False` -16: @guppy(module) -17: def foo(b: bool) -> bool: -18: s = MyStruct(qubit()) - ^ -GuppyError: Field `s.q` with linear type `qubit` is not used on all control-flow paths +Help: Make sure that `s.q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/branch_use_field2.err b/tests/error/linear_errors/branch_use_field2.err index e855382f..985f1de1 100644 --- a/tests/error/linear_errors/branch_use_field2.err +++ b/tests/error/linear_errors/branch_use_field2.err @@ -1,7 +1,12 @@ -Guppy compilation failed. Error in file $FILE:22 +Error: Linearity violation (at $FILE:22:11) + | +20 | if b: +21 | measure(s.q2) +22 | return s + | ^ Field `s.q2` with linear type `qubit` cannot be returned + | ... + | +21 | measure(s.q2) + | ---- since it was already consumed here -20: if b: -21: measure(s.q2) -22: return s - ^ -GuppyError: Field `s.q2` with linear type `qubit` was already used (at 21:16) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/break_unused.err b/tests/error/linear_errors/break_unused.err index d3bbe74e..49abb207 100644 --- a/tests/error/linear_errors/break_unused.err +++ b/tests/error/linear_errors/break_unused.err @@ -1,7 +1,13 @@ -Guppy compilation failed. Error in file $FILE:26 +Error: Linearity violation (at $FILE:26:8) + | +24 | b = False +25 | while True: +26 | q = new_qubit() + | ^ Variable `q` with linear type `qubit` may be leaked ... + | +27 | if i == 0: + | ------ if this expression is `True` -24: b = False -25: while True: -26: q = new_qubit() - ^ -GuppyError: Variable `q` with linear type `qubit` is not used on all control-flow paths +Help: Make sure that `q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/call_drop_field.err b/tests/error/linear_errors/call_drop_field.err index 6d5520aa..5d2f10fa 100644 --- a/tests/error/linear_errors/call_drop_field.err +++ b/tests/error/linear_errors/call_drop_field.err @@ -1,7 +1,12 @@ -Guppy compilation failed. Error in file $FILE:24 +Error: Linearity violation (at $FILE:24:11) + | +22 | @guppy(module) +23 | def bar() -> qubit: +24 | return foo().q1 + | ^^^^^ Linear field `q2` of expression with type `MyStruct` is + | leaked -22: @guppy(module) -23: def bar() -> qubit: -24: return foo().q1 - ^^^^^ -GuppyTypeError: Linear field `q2` of expression with type `MyStruct` is not used +Help: Consider assigning this value to a local variable before accessing the +field `q1` + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/continue_unused.err b/tests/error/linear_errors/continue_unused.err index ca69e2ec..311fac58 100644 --- a/tests/error/linear_errors/continue_unused.err +++ b/tests/error/linear_errors/continue_unused.err @@ -1,7 +1,13 @@ -Guppy compilation failed. Error in file $FILE:26 +Error: Linearity violation (at $FILE:26:8) + | +24 | b = False +25 | while i > 0: +26 | q = new_qubit() + | ^ Variable `q` with linear type `qubit` may be leaked ... + | +27 | if i % 10 == 0: + | ----------- if this expression is `True` -24: b = False -25: while i > 0: -26: q = new_qubit() - ^ -GuppyError: Variable `q` with linear type `qubit` is not used on all control-flow paths +Help: Make sure that `q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/copy_qubit.err b/tests/error/linear_errors/copy_qubit.err index 19fb3417..ef7bd8e5 100644 --- a/tests/error/linear_errors/copy_qubit.err +++ b/tests/error/linear_errors/copy_qubit.err @@ -1,7 +1,12 @@ -Guppy compilation failed. Error in file $FILE:14 +Error: Linearity violation (at $FILE:14:14) + | +12 | @guppy(module) +13 | def foo(q: qubit @owned) -> tuple[qubit, qubit]: +14 | return q, q + | ^ Variable `q` with linear type `qubit` cannot be returned + | ... + | +14 | return q, q + | - since it was already returned here -12: @guppy(module) -13: def foo(q: qubit @owned) -> tuple[qubit, qubit]: -14: return q, q - ^ -GuppyError: Variable `q` with linear type `qubit` was already used (at 14:11) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/field_copy1.err b/tests/error/linear_errors/field_copy1.err index e19ceaf9..d3da9d5d 100644 --- a/tests/error/linear_errors/field_copy1.err +++ b/tests/error/linear_errors/field_copy1.err @@ -1,7 +1,11 @@ -Guppy compilation failed. Error in file $FILE:20 +Error: Linearity violation (at $FILE:20:11) + | +18 | def foo(s: MyStruct @owned) -> tuple[qubit, qubit]: +19 | t = s +20 | return s.q, t.q + | ^^^ Field `s.q` with linear type `qubit` cannot be returned ... + | +19 | t = s + | - since it was already moved here -18: def foo(s: MyStruct @owned) -> tuple[qubit, qubit]: -19: t = s -20: return s.q, t.q - ^^^ -GuppyError: Field `s.q` with linear type `qubit` was already used (at 19:8) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/field_copy2.err b/tests/error/linear_errors/field_copy2.err index 5980884e..33d2ee5a 100644 --- a/tests/error/linear_errors/field_copy2.err +++ b/tests/error/linear_errors/field_copy2.err @@ -1,7 +1,11 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:14) + | +16 | @guppy(module) +17 | def foo(s: MyStruct @owned) -> tuple[MyStruct, qubit]: +18 | return s, s.q + | ^^^ Field `s.q` with linear type `qubit` cannot be returned ... + | +18 | return s, s.q + | - since it was already returned here -16: @guppy(module) -17: def foo(s: MyStruct @owned) -> tuple[MyStruct, qubit]: -18: return s, s.q - ^^^ -GuppyError: Field `s.q` with linear type `qubit` was already used (at 18:11) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/field_copy3.err b/tests/error/linear_errors/field_copy3.err index 43ee1108..cadfc23d 100644 --- a/tests/error/linear_errors/field_copy3.err +++ b/tests/error/linear_errors/field_copy3.err @@ -1,7 +1,11 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:16) + | +16 | @guppy(module) +17 | def foo(s: MyStruct @owned) -> tuple[qubit, MyStruct]: +18 | return s.q, s + | ^ Field `s.q` with linear type `qubit` cannot be returned ... + | +18 | return s.q, s + | --- since it was already returned here -16: @guppy(module) -17: def foo(s: MyStruct @owned) -> tuple[qubit, MyStruct]: -18: return s.q, s - ^ -GuppyError: Field `s.q` with linear type `qubit` was already used (at 18:11) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/field_copy_nested1.err b/tests/error/linear_errors/field_copy_nested1.err index 29998ac9..17df3d4d 100644 --- a/tests/error/linear_errors/field_copy_nested1.err +++ b/tests/error/linear_errors/field_copy_nested1.err @@ -1,7 +1,12 @@ -Guppy compilation failed. Error in file $FILE:27 +Error: Linearity violation (at $FILE:27:11) + | +25 | measure(s.q) +26 | s.q = s.x.q +27 | return s + | ^ Field `s.x.q` with linear type `qubit` cannot be returned + | ... + | +26 | s.q = s.x.q + | ----- since it was already moved here -25: measure(s.q) -26: s.q = s.x.q -27: return s - ^ -GuppyError: Field `s.x.q` with linear type `qubit` was already used (at 26:10) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/field_copy_nested2.err b/tests/error/linear_errors/field_copy_nested2.err index 0a87bbea..622c5929 100644 --- a/tests/error/linear_errors/field_copy_nested2.err +++ b/tests/error/linear_errors/field_copy_nested2.err @@ -1,7 +1,12 @@ -Guppy compilation failed. Error in file $FILE:26 +Error: Linearity violation (at $FILE:26:21) + | +24 | def foo(s: MyStruct1 @owned) -> MyStruct1: +25 | measure(s.x.q1) +26 | return MyStruct1(s.x) + | ^^^ Field `s.x.q1` with linear type `qubit` cannot be consumed + | ... + | +25 | measure(s.x.q1) + | ------ since it was already consumed here -24: def foo(s: MyStruct1 @owned) -> MyStruct1: -25: measure(s.x.q1) -26: return MyStruct1(s.x) - ^^^ -GuppyError: Field `s.x.q1` with linear type `qubit` was already used (at 25:12) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/field_copy_nested3.err b/tests/error/linear_errors/field_copy_nested3.err index 27fa435b..8143d2a6 100644 --- a/tests/error/linear_errors/field_copy_nested3.err +++ b/tests/error/linear_errors/field_copy_nested3.err @@ -1,7 +1,12 @@ -Guppy compilation failed. Error in file $FILE:29 +Error: Linearity violation (at $FILE:29:11) + | +27 | def foo(s: MyStruct1 @owned) -> qubit: +28 | use(s.x) +29 | return s.x.q + | ^^^^^ Field `s.x.q` with linear type `qubit` cannot be returned + | ... + | +28 | use(s.x) + | --- since it was already consumed here -27: def foo(s: MyStruct1 @owned) -> qubit: -28: use(s.x) -29: return s.x.q - ^^^^^ -GuppyError: Field `s.x.q` with linear type `qubit` was already used (at 28:8) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/field_copy_nested4.err b/tests/error/linear_errors/field_copy_nested4.err index 8f2f86f2..04949169 100644 --- a/tests/error/linear_errors/field_copy_nested4.err +++ b/tests/error/linear_errors/field_copy_nested4.err @@ -1,7 +1,12 @@ -Guppy compilation failed. Error in file $FILE:29 +Error: Linearity violation (at $FILE:29:11) + | +27 | def foo(s: MyStruct1 @owned) -> MyStruct1: +28 | use(s) +29 | return s + | ^ Field `s.x.q` with linear type `qubit` cannot be returned + | ... + | +28 | use(s) + | - since it was already consumed here -27: def foo(s: MyStruct1 @owned) -> MyStruct1: -28: use(s) -29: return s - ^ -GuppyError: Field `s.x.q` with linear type `qubit` was already used (at 28:8) +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/if_both_unused.err b/tests/error/linear_errors/if_both_unused.err index e0291646..da4794d6 100644 --- a/tests/error/linear_errors/if_both_unused.err +++ b/tests/error/linear_errors/if_both_unused.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:19 +Error: Linearity violation (at $FILE:19:8) + | +17 | def foo(b: bool) -> int: +18 | if b: +19 | q = new_qubit() + | ^ Variable `q` with linear type `qubit` is leaked -17: def foo(b: bool) -> int: -18: if b: -19: q = new_qubit() - ^ -GuppyError: Variable `q` with linear type `qubit` is not used on all control-flow paths +Help: Make sure that `q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/if_both_unused_field.err b/tests/error/linear_errors/if_both_unused_field.err index 8fd1b028..cd11e971 100644 --- a/tests/error/linear_errors/if_both_unused_field.err +++ b/tests/error/linear_errors/if_both_unused_field.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:19 +Error: Linearity violation (at $FILE:19:8) + | +17 | def foo(b: bool) -> int: +18 | if b: +19 | s = MyStruct(qubit()) + | ^ Field `s.q` with linear type `qubit` is leaked -17: def foo(b: bool) -> int: -18: if b: -19: s = MyStruct(qubit()) - ^ -GuppyError: Field `s.q` with linear type `qubit` is not used on all control-flow paths +Help: Make sure that `s.q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/if_both_unused_reassign.err b/tests/error/linear_errors/if_both_unused_reassign.err index c96b2c83..911f98dc 100644 --- a/tests/error/linear_errors/if_both_unused_reassign.err +++ b/tests/error/linear_errors/if_both_unused_reassign.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:19 +Error: Linearity violation (at $FILE:19:8) + | +17 | def foo(b: bool) -> qubit: +18 | if b: +19 | q = new_qubit() + | ^ Variable `q` with linear type `qubit` is leaked -17: def foo(b: bool) -> qubit: -18: if b: -19: q = new_qubit() - ^ -GuppyError: Variable `q` with linear type `qubit` is not used on all control-flow paths +Help: Make sure that `q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/if_both_unused_reassign_field.err b/tests/error/linear_errors/if_both_unused_reassign_field.err index 17fc50a5..bd53e9e3 100644 --- a/tests/error/linear_errors/if_both_unused_reassign_field.err +++ b/tests/error/linear_errors/if_both_unused_reassign_field.err @@ -1,6 +1,10 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:17) + | +16 | +17 | @guppy(module) +18 | def foo(b: bool, s: MyStruct @owned) -> MyStruct: + | ^^^^^^^^^^^^^^^^^^ Field `s.q` with linear type `qubit` is leaked -16: @guppy(module) -17: def foo(b: bool, s: MyStruct @owned) -> MyStruct: - ^^^^^^^^^^^^^^^^^^ -GuppyError: Field `s.q` with linear type `qubit` is not used on all control-flow paths +Help: Make sure that `s.q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/method_capture.err b/tests/error/linear_errors/method_capture.err index 30a4c538..378d7f2e 100644 --- a/tests/error/linear_errors/method_capture.err +++ b/tests/error/linear_errors/method_capture.err @@ -1,7 +1,13 @@ -Guppy compilation failed. Error in file $FILE:23 +Error: Linearity violation (at $FILE:23:8) + | +21 | @guppy(module) +22 | def foo(s: Struct @owned) -> Struct: +23 | f = s.foo + | ^^^^^ This expression implicitly constructs a closure that + | captures a linear value + | +23 | f = s.foo + | - This expression with linear type `Struct` is implicitly + | captured -21: @guppy(module) -22: def foo(s: Struct @owned) -> Struct: -23: f = s.foo - ^^^^^ -GuppyError: Capturing a value with linear type `Struct` in a closure is not allowed. Try calling the function directly instead of using it as a higher-order value. +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/reassign_unused.err b/tests/error/linear_errors/reassign_unused.err index b1d87e94..b352370b 100644 --- a/tests/error/linear_errors/reassign_unused.err +++ b/tests/error/linear_errors/reassign_unused.err @@ -1,6 +1,10 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:8) + | +16 | +17 | @guppy(module) +18 | def foo(q: qubit @owned) -> qubit: + | ^^^^^^^^^^^^^^^ Variable `q` with linear type `qubit` is leaked -16: @guppy(module) -17: def foo(q: qubit @owned) -> qubit: - ^^^^^^^^^^^^^^^ -GuppyError: Variable `q` with linear type `qubit` is not used +Help: Make sure that `q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/reassign_unused_field.err b/tests/error/linear_errors/reassign_unused_field.err index 367b703b..3f3ffb91 100644 --- a/tests/error/linear_errors/reassign_unused_field.err +++ b/tests/error/linear_errors/reassign_unused_field.err @@ -1,6 +1,10 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:8) + | +16 | +17 | @guppy(module) +18 | def foo(s: MyStruct @owned) -> MyStruct: + | ^^^^^^^^^^^^^^^^^^ Field `s.q` with linear type `qubit` is leaked -16: @guppy(module) -17: def foo(s: MyStruct @owned) -> MyStruct: - ^^^^^^^^^^^^^^^^^^ -GuppyError: Field `s.q` with linear type `qubit` is not used +Help: Make sure that `s.q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/reassign_unused_tuple.err b/tests/error/linear_errors/reassign_unused_tuple.err index 67843132..f58f3b21 100644 --- a/tests/error/linear_errors/reassign_unused_tuple.err +++ b/tests/error/linear_errors/reassign_unused_tuple.err @@ -1,6 +1,10 @@ -Guppy compilation failed. Error in file $FILE:18 +Error: Linearity violation (at $FILE:18:8) + | +16 | +17 | @guppy(module) +18 | def foo(q: qubit @owned) -> tuple[qubit, qubit]: + | ^^^^^^^^^^^^^^^ Variable `q` with linear type `qubit` is leaked -16: @guppy(module) -17: def foo(q: qubit @owned) -> tuple[qubit, qubit]: - ^^^^^^^^^^^^^^^ -GuppyError: Variable `q` with linear type `qubit` is not used +Help: Make sure that `q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/unused.err b/tests/error/linear_errors/unused.err index dc1e47a8..b55f5b54 100644 --- a/tests/error/linear_errors/unused.err +++ b/tests/error/linear_errors/unused.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:14 +Error: Linearity violation (at $FILE:14:4) + | +12 | @guppy(module) +13 | def foo(q: qubit @owned) -> int: +14 | x = q + | ^ Variable `x` with linear type `qubit` is leaked -12: @guppy(module) -13: def foo(q: qubit @owned) -> int: -14: x = q - ^ -GuppyError: Variable `x` with linear type `qubit` is not used on all control-flow paths +Help: Make sure that `x` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/unused_expr.err b/tests/error/linear_errors/unused_expr.err index c43033b4..8e1f2ac0 100644 --- a/tests/error/linear_errors/unused_expr.err +++ b/tests/error/linear_errors/unused_expr.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:13 +Error: Linearity violation (at $FILE:13:4) + | +11 | @guppy(module) +12 | def foo(q: qubit @owned) -> None: +13 | h(q) + | ^^^^ Expression with linear type `qubit` is leaked -11: @guppy(module) -12: def foo(q: qubit @owned) -> None: -13: h(q) - ^^^^ -GuppyTypeError: Value with linear type `qubit` is not used +Help: Consider assigning this value to a local variable + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/unused_field1.err b/tests/error/linear_errors/unused_field1.err index 2993b99b..fddbdc17 100644 --- a/tests/error/linear_errors/unused_field1.err +++ b/tests/error/linear_errors/unused_field1.err @@ -1,6 +1,10 @@ -Guppy compilation failed. Error in file $FILE:19 +Error: Linearity violation (at $FILE:19:8) + | +17 | +18 | @guppy(module) +19 | def foo(s: MyStruct @owned) -> int: + | ^^^^^^^^^^^^^^^^^^ Field `s.q` with linear type `qubit` is leaked -17: @guppy(module) -18: def foo(s: MyStruct @owned) -> int: - ^^^^^^^^^^^^^^^^^^ -GuppyError: Field `s.q` with linear type `qubit` is not used on all control-flow paths +Help: Make sure that `s.q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/unused_field2.err b/tests/error/linear_errors/unused_field2.err index 769fc2bb..4e5db4bb 100644 --- a/tests/error/linear_errors/unused_field2.err +++ b/tests/error/linear_errors/unused_field2.err @@ -1,6 +1,10 @@ -Guppy compilation failed. Error in file $FILE:19 +Error: Linearity violation (at $FILE:19:8) + | +17 | +18 | @guppy(module) +19 | def foo(s: MyStruct @owned) -> qubit: + | ^^^^^^^^^^^^^^^^^^ Field `s.q2` with linear type `qubit` is leaked -17: @guppy(module) -18: def foo(s: MyStruct @owned) -> qubit: - ^^^^^^^^^^^^^^^^^^ -GuppyError: Field `s.q2` with linear type `qubit` is not used on all control-flow paths +Help: Make sure that `s.q2` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/unused_field3.err b/tests/error/linear_errors/unused_field3.err index 062a18b9..898ffd35 100644 --- a/tests/error/linear_errors/unused_field3.err +++ b/tests/error/linear_errors/unused_field3.err @@ -1,6 +1,10 @@ -Guppy compilation failed. Error in file $FILE:25 +Error: Linearity violation (at $FILE:25:8) + | +23 | +24 | @guppy(module) +25 | def foo(s: MyStruct1 @owned) -> int: + | ^^^^^^^^^^^^^^^^^^^ Field `s.x.q2` with linear type `qubit` is leaked -23: @guppy(module) -24: def foo(s: MyStruct1 @owned) -> int: - ^^^^^^^^^^^^^^^^^^^ -GuppyError: Field `s.x.q2` with linear type `qubit` is not used on all control-flow paths +Help: Make sure that `s.x.q2` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/unused_same_block.err b/tests/error/linear_errors/unused_same_block.err index f13d4920..b55f5b54 100644 --- a/tests/error/linear_errors/unused_same_block.err +++ b/tests/error/linear_errors/unused_same_block.err @@ -1,7 +1,10 @@ -Guppy compilation failed. Error in file $FILE:14 +Error: Linearity violation (at $FILE:14:4) + | +12 | @guppy(module) +13 | def foo(q: qubit @owned) -> int: +14 | x = q + | ^ Variable `x` with linear type `qubit` is leaked -12: @guppy(module) -13: def foo(q: qubit @owned) -> int: -14: x = q - ^ -GuppyError: Variable `x` with linear type `qubit` is not used +Help: Make sure that `x` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/linear_errors/while_unused.err b/tests/error/linear_errors/while_unused.err index 9f02819b..bd0ac7e8 100644 --- a/tests/error/linear_errors/while_unused.err +++ b/tests/error/linear_errors/while_unused.err @@ -1,7 +1,13 @@ -Guppy compilation failed. Error in file $FILE:12 +Error: Linearity violation (at $FILE:12:4) + | +10 | @guppy(module) +11 | def test(n: int) -> None: +12 | q = qubit() + | ^ Variable `q` with linear type `qubit` may be leaked ... + | +14 | while i < n: + | ----- if this expression is `False` -10: @guppy(module) -11: def test(n: int) -> None: -12: q = qubit() - ^ -GuppyError: Variable `q` with linear type `qubit` is not used on all control-flow paths +Help: Make sure that `q` is consumed or returned to avoid the leak + +Guppy compilation failed due to 1 previous error diff --git a/tests/error/nested_errors/linear_capture.err b/tests/error/nested_errors/linear_capture.err index a1055f98..e2509545 100644 --- a/tests/error/nested_errors/linear_capture.err +++ b/tests/error/nested_errors/linear_capture.err @@ -1,7 +1,12 @@ -Guppy compilation failed. Error in file $FILE:15 +Error: Linearity violation (at $FILE:15:15) + | +13 | def foo(q: qubit @owned) -> qubit: +14 | def bar() -> qubit: +15 | return q + | ^ Variable `q` with linear type qubit cannot be used here + | since `q` is captured from an outer scope + | +13 | def foo(q: qubit @owned) -> qubit: + | --------------- `q` defined here -13: def foo(q: qubit @owned) -> qubit: -14: def bar() -> qubit: -15: return q - ^ -GuppyError: Variable `q` with linear type `qubit` may not be used here because it was defined in an outer scope (at 13:8) +Guppy compilation failed due to 1 previous error