Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,75 @@ class Duration {
self._nanoseconds / 3600000000000
}
}

implement root.ops.Add<Duration> for Duration {
type Output = Duration
function add(self, other: Duration) -> Duration throws never {
Duration {
_nanoseconds: self._nanoseconds + other._nanoseconds,
}
}
}

implement root.ops.Subtract<Duration> for Duration {
type Output = Duration
function sub(self, other: Duration) -> Duration throws never {
Duration {
_nanoseconds: self._nanoseconds - other._nanoseconds,
}
}
}

implement root.ops.Multiply<int> for Duration {
type Output = Duration
function mul(self, other: int) -> Duration throws never {
Duration {
_nanoseconds: self._nanoseconds * other,
}
}
}

implement root.ops.Multiply<bigint> for Duration {
type Output = Duration
function mul(self, other: bigint) -> Duration throws never {
Duration {
_nanoseconds: self._nanoseconds * other,
}
}
}

implement root.ops.Divide<int> for Duration {
type Output = Duration
function div(self, other: int) -> Duration throws never {
Duration {
_nanoseconds: self._nanoseconds / other,
}
}
}

implement root.ops.Divide<bigint> for Duration {
type Output = Duration
function div(self, other: bigint) -> Duration throws never {
Duration {
_nanoseconds: self._nanoseconds / other,
}
}
}

implement root.ops.Remainder<Duration> for Duration {
type Output = Duration
function rem(self, other: Duration) -> Duration throws never {
Duration {
_nanoseconds: self._nanoseconds % other._nanoseconds,
}
}
}

implement root.ops.Negate for Duration {
type Output = Duration
function neg(self) -> Duration throws never {
Duration {
_nanoseconds: -self._nanoseconds
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,30 @@ class Instant {
Instant { _nanoseconds: 0n }
}
}

implement root.ops.Add<Duration> for Instant {
type Output = Instant
function add(self, other: Duration) -> Instant {
Instant {
_nanoseconds: self._nanoseconds + other._nanoseconds
}
}
}

implement root.ops.Subtract<Duration> for Instant {
type Output = Instant
function sub(self, other: Duration) -> Instant {
Instant {
_nanoseconds: self._nanoseconds - other._nanoseconds
}
}
}

implement root.ops.Subtract<Instant> for Instant {
type Output = Duration
function sub(self, other: Instant) -> Duration {
Duration {
_nanoseconds: self._nanoseconds - other._nanoseconds
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,30 @@ class PlainDateTime {
}
}
}

implement root.ops.Add<Duration> for PlainDateTime {
type Output = PlainDateTime
function add(self, other: Duration) -> PlainDateTime {
PlainDateTime {
_nanoseconds: self._nanoseconds + other._nanoseconds
}
}
}

implement root.ops.Subtract<Duration> for PlainDateTime {
type Output = PlainDateTime
function sub(self, other: Duration) -> PlainDateTime {
PlainDateTime {
_nanoseconds: self._nanoseconds - other._nanoseconds
}
}
}

implement root.ops.Subtract<PlainDateTime> for PlainDateTime {
type Output = Duration
function sub(self, other: PlainDateTime) -> Duration {
Duration {
_nanoseconds: self._nanoseconds - other._nanoseconds
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,52 @@ class PlainTime {
}
}
}

/// Adding a `Duration` wraps around midnight: a `PlainTime` has no date to
/// carry into, so `23:00 + 2h` is `01:00`, not a next-day value.
implement root.ops.Add<Duration> for PlainTime {
type Output = PlainTime
function add(self, other: Duration) -> PlainTime {
PlainTime {
_nanoseconds: _wrap_into_day(self._nanoseconds + other._nanoseconds),
}
}
}

/// Wraps like [`Add`]: `01:00 - 2h` is `23:00`.
implement root.ops.Subtract<Duration> for PlainTime {
type Output = PlainTime
function sub(self, other: Duration) -> PlainTime {
PlainTime {
_nanoseconds: _wrap_into_day(self._nanoseconds - other._nanoseconds),
}
}
}

/// The signed gap between two wall-clock times, which does *not* wrap: the
/// result is negative when `other` is later in the day than `self`.
implement root.ops.Subtract<PlainTime> for PlainTime {
type Output = Duration
function sub(self, other: PlainTime) -> Duration {
Duration {
_nanoseconds: 0n + (self._nanoseconds - other._nanoseconds),
}
}
}

/// Internal: reduce `total_ns` to nanoseconds-since-midnight in `[0, 24h)`.
///
/// Written `((n % day) + day) % day` rather than `n % day` because `%` keeps
/// the sign of the left operand, so a negative `total_ns` would otherwise stay
/// negative and break `PlainTime`'s range invariant.
function _wrap_into_day(total_ns: bigint) -> int throws never {
let day_ns = 86400000000000n;
let wrapped = ((total_ns % day_ns) + day_ns) % day_ns;
// `wrapped` is in `[0, 24h)`, i.e. under 2^47 ns, so it always fits an int.
{
wrapped.to_int()
} catch (e) {
root.errors.InvalidArgument => baml.sys
.panic("unreachable: nanoseconds wrapped into [0, 24h) exceed int range")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,34 @@ function _format_zoned(
) -> string throws root.errors.InvalidArgument {
$rust_function
}

implement root.ops.Add<Duration> for ZonedDateTime {
type Output = ZonedDateTime
function add(self, other: Duration) -> ZonedDateTime {
ZonedDateTime {
_nanoseconds: self._nanoseconds + other._nanoseconds,
_offset_ns: self._offset_ns,
_iana: self._iana,
}
}
}

implement root.ops.Subtract<Duration> for ZonedDateTime {
type Output = ZonedDateTime
function sub(self, other: Duration) -> ZonedDateTime {
ZonedDateTime {
_nanoseconds: self._nanoseconds - other._nanoseconds,
_offset_ns: self._offset_ns,
_iana: self._iana,
}
}
}

implement root.ops.Subtract<ZonedDateTime> for ZonedDateTime {
type Output = Duration
function sub(self, other: ZonedDateTime) -> Duration {
Duration {
_nanoseconds: self._nanoseconds - other._nanoseconds,
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ class baml.time.Instant <builtin>/baml/ns_time/instant
class baml.time.PlainDate <builtin>/baml/ns_time/plaindate.baml:5
class baml.time.PlainDateTime <builtin>/baml/ns_time/plaindatetime.baml:7
class baml.time.PlainTime <builtin>/baml/ns_time/plaintime.baml:4
function baml.time._wrap_into_day <builtin>/baml/ns_time/plaintime.baml:141
type baml.time.Disambiguation <builtin>/baml/ns_time/timezone.baml:5
class baml.time.UnknownTimezoneError <builtin>/baml/ns_time/timezone.baml:9
class baml.time.AmbiguousTimeError <builtin>/baml/ns_time/timezone.baml:15
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ methods:
Returns the number of minutes in `self`.
function to_hours(self) -> bigint
Returns the number of hours in `self`.
function add(self, other: baml.time.Duration) -> baml.time.Duration
function sub(self, other: baml.time.Duration) -> baml.time.Duration
function mul(self, other: int) -> baml.time.Duration
function mul(self, other: bigint) -> baml.time.Duration
function div(self, other: int) -> baml.time.Duration
function div(self, other: bigint) -> baml.time.Duration
function rem(self, other: baml.time.Duration) -> baml.time.Duration
function neg(self) -> baml.time.Duration

static methods:
function from_nanoseconds(ns: int | bigint) -> baml.time.Duration
Expand All @@ -40,5 +48,31 @@ static methods:
function from_hours(h: int | bigint) -> baml.time.Duration
Creates a new duration of `h` hours.

implements:
implements baml.ops.Add for baml.time.Duration (<builtin>/baml/ns_time/duration.baml:103)
type Output = baml.time.Duration
function add(self, other: baml.time.Duration) -> baml.time.Duration
implements baml.ops.Divide for baml.time.Duration (<builtin>/baml/ns_time/duration.baml:139)
type Output = baml.time.Duration
function div(self, other: int) -> baml.time.Duration
implements baml.ops.Divide for baml.time.Duration (<builtin>/baml/ns_time/duration.baml:148)
type Output = baml.time.Duration
function div(self, other: bigint) -> baml.time.Duration
implements baml.ops.Multiply for baml.time.Duration (<builtin>/baml/ns_time/duration.baml:121)
type Output = baml.time.Duration
function mul(self, other: int) -> baml.time.Duration
implements baml.ops.Multiply for baml.time.Duration (<builtin>/baml/ns_time/duration.baml:130)
type Output = baml.time.Duration
function mul(self, other: bigint) -> baml.time.Duration
implements baml.ops.Negate for baml.time.Duration (<builtin>/baml/ns_time/duration.baml:166)
type Output = baml.time.Duration
function neg(self) -> baml.time.Duration
implements baml.ops.Remainder for baml.time.Duration (<builtin>/baml/ns_time/duration.baml:157)
type Output = baml.time.Duration
function rem(self, other: baml.time.Duration) -> baml.time.Duration
implements baml.ops.Subtract for baml.time.Duration (<builtin>/baml/ns_time/duration.baml:112)
type Output = baml.time.Duration
function sub(self, other: baml.time.Duration) -> baml.time.Duration

blanket implementations:
baml.Concrete (<builtin>/baml/core.baml:13)
23 changes: 23 additions & 0 deletions baml_language/crates/baml_project/src/client_codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,29 @@ pub fn build_symbol_pool(db: &ProjectDatabase) -> SymbolPool {
continue;
}

// Interface-impl methods are not part of the generated surface.
// Interfaces themselves are never emitted — host languages differ
// too much on trait/protocol/interface semantics — so a method
// that only exists to satisfy one has nothing to attach to.
//
// Emitting them is not merely redundant but unsound: the
// generated name is the bare method name, so a type implementing
// one interface at several instantiations (`Multiply<int>` and
// `Multiply<bigint>` for `baml.time.Duration`) or two interfaces
// sharing a method name (`Subtract<Duration>` and
// `Subtract<Instant>` for `baml.time.Instant`) collides with
// itself. The runtime path it invokes (`baml.time.Duration.mul`)
// is ambiguous for the same reason.
//
// `class.methods` is flat: in-body `implements I { … }` and a
// non-generic out-of-body `implement I for C` merged onto `C`
// (`lower_cst`) both land here, so the interface target — not the
// declaration site — is what identifies them.
if baml_compiler2_ppir::item_data::method_interface_target(db, method_loc).is_some()
{
continue;
}

if matches!(
baml_compiler2_ppir::function_body(db, method_loc).as_ref(),
baml_compiler2_hir::body::FunctionBody::Builtin(ast::BuiltinKind::Intrinsic)
Expand Down
Loading
Loading