Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement print for Int #1070

Merged
merged 15 commits into from
Oct 6, 2023
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
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()
}
Loading