Skip to content

Commit

Permalink
Merge pull request #1102 from hylo-lang/compile-for
Browse files Browse the repository at this point in the history
Implement code generation for non-consuming non-modifying for loops
  • Loading branch information
kyouko-taiga authored Oct 20, 2023
2 parents a87f7f6 + ed33d96 commit 7ff17dc
Show file tree
Hide file tree
Showing 9 changed files with 337 additions and 95 deletions.
33 changes: 25 additions & 8 deletions Library/Hylo/Array.hylo
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,36 @@ public type Array<Element: Movable & Deinitializable>: Deinitializable {
}
}

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

}

public conformance Array: Collection {

public typealias Index = Int

public fun start_index() -> Int { 0 }

public fun end_index() -> Int { count() }

/// Returns the position immediately after `i`.
///
/// - Requires: `i != end_index()`.
/// - Complexity: O(1).
public fun index(after i: Int) -> Int { i + 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())
// TODO: uncomment when #1046 is implemented
// precondition(position >= 0 && position < count(), "index is out of bounds")
pointer_to_element[at: position].unsafe[]
}
inout {
Expand All @@ -125,13 +149,6 @@ public type Array<Element: Movable & Deinitializable>: Deinitializable {
}
}

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

}

/*
Expand Down
5 changes: 5 additions & 0 deletions Sources/Core/AST/AST.swift
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,11 @@ public struct AST {
}
}

/// Returns `true` iff `s` is a consuming for-loop.
public func isConsuming(_ s: ForStmt.ID) -> Bool {
self[self[self[s].binding].pattern].introducer.value.isConsuming
}

/// Returns the source site of `expr`
public func site(of expr: FoldedSequenceExpr) -> SourceRange {
switch expr {
Expand Down
18 changes: 1 addition & 17 deletions Sources/Core/AST/Name.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,10 @@ public struct Name: Hashable, Codable {
/// The introducer, if any, is incorporated during parsing, after the original `Name` is created.
public let introducer: AccessEffect?

/// Creates a new name.
public init(stem: Identifier, labels: [String?] = []) {
self.stem = stem
self.labels = labels
self.notation = nil
self.introducer = nil
}

/// Creates a new operator name.
public init(stem: Identifier, notation: OperatorNotation) {
self.stem = stem
self.labels = []
self.notation = notation
self.introducer = nil
}

/// Creates an instance with the given properties.
public init(
stem: Identifier,
labels: [String?],
labels: [String?] = [],
notation: OperatorNotation? = nil,
introducer: AccessEffect? = nil
) {
Expand Down
22 changes: 11 additions & 11 deletions Sources/FrontEnd/TypeChecking/TypeChecker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -407,13 +407,13 @@ struct TypeChecker {
private mutating func demandImplementation(
of requirement: AssociatedTypeDecl.ID, for model: AnyType, in scopeOfUse: AnyScopeID
) -> AnyType? {
let trait = traitDefining(requirement)!
let trait = traitDeclaring(requirement)!
guard
let c = demandConformance(of: model, to: trait, exposedTo: scopeOfUse),
let a = MetatypeType(uncheckedType(of: c.implementations[requirement]!.decl!))
else { return nil }

return specialize(a.instance, for: c.arguments, in: scopeOfUse)
return specialize(a.instance, for: c.arguments, in: c.scope)
}

// MARK: Type transformations
Expand Down Expand Up @@ -3237,7 +3237,7 @@ struct TypeChecker {
}

// If the match is a trait member looked up with qualification, specialize its receiver.
if let t = traitDefining(m) {
if let t = traitDeclaring(m) {
specialization[program[t.decl].receiver] = context?.type
}

Expand All @@ -3252,7 +3252,7 @@ struct TypeChecker {
// If the receiver is an existential, replace its receiver.
if let container = ExistentialType(context?.type) {
candidateType = candidateType.asMember(of: container)
if let t = traitDefining(m) {
if let t = traitDeclaring(m) {
specialization[program[t.decl].receiver] = ^WitnessType(of: container)
}
}
Expand Down Expand Up @@ -3662,7 +3662,7 @@ struct TypeChecker {
}

/// Returns the trait of which `d` is a member, or `nil` if `d` isn't member of a trait.
mutating func traitDefining<T: DeclID>(_ d: T) -> TraitType? {
mutating func traitDeclaring<T: DeclID>(_ d: T) -> TraitType? {
guard let p = program.nodeToScope[d] else {
assert(d.kind == ModuleDecl.self)
return nil
Expand All @@ -3674,9 +3674,9 @@ struct TypeChecker {
case ExtensionDecl.self:
return TraitType(uncheckedType(of: ExtensionDecl.ID(p)!))
case MethodDecl.self:
return traitDefining(MethodDecl.ID(p)!)
return traitDeclaring(MethodDecl.ID(p)!)
case SubscriptDecl.self:
return traitDefining(SubscriptDecl.ID(p)!)
return traitDeclaring(SubscriptDecl.ID(p)!)
default:
return nil
}
Expand Down Expand Up @@ -4644,7 +4644,7 @@ struct TypeChecker {
private mutating func inferredIterationElementType(of s: ForStmt.ID) -> AnyType? {
guard let domain = checkedType(of: program[s].domain.value).errorFree else { return nil }

let isConsuming = program[s].binding.pattern.introducer.value.isConsuming
let isConsuming = program.ast.isConsuming(s)
let domainTraits = conformedTraits(of: domain, in: program[s].scope)
let collection = program.ast.coreTrait("Collection")!

Expand Down Expand Up @@ -4912,9 +4912,9 @@ struct TypeChecker {
private mutating func compareDepth(
_ lhs: AnyDeclID, _ rhs: AnyDeclID, in scopeOfUse: AnyScopeID
) -> StrictPartialOrdering {
if let l = traitDefining(lhs) {
if let l = traitDeclaring(lhs) {
// If `lhs` is a trait member but `rhs` isn't, then `rhs` shadows `lhs`.
guard let r = traitDefining(rhs) else { return .descending }
guard let r = traitDeclaring(rhs) else { return .descending }

// If `lhs` and `rhs` are members of traits `t1` and `t2`, respectively, then `lhs` shadows
// `rhs` iff `t1` refines `t2`.
Expand All @@ -4923,7 +4923,7 @@ struct TypeChecker {
return nil
}

if traitDefining(rhs) != nil {
if traitDeclaring(rhs) != nil {
// If `rhs` is a trait member but `lhs` isn't, then `lhs` shadows `rhs`.
return .ascending
}
Expand Down
10 changes: 9 additions & 1 deletion Sources/FrontEnd/TypedProgram.swift
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ public struct TypedProgram {
/// Returns the trait of which `d` is a member, or `nil` if `d` isn't member of a trait.
public func traitDeclaring<T: DeclID>(_ d: T) -> TraitType? {
var checker = TypeChecker(asContextFor: self)
return checker.traitDefining(d)
return checker.traitDeclaring(d)
}

/// If `d` is member of a trait `c`, returns `(d, c)` if `d` is a requirement, or `(r, c)` if `d`
Expand Down Expand Up @@ -422,6 +422,14 @@ public struct TypedProgram {
site: .empty(at: ast[scopeOfUse].site.first()))
}

/// Returns the type satisfying the associated type requirement named `n` in conformance `c`.
public func associatedType(_ n: String, for c: Core.Conformance) -> AnyType {
let r = ast.requirements(Name(stem: n), in: c.concept.decl)[0]
let d = c.implementations[r]!.decl!
let t = specialize(MetatypeType(declType[d]!)!.instance, for: c.arguments, in: c.scope)
return canonical(t, in: c.scope)
}

/// Returns the foreign representation of `t` using its conformance to `ForeignConvertible` in
/// `scopeOfUse`.
///
Expand Down
40 changes: 40 additions & 0 deletions Sources/IR/CollectionWitness.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Core

/// The witness of a type's conformance to the `Collection` trait from the standard library.
struct CollectionWitness {

/// The implementation of the `Collection.Index`.
let index: AnyType

/// The implementation of the `Collection.Element`.
let element: AnyType

/// The implementation of `Collection.start_index`.
let startIndex: Operand

/// The implementation of `Collection.end_index`.
let endIndex: Operand

/// The implementation of `Collection.index_after(_:)`.
let indexAfter: Operand

/// The implementation of `Collection.[].let`.
let accessLet: Function.ID

/// Creates the witness of the conformance `c` for use in `module`.
init(_ c: Core.Conformance, in module: inout Module) {
self.index = module.program.associatedType("Index", for: c)
self.element = module.program.associatedType("Element", for: c)

self.startIndex = .constant(
module.reference(toImplementationOf: Name(stem: "start_index"), for: c))
self.endIndex = .constant(
module.reference(toImplementationOf: Name(stem: "end_index"), for: c))
self.indexAfter = .constant(
module.reference(toImplementationOf: Name(stem: "index", labels: ["after"]), for: c))

self.accessLet = module.demandImplementation(
of: Name(stem: "[]", labels: [nil], introducer: .let), for: c)
}

}
Loading

0 comments on commit 7ff17dc

Please sign in to comment.