Skip to content

Commit

Permalink
[mojo-lang] Add support for named results using out syntax.
Browse files Browse the repository at this point in the history
This adds support for spelling "named functions results" using the same
`out`
syntax used by initializers (in addition to `-> T as name`). Functions
may have
at most one named result or return type specified with the usual `->`
syntax.
`out` arguments may occur anywhere in the argument list, but are
typically last
(except for `__init__` methods, where they are typically first).

```mojo
  # This function has type "fn() -> String"
  fn example(out result: String):
    result = "foo"
```

The parser still accepts the old syntax as a synonym for this, but that
will
eventually be deprecated and removed.

This was discussed extensively here:
#3623

MODULAR_ORIG_COMMIT_REV_ID: 23b3a120227a42d9550ba76d8cafb63c3a03edcf
  • Loading branch information
lattner authored and modularbot committed Dec 9, 2024
1 parent 6471338 commit f87dec5
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 54 deletions.
18 changes: 18 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,24 @@ what we publish.
release of Mojo, but will be removed in the future. Please migrate to the
new syntax.

- Similarly, the spelling of "named functions results" has switched to use `out`
syntax instead of `-> T as name`. Functions may have at most one named result
or return type specified with the usual `->` syntax. `out` arguments may
occur anywhere in the argument list, but are typically last (except for
`__init__` methods, where they are typically first).

```mojo
# This function has type "fn() -> String"
fn example(out result: String):
result = "foo"
```

The parser still accepts the old syntax as a synonym for this, but that will
eventually be deprecated and removed.

This was [discussed extensively in a public
proposal](https://github.com/modularml/mojo/issues/3623).

- More things have been removed from the auto-exported set of entities in the `prelude`
module from the Mojo standard library.
- `UnsafePointer` has been removed. Please explicitly import it via
Expand Down
12 changes: 8 additions & 4 deletions stdlib/src/builtin/_pybind.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,13 @@ fn fail_initialization(owned err: Error) -> PythonObject:

fn pointer_bitcast[
To: AnyType
](ptr: Pointer) -> Pointer[To, ptr.origin, ptr.address_space, *_, **_] as out:
return __type_of(out)(
](
ptr: Pointer,
out result: Pointer[To, ptr.origin, ptr.address_space, *_, **_],
):
return __type_of(result)(
_mlir_value=__mlir_op.`lit.ref.from_pointer`[
_type = __type_of(out)._mlir_type
_type = __type_of(result)._mlir_type
](
UnsafePointer(__mlir_op.`lit.ref.to_pointer`(ptr._value))
.bitcast[To]()
Expand Down Expand Up @@ -162,7 +165,8 @@ fn _try_convert_arg[
type_name_id: StringLiteral,
py_args: TypedPythonObject["Tuple"],
argidx: Int,
) raises -> T as result:
out result: T,
) raises:
try:
result = T.try_from_python(py_args[argidx])
except convert_err:
Expand Down
16 changes: 10 additions & 6 deletions stdlib/src/builtin/coroutine.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ struct Coroutine[type: AnyType, origins: OriginSet]:
__disable_del self

@always_inline
fn __await__(owned self) -> type as out:
fn __await__(owned self, out result: type):
"""Suspends the current coroutine until the coroutine is complete.
Returns:
Expand All @@ -150,9 +150,11 @@ struct Coroutine[type: AnyType, origins: OriginSet]:
__disable_del self
__mlir_op.`co.await`[_type=NoneType](
handle,
__mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)),
__mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(result)),
)
__mlir_op.`lit.ownership.mark_initialized`(
__get_mvalue_as_litref(result)
)
__mlir_op.`lit.ownership.mark_initialized`(__get_mvalue_as_litref(out))


# ===----------------------------------------------------------------------=== #
Expand Down Expand Up @@ -220,7 +222,7 @@ struct RaisingCoroutine[type: AnyType, origins: OriginSet]:
__disable_del self

@always_inline
fn __await__(owned self) raises -> type as out:
fn __await__(owned self, out result: type) raises:
"""Suspends the current coroutine until the coroutine is complete.
Returns:
Expand All @@ -233,7 +235,7 @@ struct RaisingCoroutine[type: AnyType, origins: OriginSet]:
__disable_del self
if __mlir_op.`co.await`[_type = __mlir_type.i1](
handle,
__mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(out)),
__mlir_op.`lit.ref.to_pointer`(__get_mvalue_as_litref(result)),
__mlir_op.`lit.ref.to_pointer`(
__get_mvalue_as_litref(__get_nearest_error_slot())
),
Expand All @@ -242,4 +244,6 @@ struct RaisingCoroutine[type: AnyType, origins: OriginSet]:
__get_mvalue_as_litref(__get_nearest_error_slot())
)
__mlir_op.`lit.raise`()
__mlir_op.`lit.ownership.mark_initialized`(__get_mvalue_as_litref(out))
__mlir_op.`lit.ownership.mark_initialized`(
__get_mvalue_as_litref(result)
)
2 changes: 1 addition & 1 deletion stdlib/src/builtin/int.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -1154,7 +1154,7 @@ struct Int(

@doc_private
@staticmethod
fn try_from_python(obj: PythonObject) raises -> Self as result:
fn try_from_python(obj: PythonObject, out result: Self) raises:
"""Construct an `Int` from a Python integer value.
Raises:
Expand Down
2 changes: 1 addition & 1 deletion stdlib/src/builtin/simd.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -2329,7 +2329,7 @@ struct SIMD[type: DType, size: Int](
](self, value, Int64(offset))

@always_inline("nodebug")
fn join(self, other: Self) -> SIMD[type, 2 * size] as result:
fn join(self, other: Self, out result: SIMD[type, 2 * size]):
"""Concatenates the two vectors together.
Args:
Expand Down
4 changes: 2 additions & 2 deletions stdlib/src/collections/deque.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,7 @@ struct Deque[ElementType: CollectionElement](

return (self._data + self._head)[]

fn pop(mut self) raises -> ElementType as element:
fn pop(mut self, out element: ElementType) raises:
"""Removes and returns the element from the right side of the deque.
Returns:
Expand All @@ -818,7 +818,7 @@ struct Deque[ElementType: CollectionElement](

return

fn popleft(mut self) raises -> ElementType as element:
fn popleft(mut self, out element: ElementType) raises:
"""Removes and returns the element from the left side of the deque.
Returns:
Expand Down
15 changes: 8 additions & 7 deletions stdlib/src/memory/unsafe_pointer.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,14 @@ struct UnsafePointer[
@always_inline("nodebug")
fn address_of(
ref [address_space]arg: type,
) -> UnsafePointer[
type,
address_space=address_space,
alignment=1,
origin=MutableAnyOrigin
# TODO: Propagate origin of the argument.
] as result:
out result: UnsafePointer[
type,
address_space=address_space,
alignment=1,
origin=MutableAnyOrigin
# TODO: Propagate origin of the argument.
],
):
"""Gets the address of the argument.
Args:
Expand Down
103 changes: 71 additions & 32 deletions stdlib/src/utils/index.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -454,9 +454,10 @@ struct IndexList[
@always_inline("nodebug")
fn canonicalize(
self,
) -> IndexList[
size, element_bitwidth = bitwidthof[Int](), unsigned=False
] as result:
out result: IndexList[
size, element_bitwidth = bitwidthof[Int](), unsigned=False
],
):
"""Canonicalizes the IndexList.
Returns:
Expand Down Expand Up @@ -772,11 +773,14 @@ struct IndexList[
@always_inline
fn cast[
type: DType
](self) -> IndexList[
size,
element_bitwidth = bitwidthof[type](),
unsigned = _is_unsigned[type](),
] as result:
](
self,
out result: IndexList[
size,
element_bitwidth = bitwidthof[type](),
unsigned = _is_unsigned[type](),
],
):
"""Casts to the target DType.
Parameters:
Expand All @@ -803,9 +807,12 @@ struct IndexList[
*,
element_bitwidth: Int = Self.element_bitwidth,
unsigned: Bool = Self.unsigned,
](self) -> IndexList[
size, element_bitwidth=element_bitwidth, unsigned=unsigned
] as result:
](
self,
out result: IndexList[
size, element_bitwidth=element_bitwidth, unsigned=unsigned
],
):
"""Casts to the target DType.
Parameters:
Expand Down Expand Up @@ -837,9 +844,12 @@ fn Index[
*,
element_bitwidth: Int = bitwidthof[Int](),
unsigned: Bool = False,
](x: T0) -> IndexList[
1, element_bitwidth=element_bitwidth, unsigned=unsigned
] as result:
](
x: T0,
out result: IndexList[
1, element_bitwidth=element_bitwidth, unsigned=unsigned
],
):
"""Constructs a 1-D Index from the given value.
Parameters:
Expand All @@ -859,9 +869,12 @@ fn Index[
@always_inline
fn Index[
*, element_bitwidth: Int = bitwidthof[Int](), unsigned: Bool = False
](x: UInt) -> IndexList[
1, element_bitwidth=element_bitwidth, unsigned=unsigned
] as result:
](
x: UInt,
out result: IndexList[
1, element_bitwidth=element_bitwidth, unsigned=unsigned
],
):
"""Constructs a 1-D Index from the given value.
Parameters:
Expand All @@ -884,9 +897,13 @@ fn Index[
*,
element_bitwidth: Int = bitwidthof[Int](),
unsigned: Bool = False,
](x: T0, y: T1) -> IndexList[
2, element_bitwidth=element_bitwidth, unsigned=unsigned
] as result:
](
x: T0,
y: T1,
out result: IndexList[
2, element_bitwidth=element_bitwidth, unsigned=unsigned
],
):
"""Constructs a 2-D Index from the given values.
Parameters:
Expand All @@ -908,9 +925,13 @@ fn Index[
@always_inline
fn Index[
*, element_bitwidth: Int = bitwidthof[Int](), unsigned: Bool = False
](x: UInt, y: UInt) -> IndexList[
2, element_bitwidth=element_bitwidth, unsigned=unsigned
] as result:
](
x: UInt,
y: UInt,
out result: IndexList[
2, element_bitwidth=element_bitwidth, unsigned=unsigned
],
):
"""Constructs a 2-D Index from the given values.
Parameters:
Expand All @@ -935,9 +956,14 @@ fn Index[
*,
element_bitwidth: Int = bitwidthof[Int](),
unsigned: Bool = False,
](x: T0, y: T1, z: T2) -> IndexList[
3, element_bitwidth=element_bitwidth, unsigned=unsigned
] as result:
](
x: T0,
y: T1,
z: T2,
out result: IndexList[
3, element_bitwidth=element_bitwidth, unsigned=unsigned
],
):
"""Constructs a 3-D Index from the given values.
Parameters:
Expand Down Expand Up @@ -967,9 +993,15 @@ fn Index[
*,
element_bitwidth: Int = bitwidthof[Int](),
unsigned: Bool = False,
](x: T0, y: T1, z: T2, w: T3) -> IndexList[
4, element_bitwidth=element_bitwidth, unsigned=unsigned
] as result:
](
x: T0,
y: T1,
z: T2,
w: T3,
out result: IndexList[
4, element_bitwidth=element_bitwidth, unsigned=unsigned
],
):
"""Constructs a 4-D Index from the given values.
Parameters:
Expand Down Expand Up @@ -1002,9 +1034,16 @@ fn Index[
*,
element_bitwidth: Int = bitwidthof[Int](),
unsigned: Bool = False,
](x: T0, y: T1, z: T2, w: T3, v: T4) -> IndexList[
5, element_bitwidth=element_bitwidth, unsigned=unsigned
] as result:
](
x: T0,
y: T1,
z: T2,
w: T3,
v: T4,
out result: IndexList[
5, element_bitwidth=element_bitwidth, unsigned=unsigned
],
):
"""Constructs a 5-D Index from the given values.
Parameters:
Expand Down
2 changes: 1 addition & 1 deletion stdlib/test/os/path/test_expandvars.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ from testing import assert_equal
struct EnvVar:
var name: String

fn __init__(out self, name: String, value: String) -> None:
fn __init__(out self, name: String, value: String):
self.name = name
_ = os.setenv(name, value)

Expand Down

0 comments on commit f87dec5

Please sign in to comment.