Skip to content
Draft
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
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- uses: actions/checkout@v4

- name: Set up Swift ${{ matrix.swift }} on ${{ matrix.os }}
uses: SwiftyLab/setup-swift@latest
uses: SwiftyLab/setup-swift@v1.12.0
with:
swift-version: ${{ matrix.swift }}

Expand Down
24 changes: 24 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Lint
on:
push:
paths-ignore:
- "**.md"
- "LICENSE"
- ".gitignore"
- ".editorconfig"

jobs:
lint:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Set up Swift 6.2
uses: SwiftyLab/setup-swift@v1.12.0
with:
swift-version: "6.2"

- name: Lint
run: ./lint.sh

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/.vscode
/.swiftpm
/.vscode/launch.json
/.idea

# macOS
.DS_Store
Expand Down
6 changes: 3 additions & 3 deletions .swift-format.json → .swift-format
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"AllPublicDeclarationsHaveDocumentation" : false,
"AlwaysUseLowerCamelCase" : true,
"AmbiguousTrailingClosureOverload" : true,
"BeginDocumentationCommentWithOneLineSummary" : true,
"BeginDocumentationCommentWithOneLineSummary" : false,
"DoNotUseSemicolons" : true,
"DontRepeatTypeInStaticProperties" : true,
"FileScopedDeclarationPrivacy" : true,
Expand All @@ -30,7 +30,7 @@
"NeverUseForceTry" : false,
"NeverUseImplicitlyUnwrappedOptionals" : false,
"NoAccessLevelOnExtensionDeclaration" : true,
"NoAssignmentInExpressions" : true,
"NoAssignmentInExpressions" : false,
"NoBlockComments" : true,
"NoCasesWithOnlyFallthrough" : true,
"NoEmptyTrailingClosureParentheses" : true,
Expand All @@ -40,7 +40,7 @@
"NoVoidReturnOnFunctionSignature" : true,
"OneCasePerLine" : true,
"OneVariableDeclarationPerLine" : true,
"OnlyOneTrailingClosureArgument" : true,
"OnlyOneTrailingClosureArgument" : false,
"OrderedImports" : true,
"ReturnVoidInsteadOfEmptyTuple" : true,
"UseEarlyExits" : false,
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,10 @@ If `m` denotes a trait requirement, then name resolution should bind it to the e
## Questions

- Is it desirable to write extensions of context functions?

## Development
### Code formatting
The project uses `swift-format` bundled by the Swift toolchain to enforce a consistent code style.

- Use `./format.sh` to format the code.
- Use `./lint.sh` to check the code.
2 changes: 1 addition & 1 deletion Sources/FrontEnd/Files/SourceFileIdentity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ extension SourceFile {

/// The file offset of the node represented by `self` in its containing collection.
public var offset: Int {
.init((rawValue & 0xffff0000) >> 16)
.init((rawValue & 0xffff_0000) >> 16)
}

/// Returns the contents of `self`.
Expand Down
11 changes: 6 additions & 5 deletions Sources/FrontEnd/Files/SourceSpan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,12 @@ extension SourceSpan: CustomStringConvertible {
/// [GNU coding standards](https://www.gnu.org/prep/standards/html_node/Errors.html) whose path
/// is shown as `pathStyle`.
public func gnuStandardText(showingPath pathStyle: FileName.PathStyle = .absolute) -> String {
let n: String = if case .relative(let u) = pathStyle {
source.name.gnuPath(relativeTo: u) ?? source.name.description
} else {
source.name.description
}
let n: String =
if case .relative(let u) = pathStyle {
source.name.gnuPath(relativeTo: u) ?? source.name.description
} else {
source.name.description
}

let s = start.lineAndColumn
let h = "\(n):\(s.line).\(s.column)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ extension AbstractContext: Showable {
/// Returns a textual representation of `self` using `printer`.
internal func show(using printer: inout TreePrinter) -> String {
let ls = printer.show(locals)
let ms = memory
let ms =
memory
.sorted(by: \.key, using: Self.areInIncreasingOrder(_:_:))
.reduce(into: "", { (s, p) in s += "\(printer.show(p.key)) ↦ \(printer.show(p.value))\n" })

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ internal struct AbstractMachine<Transfer: AbstractTransferFunction> {

/// The knowledge of the abstract interpreter about a single block.
private typealias BlockState = (
sources: SortedSet<IRBlock.ID>, pre: Transfer.Context, post: Transfer.Context)
sources: SortedSet<IRBlock.ID>, pre: Transfer.Context, post: Transfer.Context
)

/// A map from basic block to the machine's state before and after the block's execution.
private typealias State = [IRBlock.ID: BlockState]
Expand Down
74 changes: 38 additions & 36 deletions Sources/FrontEnd/IR/IREmitter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -211,17 +211,19 @@ internal struct IREmitter {
lowering(r, { $0._return() })

case .next:
lowering(after: body.last!, { (me) in
// If the function returns `Void`, assume the return register is initialized to deal with
// elided return statements.
if me.currentFunction.isProcedure {
let r = me.currentFunction.returnRegister!
me._assume_state(r, initialized: true)
}
lowering(
after: body.last!,
{ (me) in
// If the function returns `Void`, assume the return register is initialized to deal with
// elided return statements.
if me.currentFunction.isProcedure {
let r = me.currentFunction.returnRegister!
me._assumeState(r, initialized: true)
}

// Add a return statement to terminate the block.
me._return()
})
// Add a return statement to terminate the block.
me._return()
})
}

program[module][ir: f] = insertionContext.function.sink()
Expand Down Expand Up @@ -386,7 +388,7 @@ internal struct IREmitter {
if let e = program[s].value {
lower(store: e, to: r)
} else if currentFunction.result(of: r)?.type == .void {
lowering(s, { $0._assume_state(r, initialized: true) })
lowering(s, { $0._assumeState(r, initialized: true) })
}

// The return instruction is emitted by the caller handling this control-flow effect.
Expand Down Expand Up @@ -559,7 +561,7 @@ internal struct IREmitter {
private mutating func lower(store e: TupleLiteral.ID, to target: IRValue) {
// Just mark the storage initialized if the literal is empty.
if program[e].elements.isEmpty {
lowering(e, { $0._assume_state(target, initialized: true) })
lowering(e, { $0._assumeState(target, initialized: true) })
return
}

Expand Down Expand Up @@ -681,7 +683,7 @@ internal struct IREmitter {

case .typeApplication(let f, let a):
let poly = loweredCallee(f, at: site, in: scope)
let mono = lowering(at: site, in: scope) { (me) in me._type_apply(poly.value, to: a) }
let mono = lowering(at: site, in: scope) { (me) in me._typeApply(poly.value, to: a) }
return LoweredCallee(value: mono, operands: poly.operands)

default:
Expand Down Expand Up @@ -860,7 +862,7 @@ internal struct IREmitter {
// Other declarations have capture and parameter lists.
else {
let parameters = program.parametersAndCaptures(of: d)
assert(parameters.captures.isEmpty) // TODO
assert(parameters.captures.isEmpty) // TODO

// Using parameters come first.
for p in parameters.usings {
Expand Down Expand Up @@ -1044,7 +1046,6 @@ internal struct IREmitter {
return r
}


/// Returns the result of calling `action` on `self` with the insertion context configured to
/// emit new instructions at `p` in `f`, anchoring them to `a`.
private mutating func lowering<R>(
Expand Down Expand Up @@ -1178,16 +1179,17 @@ internal struct IREmitter {
@discardableResult
private mutating func insert<T: Instruction>(_ instruction: T) -> IRValue? {
modify(&insertionContext.function!) { [p = insertionContext.point!] (f) in
let i: AnyInstructionIdentity = switch p {
case .before(let i):
f.insert(instruction, before: i)
case .after(let i):
f.insert(instruction, after: i)
case .start(let b):
f.prepend(instruction, to: b)
case .end(let b):
f.append(instruction, to: b)
}
let i: AnyInstructionIdentity =
switch p {
case .before(let i):
f.insert(instruction, before: i)
case .after(let i):
f.insert(instruction, after: i)
case .start(let b):
f.prepend(instruction, to: b)
case .end(let b):
f.append(instruction, to: b)
}
return f.definition(i)
}
}
Expand Down Expand Up @@ -1241,7 +1243,7 @@ internal struct IREmitter {
}

/// Inserts a `apply_builtin` instruction.
internal mutating func _apply_builtin(
internal mutating func _applyBuiltin(
_ callee: BuiltinFunction, to arguments: [IRValue]
) -> IRValue {
let f = BuiltinFunction.trap.type(uniquingTypesWith: &program.types)
Expand All @@ -1254,7 +1256,7 @@ internal struct IREmitter {
}

/// Inserts a `assume_state` instruction.
internal mutating func _assume_state(_ s: IRValue, initialized: Bool) {
internal mutating func _assumeState(_ s: IRValue, initialized: Bool) {
insert(IRAssumeState(storage: s, initialized: initialized, anchor: currentAnchor))
}

Expand Down Expand Up @@ -1286,7 +1288,7 @@ internal struct IREmitter {
}

/// Inserts a `memcpy` instruction.
internal mutating func _memory_copy(_ source: IRValue, to target: IRValue) {
internal mutating func _memoryCopy(_ source: IRValue, to target: IRValue) {
assert(currentFunction.isAddress(source))
assert(currentFunction.isAddress(target))
insert(IRMemoryCopy(source: source, target: target, anchor: currentAnchor))
Expand Down Expand Up @@ -1351,7 +1353,7 @@ internal struct IREmitter {
}

/// Inserts a `type_apply` instruction.
internal mutating func _type_apply(
internal mutating func _typeApply(
_ callee: IRValue, to arguments: TypeArguments
) -> IRValue {
// The callee must have a universal type.
Expand Down Expand Up @@ -1437,7 +1439,7 @@ internal struct IREmitter {

case .typeApplication(let f, let a):
let x = _emit(witness: f)
return _type_apply(x, to: a)
return _typeApply(x, to: a)

default:
fatalError()
Expand All @@ -1451,19 +1453,19 @@ internal struct IREmitter {

// Nothing to do for machine types.
if program.types.tag(of: typeOfSource) == MachineType.self {
_assume_state(source, initialized: false)
_assumeState(source, initialized: false)
return true
}

// Other types need a conformance to `Hylo.Deinitializable`.
guard let w = conformanceWitness(of: typeOfSource, is: .deinitializable) else {
_ = _apply_builtin(.trap, to: [])
_ = _applyBuiltin(.trap, to: [])
return false
}

// Does the conformance have any operational semantics.
if program.isTransitivelySyntheticConformance(w) {
_assume_state(source, initialized: false)
_assumeState(source, initialized: false)
return true
}

Expand Down Expand Up @@ -1542,15 +1544,15 @@ internal struct IREmitter {

// Other types require a conformance to `Hylo.Movable`.
guard let w = conformanceWitness(of: typeOfSource, is: .movable) else {
_ = _apply_builtin(.trap, to: [])
_ = _applyBuiltin(.trap, to: [])
return
}

// Does the conformance have any operational semantics.
if program.isTransitivelySyntheticConformance(w) {
let x0 = _access([.sink], from: source)
let x1 = _access([.set], from: target)
_memory_copy(x0, to: x1)
_memoryCopy(x0, to: x1)
_end(IRAccess.self, openedBy: x1)
_end(IRAccess.self, openedBy: x0)
return
Expand Down Expand Up @@ -1669,6 +1671,6 @@ extension Program {
}

/// Indicates an invalid IR operand.
fileprivate func badOperand(file: StaticString = #file, line: UInt = #line) -> Never {
private func badOperand(file: StaticString = #file, line: UInt = #line) -> Never {
preconditionFailure("bad operand", file: file, line: line)
}
7 changes: 3 additions & 4 deletions Sources/FrontEnd/IR/IRFunction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public struct IRFunction: Sendable {
!blocks.isEmpty
}

/// `true` iff the function returns a unit value (i.e., an isntance of `Hylo.Void`).
/// `true` iff the function returns a unit value (i.e., an isntance of `Hylo.Void`).
public var isProcedure: Bool {
returnRegister.flatMap(result(of:))?.type == .void
}
Expand Down Expand Up @@ -475,7 +475,6 @@ public struct IRFunction: Sendable {
}
}


/// Inserts `instruction` immediately after `j` and returns its identity.
@discardableResult
public mutating func insert<T: Instruction>(
Expand Down Expand Up @@ -543,7 +542,7 @@ public struct IRFunction: Sendable {
/// iff `old` was a successor of `source`.
internal mutating func replaceSuccessor(
_ old: IRBlock.ID, of source: IRBlock.ID, for new: IRBlock.ID
) -> Bool {
) -> Bool {
let l = blocks[source].last!
if var s = at(l) as? any Terminator, s.replaceSuccessor(old, with: new) {
slots[l.address].assign(s)
Expand Down Expand Up @@ -623,7 +622,7 @@ extension IRFunction: Showable {

result.append("(")
for (i, p) in termParameters.enumerated() {
if (i != 0) { result.append(", ") }
if i != 0 { result.append(", ") }
result.append("\(p.access) \(printer.show(IRValue.parameter(i))): \(printer.show(p.type))")
}
result.append(")")
Expand Down
1 change: 0 additions & 1 deletion Sources/FrontEnd/IR/IRValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,3 @@ extension IRValue: Showable {
}

}

2 changes: 1 addition & 1 deletion Sources/FrontEnd/IR/Instructions/IRApply.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public struct IRApply: Instruction {
result: IRValue,
anchor: Anchor
) {
var operands = Array<IRValue>(minimumCapacity: arguments.count + 2)
var operands = [IRValue](minimumCapacity: arguments.count + 2)
operands.append(callee)
operands.append(contentsOf: arguments)
operands.append(result)
Expand Down
2 changes: 1 addition & 1 deletion Sources/FrontEnd/IR/Instructions/IRLoad.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/// If the source is not a machine type, the operation requires exclusive access to the source,
/// which must be initialized before the operation and is left uninitialized after. For machine
/// types, we always copy the values, so no consume semantics.
///
///
/// The size of the value being loaded must be known at compile-time.
public struct IRLoad: Instruction {

Expand Down
2 changes: 1 addition & 1 deletion Sources/FrontEnd/IR/Instructions/IRProject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public struct IRProject: IRRegionEntry {
access: AccessEffect,
anchor: Anchor
) {
var operands = Array<IRValue>(minimumCapacity: arguments.count + 1)
var operands = [IRValue](minimumCapacity: arguments.count + 1)
operands.append(callee)
operands.append(contentsOf: arguments)

Expand Down
Loading
Loading