Skip to content

Commit

Permalink
Merge pull request #1070 from hylo-lang/print-integer
Browse files Browse the repository at this point in the history
Implement `print` for `Int`
  • Loading branch information
kyouko-taiga authored Oct 6, 2023
2 parents e9d5087 + 44d1c69 commit be78e1f
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 22 deletions.
62 changes: 53 additions & 9 deletions Library/Hylo/Array.hylo
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public type Array<Element: Movable & Deinitializable>: Deinitializable {
/// The header of the buffer indicates the number of elements contained in the array.
var storage: DynamicBuffer<Int, Element>

/// Creates a new, empty array.
/// Creates an empty array.
public init() {
&storage = .new()
}
Expand All @@ -15,22 +15,27 @@ public type Array<Element: Movable & Deinitializable>: Deinitializable {
public fun deinit() sink {
var i = 0
while i < count() {
&pointer_to_element(at: i).unsafe_pointee().deinit()
&pointer_to_element[at: i].unsafe_pointee().deinit()
&i += 1
}
}

/// The number of elements in the array.
/// Returns the number of elements in `self`.
public fun count() -> Int {
if storage.capacity() == 0 { 0 } else { storage.header.copy() }
}

/// Returns `true` if `self` is empty.
public fun is_empty() -> Bool {
count() == 0
}

/// The number of elements that can be stored in the array before new storage must be allocated.
public fun capacity() -> Int {
return storage.capacity()
}

/// Reserves enough space to store `n` elements
/// Reserves enough space to store `n` elements in `self`.
public fun reserve_capacity(_ n: Int) inout {
if n < capacity() { return }

Expand Down Expand Up @@ -60,31 +65,70 @@ public type Array<Element: Movable & Deinitializable>: Deinitializable {
&storage = new_storage
}

/// Projects a pointer to the start of the array's contiguous storage.
///
/// The projected pointer is valid only for the duration of the projection and can be advanced up
/// to `count()`. It may be null if `self` is empty.
public property contiguous_storage: Pointer<Element> {
yield if capacity() == 0 { .null() } else { .new(pointer_to_element[at: 0]) }
}

/// Calls `action` with a pointer to the start of the array's mutable contiguous storage.
///
/// The projected pointer is valid only for the duration of the projection and can be advanced up
/// to `count()`. It may be null if `self` is empty.
public fun with_mutable_contiguous_storage<E, T>(
_ action: inout [E](PointerToMutable<Element>) inout -> T
) inout -> T {
if capacity() == 0 { &action(.null()) } else { &action(pointer_to_element[at: 0]) }
}

/// Adds a new element at the end of the array.
public fun append(_ source: sink Element) inout {
&reserve_capacity(count() + 1)
pointer_to_element(at: count()).unsafe_initialize_pointee(source)
pointer_to_element[at: count()].unsafe_initialize_pointee(source)
&storage.header += 1
}

/// Exchanges the values at the given positions in `self`.
public fun swap_at(_ i: Int, _ j: Int) inout {
// precondition(i >= 0 && i < count())
// precondition(j >= 0 && j < count())
if i == j { return }
swap(&pointer_to_element[at: i].unsafe[], &pointer_to_element[at: j].unsafe[])
}

/// Reverses the elements of `self` in place.
///
/// - Complexity: O(n), where n is the number of elements in `self`.
public fun reverse() inout {
var i = count() - 1
var j = 0
while i > j {
swap_at(i, j)
&i -= 1
&j += 1
}
}

/// Accesses the element at `position`.
///
/// - Requires: `position` is in the range `0 ..< count()`.
public subscript(_ position: Int): Element {
let {
// precondition(position >= 0 && position < count())
pointer_to_element(at: position).unsafe[]
pointer_to_element[at: position].unsafe[]
}
inout {
// precondition(position >= 0 && position < count())
pointer_to_element(at: position).unsafe[]
pointer_to_element[at: position].unsafe[]
}
}

/// Returns the address of the element at `position`.
/// Projects the address of the element at `position`.
///
/// - Requires: `position` is in the range `0 ..< capacity()`.
fun pointer_to_element(at position: Int) -> PointerToMutable<Element> {
subscript pointer_to_element(at position: Int): PointerToMutable<Element> {
storage.first_element_address().advance(by: position)
}

Expand Down
8 changes: 7 additions & 1 deletion Library/Hylo/Core/Bitcast.hylo
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ public subscript unsafe_bitcast<T, U>(_ value: T): U {
yield p.unsafe[]
}
inout {
sink let p: PointerToMutable<U> = PointerToMutable(type_punning: pointerToMutable[&value])
sink let p: PointerToMutable<U> = PointerToMutable(type_punning: mutable_pointer[to: &value])
yield &p.unsafe[]
}
}

/// Returns `value` with its memory representation reinterpreted as a value of type `U`.
public fun unsafe_bitcast<T, U: Movable>(consuming value: T) -> U {
sink let p: PointerToMutable<U> = PointerToMutable(type_punning: mutable_pointer[to: &value])
return p.unsafe_pointee()
}
18 changes: 18 additions & 0 deletions Library/Hylo/Core/Movable.hylo
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,21 @@ public trait Movable {
}

}

public extension Movable {

/// Exchanges the value of `self` with that of `other`.
public fun exchange(with other: inout Self) inout {
sink let x = self
&self = other
&other = x
}

}

// TODO: Remove in favor of `Movable.exchange(with:)` when #1064 is fixed
public fun swap<T: Movable>(_ a: inout T, _ b: inout T) {
sink let x = a
&a = b
&b = x
}
9 changes: 1 addition & 8 deletions Library/Hylo/Core/PointerToMutable.hylo
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,6 @@ public type PointerToMutable<Pointee>: Regular {

}


// FIXME: rename pointer[toMutable:]
/// The address of `x`.
public subscript pointerToMutable<T>(_ x: inout T): PointerToMutable<T> {
let { yield PointerToMutable(base: Builtin.address(of: x)) }
}

public conformance PointerToMutable: Copyable {

/// Returns an equivalent instance.
Expand Down Expand Up @@ -113,12 +106,12 @@ public extension PointerToMutable where Pointee: Movable {

}

// TODO: Rename to `pointer[to_mutable:]`
/// The address of `x`.
public subscript mutable_pointer<T>(to x: inout T): PointerToMutable<T> {
let { yield PointerToMutable(base: Builtin.address(of: x)) }
}


/// Initializes `x` to `y`.
///
/// - Note: This function is a workaround for the lack of `set` bindings (see #925).
Expand Down
8 changes: 5 additions & 3 deletions Library/Hylo/Pointers+DynamicAllocation.hylo
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ public extension PointerToMutable where Pointee == Never {

/// Allocates memory for `count` bytes at given `alignment`.
public static fun allocate_bytes(count: Int, aligned_at alignment: Int) -> Self {
Self.new(base: aligned_alloc(alignment, count).base)
Self.new(base: hylo_aligned_alloc(alignment, count).base)
}

}
Expand All @@ -23,12 +23,14 @@ public extension PointerToMutable {

/// Allocates memory for `count` instances of `Pointee`.
public static fun allocate(count: Int) -> Self {
Self.new(base: aligned_alloc(MemoryLayout<Pointee>.alignment(), MemoryLayout<Pointee>.stride() * count).base)
return Self.new(base: hylo_aligned_alloc(
MemoryLayout<Pointee>.alignment(),
MemoryLayout<Pointee>.stride() * count).base)
}

/// Deallocates the memory previously allocated at `self`.
public fun deallocate() {
free(CVoidPointer(base: base))
hylo_aligned_free(MemoryAddress(base: base))
}

}
27 changes: 27 additions & 0 deletions Library/Hylo/Print.hylo
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,33 @@ public fun print(_ item: String, terminator: String = "\n") {
_ = fwrite(CVoidPointer(base: terminator.utf8.base), 1, 1, CVoidPointer(base: stream.base))
}

/// Writes the textual representation of `item` to the standard output.
public fun print(_ item: Int, radix: Int = 10, terminator: String = "\n") {
if item == 0 {
print("0")
return
}

var a = Array<Int8>()
var v = item.abs()

while v != 0 {
let i = v % radix
&v /= radix
// Note: 48 = "0" and 97 = "a"
&a.append((Int8(truncating_or_extending: i + if i < 10 { 48 } else { 87 })))
}

// Note: 45 = "-"
if item < 0 { &a.append(45) }
&a.reverse()

let stream = stdout()
let buffer = a.contiguous_storage.base
_ = fwrite(CVoidPointer(base: buffer), 1, a.count(), CVoidPointer(base: stream.base))
_ = fwrite(CVoidPointer(base: terminator.utf8.base), 1, 1, CVoidPointer(base: stream.base))
}

/// The standard output of the current process.
fun stdout() -> MemoryAddress {
.new(base: fdopen(1, CVoidPointer(base: "w".utf8.base)).base)
Expand Down
30 changes: 30 additions & 0 deletions Sources/FrontEnd/TypeChecking/TypeChecker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,15 @@ struct TypeChecker {
for s in program.scopes(from: scopeOfUse) where s.kind.value is GenericScope.Type {
let e = environment(of: s)!
result.formUnion(e.conformedTraits(of: ^t))

// Note: `s` might be extending the type whose declaration introduced the generic environment
// that declared `t`.
if s.kind.value is TypeExtendingDecl.Type {
let d = AnyDeclID(s)!
if let g = environment(introducedByDeclOf: uncheckedType(of: d)) {
result.formUnion(g.conformedTraits(of: ^t))
}
}
}
return result
}
Expand Down Expand Up @@ -1359,6 +1368,20 @@ struct TypeChecker {
return result
}

/// Returns the generic environment introduced by the declaration of `t`, if any.
private mutating func environment(introducedByDeclOf t: AnyType) -> GenericEnvironment? {
switch t.base {
case let u as ProductType:
return environment(of: u.decl)
case let u as TraitType:
return environment(of: u.decl)
case let u as TypeAliasType:
return environment(of: u.decl)
default:
return nil
}
}

/// Insert's `d`'s constraints in `e`.
private mutating func insertConstraints(
of d: AssociatedTypeDecl.ID, in e: inout GenericEnvironment
Expand Down Expand Up @@ -2978,6 +3001,7 @@ struct TypeChecker {

// The specialization of the match includes that of context in which it was looked up.
var specialization = genericArguments(inScopeIntroducing: m, resolvedIn: context)
candidateType = specialize(candidateType, for: specialization, in: scopeOfUse)

// Keep track of generic arguments that should be captured later on.
let candidateSpecialization = genericArguments(
Expand Down Expand Up @@ -3008,6 +3032,12 @@ struct TypeChecker {
}
}

// Re-specialize the candidate's type now that the substitution map is complete.
//
// The specialization map now contains the substitutions accumulated from the candidate's
// qualification as well as the ones related to the resolution of the candidate itself. For
// example, if we resolved `A<X>.f<Y>`, we'd get `X` from the resolution of the qualification
// and `Y` from the resolution of the candidate.
candidateType = specialize(candidateType, for: specialization, in: scopeOfUse)

let r = program.makeReference(
Expand Down
14 changes: 13 additions & 1 deletion Tests/HyloTests/TestCases/TypeChecking/Property.hylo
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
//- typeCheck expecting: success

type A {
type A: Deinitializable {
public memberwise init
public property x: Int { let { 0 } }
}

type B<T>: Deinitializable {
public memberwise init
}

type C<T>: Deinitializable {
public memberwise init
public property x: B<T> { .new() }
}

fun check<T>(_ x: T) {}

public fun main() {
check<Int>(A().x)

let a = C<Bool>().x
check<B<Bool>>(a)
}
22 changes: 22 additions & 0 deletions Tests/LibraryTests/TestCases/ArrayTests.hylo
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,29 @@ fun test_append() {
precondition(d[2] == 84)
}

fun test_swap_at() {
var a = Array<Bool>()
&a.append(false)
&a.append(true)
&a.swap_at(0, 1)
precondition(a[0])
}

fun test_reverse() {
var a = Array<Int>()
&a.append(21)
&a.append(42)
&a.append(84)

&a.reverse()
precondition(a[0] == 84)
precondition(a[1] == 42)
precondition(a[2] == 21)
}

public fun main() {
test_init_empty()
test_append()
test_swap_at()
test_reverse()
}

0 comments on commit be78e1f

Please sign in to comment.