From f619e5fd9a80e97d4163f0547b12f10ebc6004ca Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Mon, 25 Sep 2023 18:30:13 +0200 Subject: [PATCH 001/157] Move 'TypedProgram.accumulatedGenericParameters' to the type checker --- .../FrontEnd/TypeChecking/TypeChecker.swift | 55 +++++++++++++++++++ Sources/FrontEnd/TypedProgram.swift | 41 +------------- 2 files changed, 57 insertions(+), 39 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index cf3f5f472..5cd87d192 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -3295,6 +3295,61 @@ struct TypeChecker { return result } + /// Returns generic parameters captured by `s` and the scopes semantically containing `s`. + /// + /// A declaration may take generic parameters even if it doesn't declare any. For example, a + /// nested function will implicitly capture the generic parameters introduced in its context. + /// + /// Parameters are returned outer to inner, left to right: the first parameter of the outermost + /// generic scope appears first; the last parameter of the innermost generic scope appears last. + public mutating func accumulatedGenericParameters( + in s: T + ) -> ReversedCollection<[GenericParameterDecl.ID]> { + var result: [GenericParameterDecl.ID] = [] + appendGenericParameters(in: s, to: &result) + return result.reversed() + } + + /// Appends generic parameters captured by `s` and the scopes semantically containing `s` to + /// `accumulatedParameters`, right to left, inner to outer. + private mutating func appendGenericParameters( + in s: T, to accumulatedParameters: inout [GenericParameterDecl.ID] + ) { + switch s.kind.value { + case is ConformanceDecl.Type: + appendGenericParameters(in: ConformanceDecl.ID(s)!, to: &accumulatedParameters) + case is ExtensionDecl.Type: + appendGenericParameters(in: ExtensionDecl.ID(s)!, to: &accumulatedParameters) + case is GenericScope.Type: + accumulatedParameters.append(contentsOf: (program.ast[s] as! GenericScope).genericParameters) + case is TranslationUnit.Type, is ModuleDecl.Type: + return + default: + break + } + + appendGenericParameters(in: program[s].scope, to: &accumulatedParameters) + } + + /// Appends generic parameters captured by `s` and the scopes semantically containing `s` to + /// `accumulatedParameters`, right to left, inner to outer. + private mutating func appendGenericParameters( + in d: T.ID, to accumulatedParameters: inout [GenericParameterDecl.ID] + ) { + guard let p = scopeExtended(by: d) else { return } + appendGenericParameters(in: p, to: &accumulatedParameters) + } + + /// Appends generic parameters captured by `s` and the scopes semantically containing `s` to + /// `accumulatedParameters`, right to left, inner to outer. + private func appendGenericParameters( + in d: T.ID, to accumulatedParameters: inout [GenericParameterDecl.ID] + ) { + if let clause = program[d].genericClause { + accumulatedParameters.append(contentsOf: clause.value.parameters) + } + } + // MARK: Quantifier elimination /// A context in which a generic parameter can be instantiated. diff --git a/Sources/FrontEnd/TypedProgram.swift b/Sources/FrontEnd/TypedProgram.swift index fabafcc90..978ed2bf5 100644 --- a/Sources/FrontEnd/TypedProgram.swift +++ b/Sources/FrontEnd/TypedProgram.swift @@ -265,48 +265,11 @@ public struct TypedProgram { } /// Returns generic parameters captured by `s` and the scopes semantically containing `s`. - /// - /// A declaration may take generic parameters even if it doesn't declare any. For example, a - /// nested function will implicitly capture the generic parameters introduced in its context. - /// - /// Parameters are returned outer to inner, left to right: the first parameter of the outermost - /// generic scope appears first; the last parameter of the innermost generic scope appears last. public func accumulatedGenericParameters( in s: T ) -> ReversedCollection<[GenericParameterDecl.ID]> { - var result: [GenericParameterDecl.ID] = [] - appendGenericParameters(in: s, to: &result) - return result.reversed() - } - - /// Appends generic parameters captured by `s` and the scopes semantically containing `s` to - /// `accumulatedParameters`, right to left, inner to outer. - private func appendGenericParameters( - in s: T, to accumulatedParameters: inout [GenericParameterDecl.ID] - ) { - switch s.kind.value { - case is ConformanceDecl.Type: - appendGenericParameters(in: ConformanceDecl.ID(s)!, to: &accumulatedParameters) - case is ExtensionDecl.Type: - appendGenericParameters(in: ExtensionDecl.ID(s)!, to: &accumulatedParameters) - case is GenericScope.Type: - accumulatedParameters.append(contentsOf: environment[AnyDeclID(s)!]!.parameters) - case is TranslationUnit.Type, is ModuleDecl.Type: - return - default: - break - } - - appendGenericParameters(in: nodeToScope[s]!, to: &accumulatedParameters) - } - - /// Appends generic parameters captured by `s` and the scopes semantically containing `s` to - /// `accumulatedParameters`, right to left, inner to outer. - private func appendGenericParameters( - in d: T.ID, to accumulatedParameters: inout [GenericParameterDecl.ID] - ) { - guard let p = scopeExtended(by: d) else { return } - appendGenericParameters(in: p, to: &accumulatedParameters) + var checker = TypeChecker(asContextFor: self) + return checker.accumulatedGenericParameters(in: s) } /// Returns `true` iff `model` conforms to `concept` in `scopeOfUse`. From 448bd5bd9c38ee96c043e15f09842e7a410ed6bc Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Mon, 25 Sep 2023 18:32:05 +0200 Subject: [PATCH 002/157] Add an initializer to create skolemized generic argument lists --- Sources/Core/CompileTimeValues/GenericArguments.swift | 10 ++++++++++ Sources/FrontEnd/TypeChecking/TypeChecker.swift | 5 +---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Sources/Core/CompileTimeValues/GenericArguments.swift b/Sources/Core/CompileTimeValues/GenericArguments.swift index e0017f7ec..48ba07a40 100644 --- a/Sources/Core/CompileTimeValues/GenericArguments.swift +++ b/Sources/Core/CompileTimeValues/GenericArguments.swift @@ -31,6 +31,16 @@ public struct GenericArguments { self.contents = .init(uniqueKeysWithValues: keysAndValues) } + /// Creates an instance mapping each element of `parameters`, which is defined in `ast`, to its + /// skolemized form. + public init( + skolemizing parameters: S, in ast: AST + ) where S.Element == GenericParameterDecl.ID { + self.contents = .init(uniqueKeysWithValues: parameters.map { (p) in + (key: p, value: ^GenericTypeParameterType(p, ast: ast)) + }) + } + /// A collection with the parameters to which arguments are passed. public var keys: some Collection { contents.keys diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 5cd87d192..fe599e244 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -3180,10 +3180,7 @@ struct TypeChecker { return unspecialized } - let arguments = GenericArguments( - uniqueKeysWithValues: parameters.map({ (p) in - (key: p, value: ^GenericTypeParameterType(p, ast: program.ast)) - })) + let arguments = GenericArguments(skolemizing: parameters, in: program.ast) return MetatypeType(of: BoundGenericType(unspecialized.instance, arguments: arguments)) } From dfbf014522b13e0b81d3e29e994faa0948f1572d Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Mon, 25 Sep 2023 18:33:22 +0200 Subject: [PATCH 003/157] Avoid overspecializing function references --- Sources/IR/Emitter.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 045cf12e2..f5755632a 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -1697,18 +1697,16 @@ struct Emitter { UNIMPLEMENTED() } - let specialization = module.specialization(in: insertionFunction!).merging(a) let f = FunctionReference( - to: FunctionDecl.ID(d)!, in: &module, specializedBy: specialization, in: insertionScope!) + to: FunctionDecl.ID(d)!, in: &module, specializedBy: a, in: insertionScope!) return (.direct(f), []) case .member(let d, let a, let s): // Callee is a member reference to a function or method. Its receiver is the only capture. - let specialization = module.specialization(in: insertionFunction!).merging(a) let receiver = emitLValue(receiver: s, at: ast[callee].site) let k = receiverCapabilities(program[callee].type) let c = insert(module.makeAccess(k, from: receiver, at: ast[callee].site))! - let f = Callee(d, specializedBy: specialization, in: &module, usedIn: insertionScope!) + let f = Callee(d, specializedBy: a, in: &module, usedIn: insertionScope!) return (f, [c]) case .builtinFunction, .builtinType: From bb403ac4fc1fdd7ff477d44081bff960d3bea538 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Mon, 25 Sep 2023 18:34:05 +0200 Subject: [PATCH 004/157] Gather implicit specialization arguments in unqualified lookup --- .../FrontEnd/TypeChecking/TypeChecker.swift | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index fe599e244..1b7eebbed 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -2941,17 +2941,19 @@ struct TypeChecker { guard var candidateType = resolveType(of: m) else { continue } var log = DiagnosticSet() + // The specialization of the match includes that of context in which it was looked up. + var specialization = genericArguments(inScopeIntroducing: m, resolvedIn: context) + // Keep track of generic arguments that should be captured later on. let candidateSpecialization = genericArguments( passedTo: m, typed: candidateType, referredToBy: name, specializedBy: arguments, reportingDiagnosticsTo: &log) + for (p, a) in candidateSpecialization { + specialization[p] = a + } - // The specialization of the match includes that of context in which it was looked up. - var specialization = context?.arguments ?? [:] - - // If the match is a trait member, specialize its receiver as necessary. + // If the match is a trait member looked up with qualification, specialize its receiver. if let t = program.trait(defining: m) { - assert(specialization[program[t].receiver] == nil) specialization[program[t].receiver] = context?.type } @@ -2971,7 +2973,6 @@ struct TypeChecker { } } - specialization.append(candidateSpecialization) candidateType = specialize(candidateType, for: specialization, in: scopeOfUse) let r = program.makeReference( @@ -3246,6 +3247,24 @@ struct TypeChecker { } } + /// Returns the list of generic arguments passed to a symbol occurring in `scope` of use and + /// looked up in `context`. + /// + /// The arguments of `context` are returned if the latter isn't `nil`. Otherwise, the arguments + /// captured in the scope introducing `d` are returned in the form of a table mapping accumulated + /// generic parameters to a skolem. + private mutating func genericArguments( + inScopeIntroducing d: AnyDeclID, resolvedIn context: NameResolutionContext? + ) -> GenericArguments { + if let a = context?.arguments { return a } + + // References to modules can never capture any generic arguments. + if d.kind == ModuleDecl.self { return [:] } + + let parameters = accumulatedGenericParameters(in: program[d].scope) + return .init(skolemizing: parameters, in: program.ast) + } + /// Associates `parameters`, which are introduced by `name`'s declaration, to corresponding /// values in `arguments` if the two arrays have the same length. Otherwise, returns `nil`, /// reporting diagnostics to `log`. From afbf0c0d1c20d475932fc541c9e94dd39d583869 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Mon, 25 Sep 2023 18:34:19 +0200 Subject: [PATCH 005/157] Fix typo in comments --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 1b7eebbed..864374761 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -1015,8 +1015,8 @@ struct TypeChecker { return specialize(t, for: arguments, in: scopeOfExposition) } - /// Returns a concrete or syntheszed implementation of requirement `r` in `concept` for `model` - /// exposed to `scopeOfUse`, or `nil` if no such implementation exist. + /// Returns a concrete or synthesized implementation of requirement `r` in `concept` for + /// `model` exposed to `scopeOfUse`, or `nil` if no such implementation exist. func implementation(of r: AnyDeclID) { // FIXME: remove me if r.kind == AssociatedTypeDecl.self { return } From fdb53ad73566ff7486d274a25d5012a52b681c5c Mon Sep 17 00:00:00 2001 From: Nils Hjelte Date: Tue, 26 Sep 2023 08:08:08 +0200 Subject: [PATCH 006/157] Add missing introducerSite for type declarations --- Sources/Core/AST/Decl/ConformanceDecl.swift | 5 +++++ Sources/Core/AST/Decl/ExtensionDecl.swift | 5 +++++ Sources/Core/AST/Decl/ProductTypeDecl.swift | 5 +++++ Sources/Core/AST/Decl/TypeAliasDecl.swift | 5 +++++ Sources/FrontEnd/Parse/Parser.swift | 4 ++++ 5 files changed, 24 insertions(+) diff --git a/Sources/Core/AST/Decl/ConformanceDecl.swift b/Sources/Core/AST/Decl/ConformanceDecl.swift index 4014a0d17..0a363fa78 100644 --- a/Sources/Core/AST/Decl/ConformanceDecl.swift +++ b/Sources/Core/AST/Decl/ConformanceDecl.swift @@ -5,6 +5,9 @@ public struct ConformanceDecl: TypeExtendingDecl { public let site: SourceRange + /// The site of the `conformance` introducer. + public let introducerSite: SourceRange + /// The access modifier of the declaration, if any. public let accessModifier: SourceRepresentable @@ -22,6 +25,7 @@ public struct ConformanceDecl: TypeExtendingDecl { /// Creates an instance with the given properties. public init( + introducerSite: SourceRange, accessModifier: SourceRepresentable, subject: AnyExprID, conformances: [NameExpr.ID], @@ -29,6 +33,7 @@ public struct ConformanceDecl: TypeExtendingDecl { members: [AnyDeclID], site: SourceRange ) { + self.introducerSite = introducerSite self.site = site self.accessModifier = accessModifier self.subject = subject diff --git a/Sources/Core/AST/Decl/ExtensionDecl.swift b/Sources/Core/AST/Decl/ExtensionDecl.swift index 3f16999ee..a8aca9d7b 100644 --- a/Sources/Core/AST/Decl/ExtensionDecl.swift +++ b/Sources/Core/AST/Decl/ExtensionDecl.swift @@ -5,6 +5,9 @@ public struct ExtensionDecl: TypeExtendingDecl { public let site: SourceRange + /// The site of the `extension` introducer. + public let introducerSite: SourceRange + /// The access modifier of the declaration, if any. public let accessModifier: SourceRepresentable @@ -19,12 +22,14 @@ public struct ExtensionDecl: TypeExtendingDecl { /// Creates an instance with the given properties. public init( + introducerSite: SourceRange, accessModifier: SourceRepresentable, subject: AnyExprID, whereClause: SourceRepresentable?, members: [AnyDeclID], site: SourceRange ) { + self.introducerSite = introducerSite self.site = site self.accessModifier = accessModifier self.subject = subject diff --git a/Sources/Core/AST/Decl/ProductTypeDecl.swift b/Sources/Core/AST/Decl/ProductTypeDecl.swift index c979e0968..4ce39d622 100644 --- a/Sources/Core/AST/Decl/ProductTypeDecl.swift +++ b/Sources/Core/AST/Decl/ProductTypeDecl.swift @@ -5,6 +5,9 @@ public struct ProductTypeDecl: ExposableDecl, GenericDecl, SingleEntityDecl, Gen public let site: SourceRange + /// The site of the `type` introducer. + public let introducerSite: SourceRange + /// The access modifier of the declaration, if any. public let accessModifier: SourceRepresentable @@ -22,6 +25,7 @@ public struct ProductTypeDecl: ExposableDecl, GenericDecl, SingleEntityDecl, Gen /// Creates an instance with the given properties. public init( + introducerSite: SourceRange, accessModifier: SourceRepresentable, identifier: SourceRepresentable, genericClause: SourceRepresentable?, @@ -29,6 +33,7 @@ public struct ProductTypeDecl: ExposableDecl, GenericDecl, SingleEntityDecl, Gen members: [AnyDeclID], site: SourceRange ) { + self.introducerSite = introducerSite self.site = site self.accessModifier = accessModifier self.identifier = identifier diff --git a/Sources/Core/AST/Decl/TypeAliasDecl.swift b/Sources/Core/AST/Decl/TypeAliasDecl.swift index e15263da3..6afd41755 100644 --- a/Sources/Core/AST/Decl/TypeAliasDecl.swift +++ b/Sources/Core/AST/Decl/TypeAliasDecl.swift @@ -5,6 +5,9 @@ public struct TypeAliasDecl: ExposableDecl, GenericDecl, SingleEntityDecl, Gener public let site: SourceRange + /// The site of the `typealias` introducer. + public let introducerSite: SourceRange + /// The access modifier of the declaration, if any. public let accessModifier: SourceRepresentable @@ -19,12 +22,14 @@ public struct TypeAliasDecl: ExposableDecl, GenericDecl, SingleEntityDecl, Gener /// Creates an instance with the given properties. public init( + introducerSite: SourceRange, accessModifier: SourceRepresentable, identifier: SourceRepresentable, genericClause: SourceRepresentable?, aliasedType: AnyExprID, site: SourceRange ) { + self.introducerSite = introducerSite self.site = site self.accessModifier = accessModifier self.identifier = identifier diff --git a/Sources/FrontEnd/Parse/Parser.swift b/Sources/FrontEnd/Parse/Parser.swift index 32ebe3c97..32aca4bfd 100644 --- a/Sources/FrontEnd/Parse/Parser.swift +++ b/Sources/FrontEnd/Parse/Parser.swift @@ -464,6 +464,7 @@ public enum Parser { // Create a new `ConformanceDecl`. return state.insert( ConformanceDecl( + introducerSite: parts.0.0.0.0.site, accessModifier: declAccessModifier(ofDeclPrologue: prologue, in: &state), subject: parts.0.0.0.1, conformances: parts.0.0.1, @@ -498,6 +499,7 @@ public enum Parser { // Create a new `ExtensionDecl`. return state.insert( ExtensionDecl( + introducerSite: parts.0.0.0.site, accessModifier: declAccessModifier(ofDeclPrologue: prologue, in: &state), subject: parts.0.0.1, whereClause: parts.0.1, @@ -1018,6 +1020,7 @@ public enum Parser { // Create a new `ProductTypeDecl`. return state.insert( ProductTypeDecl( + introducerSite: parts.0.0.0.0.site, accessModifier: declAccessModifier(ofDeclPrologue: prologue, in: &state), identifier: state.token(parts.0.0.0.1), genericClause: parts.0.0.1, @@ -1053,6 +1056,7 @@ public enum Parser { // Create a new `TypeAliasDecl`. return state.insert( TypeAliasDecl( + introducerSite: parts.0.0.0.0.site, accessModifier: declAccessModifier(ofDeclPrologue: prologue, in: &state), identifier: state.token(parts.0.0.0.1), genericClause: parts.0.0.1, From a5d0cd8d16ab2e0853f5672484fedc5eecd2ab41 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 26 Sep 2023 22:32:35 +0200 Subject: [PATCH 007/157] Avoid overspecialization of implicit calls --- Sources/IR/Emitter.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index f5755632a..84f3f1690 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -2492,7 +2492,7 @@ struct Emitter { private func reference( to d: Function.ID, implementedFor c: Core.Conformance ) -> FunctionReference { - var a = module.specialization(in: insertionFunction!).merging(c.arguments) + var a = c.arguments if let m = program.traitMember(referredBy: d) { a = a.merging([program[m.trait.decl].receiver: c.model]) } From dade466791ec3864209f90209985fda22e228a41 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 26 Sep 2023 22:34:44 +0200 Subject: [PATCH 008/157] Add an IR instruction representing constant strings --- .../Operands/Instruction/ConstantString.swift | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Sources/IR/Operands/Instruction/ConstantString.swift diff --git a/Sources/IR/Operands/Instruction/ConstantString.swift b/Sources/IR/Operands/Instruction/ConstantString.swift new file mode 100644 index 000000000..d93b45115 --- /dev/null +++ b/Sources/IR/Operands/Instruction/ConstantString.swift @@ -0,0 +1,47 @@ +import Core +import Foundation + +/// Creates a pointer to a constant string allocated statically. +public struct ConstantString: Instruction { + + /// The value of the string, encoded in UTF-8. + public let value: Data + + /// The site of the code corresponding to that instruction. + public let site: SourceRange + + /// Creates an instance with the given properties. + fileprivate init(value: Data, site: SourceRange) { + self.value = value + self.site = site + } + + public var result: IR.`Type`? { + .object(BuiltinType.ptr) + } + + public var operands: [Operand] { [] } + + public mutating func replaceOperand(at i: Int, with new: Operand) { + preconditionFailure() + } + +} + +extension ConstantString: CustomStringConvertible { + + public var description: String { + "constant_string \"\(value)\"" + } + +} + +extension Module { + + /// Creates a `constant_string` anchored at `site` that returns a pointer to a statically + /// allocated string with given `value`, encoded in UTF8. + func makeConstantString(utf8 value: Data, at site: SourceRange) -> ConstantString { + .init(value: value, site: site) + } + +} From 2574a88e9b8c4370462fda5b5a6f6e2929b19ecc Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 26 Sep 2023 22:41:24 +0200 Subject: [PATCH 009/157] Use 'constant_string' to lower string literals --- Sources/IR/Emitter.swift | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 84f3f1690..2bba51372 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -1440,19 +1440,14 @@ struct Emitter { /// /// - Requires: `storage` is the address of uninitialized memory of type `Hylo.String`. private mutating func emitStore(string v: String, to storage: Operand, at site: SourceRange) { - var bytes = v.unescaped.data(using: .utf8)! - let utf8 = PointerConstant(module.id, module.addGlobal(BufferConstant(bytes))) - let size = bytes.count - - // Make sure the string is null-terminated. - bytes.append(contentsOf: [0]) + let bytes = v.unescaped.data(using: .utf8)! let x0 = emitSubfieldView(storage, at: [0], at: site) - emitStore(int: size, to: x0, at: site) + emitStore(int: bytes.count, to: x0, at: site) let x1 = emitSubfieldView(storage, at: [1, 0], at: site) - let x2 = insert(module.makeAccess(.set, from: x1, at: site))! - insert(module.makeStore(.constant(utf8), at: x2, at: site)) + let x2 = insert(module.makeConstantString(utf8: bytes, at: site))! + emitStore(value: x2, to: x1, at: site) } /// Inserts the IR for storing `a`, which is an `access`, to `storage`. From 8b344bf67c7d8f62f98ded36996e0670c8b75d62 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 26 Sep 2023 22:42:08 +0200 Subject: [PATCH 010/157] Handle 'constant_string' during IR analysis --- Sources/IR/Analysis/Module+Depolymorphize.swift | 8 ++++++++ Sources/IR/Analysis/Module+NormalizeObjectStates.swift | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/Sources/IR/Analysis/Module+Depolymorphize.swift b/Sources/IR/Analysis/Module+Depolymorphize.swift index 6ebfa5b79..70d9ea6cb 100644 --- a/Sources/IR/Analysis/Module+Depolymorphize.swift +++ b/Sources/IR/Analysis/Module+Depolymorphize.swift @@ -185,6 +185,8 @@ extension Module { rewrite(closeUnion: i, to: b) case is CondBranch: rewrite(condBranch: i, to: b) + case is ConstantString: + rewrite(constantString: i, to: b) case is DeallocStack: rewrite(deallocStack: i, to: b) case is EndAccess: @@ -311,6 +313,12 @@ extension Module { append(newInstruction, to: b) } + /// Rewrites `i`, which is in `r.function`, into `result`, at the end of `b`. + func rewrite(constantString i: InstructionID, to b: Block.ID) { + let s = sourceModule[i] as! ConstantString + append(makeConstantString(utf8: s.value, at: s.site), to: b) + } + /// Rewrites `i`, which is in `r.function`, into `result`, at the end of `b`. func rewrite(deallocStack i: InstructionID, to b: Block.ID) { let s = sourceModule[i] as! DeallocStack diff --git a/Sources/IR/Analysis/Module+NormalizeObjectStates.swift b/Sources/IR/Analysis/Module+NormalizeObjectStates.swift index 9858540fe..0033acebd 100644 --- a/Sources/IR/Analysis/Module+NormalizeObjectStates.swift +++ b/Sources/IR/Analysis/Module+NormalizeObjectStates.swift @@ -44,6 +44,8 @@ extension Module { pc = interpret(closeUnion: user, in: &context) case is CondBranch: pc = interpret(condBranch: user, in: &context) + case is ConstantString: + pc = interpret(constantString: user, in: &context) case is DeallocStack: pc = interpret(deallocStack: user, in: &context) case is EndAccess: @@ -271,6 +273,12 @@ extension Module { return successor(of: i) } + /// Interprets `i` in `context`, reporting violations into `diagnostics`. + func interpret(constantString i: InstructionID, in context: inout Context) -> PC? { + initializeRegister(createdBy: i, in: &context) + return successor(of: i) + } + /// Interprets `i` in `context`, reporting violations into `diagnostics`. func interpret(deallocStack i: InstructionID, in context: inout Context) -> PC? { let s = self[i] as! DeallocStack From 3b4164b9a191fd8f0305a28fb30eaad1bad4094b Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 26 Sep 2023 22:43:00 +0200 Subject: [PATCH 011/157] Transpile 'constant_string' to LLVM --- Sources/CodeGen/LLVM/Transpilation.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index e9b43a468..3b970bb91 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -1,5 +1,6 @@ import Core import IR +import Foundation import LLVM import Utils @@ -572,6 +573,8 @@ extension LLVM.Module { insert(closeUnion: i) case is IR.CondBranch: insert(condBranch: i) + case is IR.ConstantString: + insert(constantString: i) case is IR.DeallocStack: return case is IR.EndAccess: @@ -708,6 +711,17 @@ extension LLVM.Module { insertStore(word().constant(UInt64(n)), to: discriminator, at: insertionPoint) } + /// Inserts the transpilation of `i` at `insertionPoint`. + func insert(constantString i: IR.InstructionID) { + let s = m[i] as! ConstantString + let v = LLVM.ArrayConstant(bytes: s.value, in: &self) + let d = declareGlobalVariable(UUID().uuidString, v.type) + setInitializer(v, for: d) + setLinkage(.private, for: d) + setGlobalConstant(true, for: d) + register[.register(i)] = d + } + /// Inserts the transpilation of `i` at `insertionPoint`. func insert(condBranch i: IR.InstructionID) { let s = m[i] as! CondBranch From 42ab9ce9533076f38d09231da373b82365964e59 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 26 Sep 2023 22:44:37 +0200 Subject: [PATCH 012/157] Remove 'IR.BufferConstant' --- Sources/CodeGen/LLVM/Transpilation.swift | 3 --- .../IR/Operands/Constant/BufferConstant.swift | 26 ------------------- 2 files changed, 29 deletions(-) delete mode 100644 Sources/IR/Operands/Constant/BufferConstant.swift diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index 3b970bb91..18a309bad 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -175,9 +175,6 @@ extension LLVM.Module { let t = LLVM.FloatingPointType(ir.llvm(c.type.ast, in: &self))! return t.constant(parsing: v.value) - case let v as IR.BufferConstant: - return LLVM.ArrayConstant(bytes: v.contents, in: &self) - case let v as IR.WitnessTable: return transpiledWitnessTable(v, usedIn: m, from: ir) diff --git a/Sources/IR/Operands/Constant/BufferConstant.swift b/Sources/IR/Operands/Constant/BufferConstant.swift deleted file mode 100644 index 1de8166dc..000000000 --- a/Sources/IR/Operands/Constant/BufferConstant.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Core -import Foundation - -/// A constant buffer of bytes in Hylo IR. -public struct BufferConstant: Constant, Hashable { - - /// The contents of this instance. - public let contents: Data - - /// Creates an instance with given `contents`. - public init(_ contents: Data) { - self.contents = contents - } - - /// The Hylo IR type of this instance. - public var type: IR.`Type` { .object(BuiltinType.ptr) } - -} - -extension BufferConstant: CustomStringConvertible { - - public var description: String { - contents.reduce(into: "\"", { (s, b) in s.append("\\x" + String(b, radix: 16)) }) + "\"" - } - -} From 6c7b36b588155672282274d8f4176883d296e06b Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 26 Sep 2023 22:49:31 +0200 Subject: [PATCH 013/157] Update Package.resolved --- Package.resolved | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/Package.resolved b/Package.resolved index a19da3a35..43b1be7dc 100644 --- a/Package.resolved +++ b/Package.resolved @@ -45,24 +45,6 @@ "version" : "1.0.4" } }, - { - "identity" : "swift-docc-plugin", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-docc-plugin.git", - "state" : { - "revision" : "26ac5758409154cc448d7ab82389c520fa8a8247", - "version" : "1.3.0" - } - }, - { - "identity" : "swift-docc-symbolkit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-docc-symbolkit", - "state" : { - "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34", - "version" : "1.0.0" - } - }, { "identity" : "swift-format", "kind" : "remoteSourceControl", From b39aa4ce858ff287d3fc40bd28ed3af90c5cdc07 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 13:56:32 +0200 Subject: [PATCH 014/157] Move integer type sources to a dedicated directory --- Library/Hylo/Core/{ => Integers}/Int.hylo | 0 Library/Hylo/Core/{ => Integers}/Int32.hylo | 0 Library/Hylo/Core/{ => Integers}/Int8.hylo | 0 Library/Hylo/Core/{ => Integers}/UInt.hylo | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename Library/Hylo/Core/{ => Integers}/Int.hylo (100%) rename Library/Hylo/Core/{ => Integers}/Int32.hylo (100%) rename Library/Hylo/Core/{ => Integers}/Int8.hylo (100%) rename Library/Hylo/Core/{ => Integers}/UInt.hylo (100%) diff --git a/Library/Hylo/Core/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo similarity index 100% rename from Library/Hylo/Core/Int.hylo rename to Library/Hylo/Core/Integers/Int.hylo diff --git a/Library/Hylo/Core/Int32.hylo b/Library/Hylo/Core/Integers/Int32.hylo similarity index 100% rename from Library/Hylo/Core/Int32.hylo rename to Library/Hylo/Core/Integers/Int32.hylo diff --git a/Library/Hylo/Core/Int8.hylo b/Library/Hylo/Core/Integers/Int8.hylo similarity index 100% rename from Library/Hylo/Core/Int8.hylo rename to Library/Hylo/Core/Integers/Int8.hylo diff --git a/Library/Hylo/Core/UInt.hylo b/Library/Hylo/Core/Integers/UInt.hylo similarity index 100% rename from Library/Hylo/Core/UInt.hylo rename to Library/Hylo/Core/Integers/UInt.hylo From 7c3e92b4b0ebe00bad36733177b3cd8ef7183623 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 15:11:29 +0200 Subject: [PATCH 015/157] Refactor the transpilation of metatypes --- Sources/CodeGen/LLVM/Transpilation.swift | 70 ++++++++++++------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index 18a309bad..d7dec4fe7 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -282,30 +282,39 @@ extension LLVM.Module { private mutating func transpiledMetatype( of t: AnyType, usedIn m: IR.Module, from ir: IR.Program ) -> LLVM.GlobalVariable { - switch t.base { - case let u as ProductType: - return transpiledMetatype(of: u, usedIn: m, from: ir) - case let u as TupleType: - return transpiledMetatype(of: u, usedIn: m, from: ir) - default: - UNIMPLEMENTED() + demandMetatype(of: t, usedIn: m, from: ir) { (me, v) in + if let u = ProductType(t) { + me.initializeTranspiledProductTypeMetatype(v, of: u, usedIn: m, from: ir) + } else { + me.initializeTranspiledMetatype(v, of: t, usedIn: m, from: ir) + } } } - /// Returns the LLVM IR value of the metatype `t` used in `m` in `ir`. - private mutating func transpiledMetatype( - of t: ProductType, usedIn m: IR.Module, from ir: IR.Program - ) -> LLVM.GlobalVariable { - let globalName = ir.base.mangled(t) - if let g = global(named: globalName) { return g } + /// Initializes `instance` with the value of the metatype of `t` used in `m` in `ir`. + private mutating func initializeTranspiledMetatype( + _ instance: LLVM.GlobalVariable, + of t: T, usedIn m: IR.Module, from ir: IR.Program + ) { + setLinkage(.linkOnce, for: instance) - // Initialize the instance if it's being used in the module defining `t`. Otherwise, simply - // declare the symbol and let it be linked later. - let metatype = metatypeType() - let instance = declareGlobalVariable(globalName, metatype) - if m.id != ir.base.module(containing: t.decl) { - return instance - } + let layout = memoryLayout(of: t, from: ir) + let v = LLVM.StructType(instance.valueType)!.constant( + aggregating: [layout.size, layout.preferredAlignment, ptr.null], + in: &self) + + setInitializer(v, for: instance) + setGlobalConstant(true, for: instance) + } + + /// Initializes `instance` with the value of the metatype of `t` used in `m` in `ir`. + private mutating func initializeTranspiledProductTypeMetatype( + _ instance: LLVM.GlobalVariable, + of t: ProductType, usedIn m: IR.Module, from ir: IR.Program + ) { + // Initialize the instance if it's being used in the module defining `t`. Otherwise, simply let + // the symbol be linked to its definition later. + if m.id != ir.base.module(containing: t.decl) { return } // If `t` is generic, its metatype is only a stub. let layout: LLVMMemoryLayout @@ -315,33 +324,24 @@ extension LLVM.Module { layout = memoryLayout(of: t, from: ir) } - let v = metatype.constant( + let v = LLVM.StructType(instance.valueType)!.constant( aggregating: [layout.size, layout.preferredAlignment, ptr.null], in: &self) setInitializer(v, for: instance) setGlobalConstant(true, for: instance) - return instance } - /// Returns the LLVM IR value of the metatype `t` used in `m` in `ir`. - private mutating func transpiledMetatype( - of t: TupleType, usedIn m: IR.Module, from ir: IR.Program - ) -> LLVM.GlobalVariable { + private mutating func demandMetatype( + of t: T, usedIn m: IR.Module, from ir: IR.Program, + initializedWith initializeInstance: (inout Self, LLVM.GlobalVariable) -> Void + ) -> LLVM.GlobalVariable{ let globalName = ir.base.mangled(t) if let g = global(named: globalName) { return g } let metatype = metatypeType() let instance = declareGlobalVariable(globalName, metatype) - setLinkage(.linkOnce, for: instance) - - let layout = memoryLayout(of: t, from: ir) - let v = metatype.constant( - aggregating: [layout.size, layout.preferredAlignment, ptr.null], - in: &self) - - setInitializer(v, for: instance) - setGlobalConstant(true, for: instance) + initializeInstance(&self, instance) return instance } From e1c8f93ba0c5e81239ffe4a1cbece75d0cfe2fa1 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 15:30:36 +0200 Subject: [PATCH 016/157] Add 'FixedWidthInteger' to the standard library --- .../Hylo/Core/Integers/FixedWidthInteger.hylo | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Library/Hylo/Core/Integers/FixedWidthInteger.hylo diff --git a/Library/Hylo/Core/Integers/FixedWidthInteger.hylo b/Library/Hylo/Core/Integers/FixedWidthInteger.hylo new file mode 100644 index 000000000..0209438e5 --- /dev/null +++ b/Library/Hylo/Core/Integers/FixedWidthInteger.hylo @@ -0,0 +1,16 @@ +/// An integer type with a binary representation of a fixed size for every instance. +/// +/// Use this trait to write algorithms that depend on bit shifting, perform bitwise opeperations, +/// catch overflows, or access the minimum or maximum representable values of an integer type. +public trait FixedWidthInteger { + + /// Returns the number of bits in the representation of an instance of this type. + public static fun bit_width() -> Int + + /// Returns the maximum value representable by this type. + public static fun max() -> Self + + /// Returns the minimum value representable by this type. + public static fun min() -> Self + +} \ No newline at end of file From 972671bb18b711c6522f9cc58029e74507576a0d Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 15:31:20 +0200 Subject: [PATCH 017/157] Make 'Int' conform to 'FixedWidthInteger' --- Library/Hylo/Core/Integers/Int.hylo | 16 ++++++++++++++++ Tests/LibraryTests/TestCases/IntTests.hylo | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index 5f9fbc127..963e68a08 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -219,6 +219,22 @@ public conformance Int: Copyable { } +public conformance Int: FixedWidthInteger { + + public static fun bit_width() -> Int { + MemoryLayout.size() * 8 + } + + public static fun max() -> Int { + ~0 >> 1 + } + + public static fun min() -> Int { + 1 << (bit_width() - 1) + } + +} + public conformance Int: Comparable {} public conformance Int: ForeignConvertible { diff --git a/Tests/LibraryTests/TestCases/IntTests.hylo b/Tests/LibraryTests/TestCases/IntTests.hylo index 7040b9734..89fd7644e 100644 --- a/Tests/LibraryTests/TestCases/IntTests.hylo +++ b/Tests/LibraryTests/TestCases/IntTests.hylo @@ -18,4 +18,11 @@ public fun main() { test_min() test_max() + + // TODO: + // These tests assume they are running on a 64-bit architecture. + // They should be wrapped in a conditional compilation block (see #1035). + precondition(Int.bit_width() == 64) + precondition(Int.max() == 9223372036854775807) + precondition(Int.min() == -9223372036854775808) } From f993aa913711d3566e6792e2378632d755c47860 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 16:06:31 +0200 Subject: [PATCH 018/157] Implement 'Bool.infix&&' and 'Bool.infix||' --- Library/Hylo/Core/Bool.hylo | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Library/Hylo/Core/Bool.hylo b/Library/Hylo/Core/Bool.hylo index b2012f130..e936f2dcb 100644 --- a/Library/Hylo/Core/Bool.hylo +++ b/Library/Hylo/Core/Bool.hylo @@ -15,6 +15,16 @@ public type Bool { Bool(value: Builtin.icmp_eq_i1(value, Builtin.zeroinitializer_i1())) } + /// Returns the logical conjunction of `self` and `other`. + public fun infix&& (_ rhs: Bool) -> Bool { + Bool(value: Builtin.and_i1(self.value, rhs.value)) + } + + /// Returns the logical disjunction of `self` and `other`. + public fun infix|| (_ rhs: Bool) -> Bool { + Bool(value: Builtin.or_i1(self.value, rhs.value)) + } + /// Returns `true` if `self` is equal to `other`. Otherwise, returns `false`. public fun infix== (_ other: Self) -> Bool { Bool(value: Builtin.icmp_eq_i1(value, other.value)) From 80b00367269f3ae13d60c2289fe2cd88e388bd9b Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 16:46:33 +0200 Subject: [PATCH 019/157] Add overflow reporting operations to 'FixedWidthInteger' --- .../Hylo/Core/Integers/FixedWidthInteger.hylo | 22 +++++++++ Library/Hylo/Core/Integers/Int.hylo | 48 ++++++++++++++++--- 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/Library/Hylo/Core/Integers/FixedWidthInteger.hylo b/Library/Hylo/Core/Integers/FixedWidthInteger.hylo index 0209438e5..b7317c390 100644 --- a/Library/Hylo/Core/Integers/FixedWidthInteger.hylo +++ b/Library/Hylo/Core/Integers/FixedWidthInteger.hylo @@ -4,6 +4,28 @@ /// catch overflows, or access the minimum or maximum representable values of an integer type. public trait FixedWidthInteger { + /// Returns the sum of `self` and `other` along with a flag indicating whether overflow occurred + /// in the operation. + fun adding_reporting_overflow(_ other: Self) -> {partial_value: Self, overflow: Bool} + + /// Returns `self` subtracted by `other` along with a flag indicating whether overflow occurred + /// in the operation. + fun subtracting_reporting_overflow(_ other: Self) -> {partial_value: Self, overflow: Bool} + + /// Returns the product of `self` and `other` along with a flag indicating whether overflow + /// occurred in the operation. + fun multiplied_reporting_overflow(by other: Self) -> {partial_value: Self, overflow: Bool} + + /// Returns the quotient of dividing `self` by `other` along with a flag indicating whether + /// overflow occurred in the operation. + fun divided_reporting_overflow(by other: Self) -> {partial_value: Self, overflow: Bool} + + /// Returns the remainder of dividing `self` by `other` along with a flag indicating whether + /// overflow occurred in the operation. + fun remainder_reporting_overflow( + dividing_by other: Self + ) -> {partial_value: Self, overflow: Bool} + /// Returns the number of bits in the representation of an instance of this type. public static fun bit_width() -> Int diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index 963e68a08..886ba3e2e 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -198,13 +198,6 @@ public type Int { &self.value = Builtin.lshr_word(value, n.value) } - /// Returns the sum of `self` and `other` along with a flag indicating whether overflow occurred - /// in the operation. - public fun adding_reporting_overflow(_ other: Self) -> {partial_value: Self, overflow: Bool} { - let r = Builtin.sadd_with_overflow_word(value, other.value) - return (partial_value: Int(value: r.0), overflow: Bool(value: r.1)) - } - } public conformance Int: ExpressibleByIntegerLiteral {} @@ -233,6 +226,47 @@ public conformance Int: FixedWidthInteger { 1 << (bit_width() - 1) } + public fun adding_reporting_overflow(_ other: Self) -> {partial_value: Self, overflow: Bool} { + let r = Builtin.sadd_with_overflow_word(value, other.value) + return (partial_value: Int(value: r.0), overflow: Bool(value: r.1)) + } + + public fun subtracting_reporting_overflow( + _ other: Self + ) -> {partial_value: Self, overflow: Bool} { + let r = Builtin.ssub_with_overflow_word(value, other.value) + return (partial_value: Int(value: r.0), overflow: Bool(value: r.1)) + } + + public fun multiplied_reporting_overflow( + by other: Self + ) -> {partial_value: Self, overflow: Bool} { + let r = Builtin.smul_with_overflow_word(value, other.value) + return (partial_value: Int(value: r.0), overflow: Bool(value: r.1)) + } + + public fun divided_reporting_overflow(by other: Self) -> {partial_value: Self, overflow: Bool} { + if other == 0 { + return (partial_value: self.copy(), overflow: true) + } + if (self == Self.min()) && (other == -1) { + return (partial_value: self.copy(), overflow: true) + } + return (partial_value: Int(value: Builtin.sdiv_word(value, other.value)), overflow: false) + } + + public fun remainder_reporting_overflow( + dividing_by other: Self + ) -> {partial_value: Self, overflow: Bool} { + if other == 0 { + return (partial_value: self.copy(), overflow: true) + } + if (self == Self.min()) && (other == -1) { + return (partial_value: 0, overflow: true) + } + return (partial_value: Int(value: Builtin.srem_word(value, other.value)), overflow: false) + } + } public conformance Int: Comparable {} From f2799b177c25f4b983eba879364f7613a557a700 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 16:47:25 +0200 Subject: [PATCH 020/157] Test overflow reporting operations --- Tests/LibraryTests/TestCases/IntTests.hylo | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Tests/LibraryTests/TestCases/IntTests.hylo b/Tests/LibraryTests/TestCases/IntTests.hylo index 89fd7644e..1e1559bd5 100644 --- a/Tests/LibraryTests/TestCases/IntTests.hylo +++ b/Tests/LibraryTests/TestCases/IntTests.hylo @@ -12,12 +12,48 @@ fun test_max() { precondition(max[2, 1] == 2) } +fun test_adding_reporting_overflow() { + let a0 = (1).adding_reporting_overflow(1) + precondition(a0.0 == 2) + precondition(a0.1 == false) + + let a1 = Int.max().adding_reporting_overflow(1) + precondition(a1.0 == Int.min()) + precondition(a1.1 == true) +} + +fun test_multiplied_reporting_overflow() { + let a0 = (1).multiplied_reporting_overflow(by: 2) + precondition(a0.0 == 2) + precondition(a0.1 == false) + + let a1 = Int.max().multiplied_reporting_overflow(by: 2) + precondition(a1.0 == -2) + precondition(a1.1 == true) +} + +fun test_divided_reporting_overflow() { + let a0 = (1).divided_reporting_overflow(by: 0) + precondition(a0.0 == 1) + precondition(a0.1 == true) +} + +fun test_remainder_reporting_overflow() { + let a0 = (1).remainder_reporting_overflow(dividing_by: 0) + precondition(a0.0 == 1) + precondition(a0.1 == true) +} + public fun main() { // Int.init() precondition(Int() == 0) test_min() test_max() + test_adding_reporting_overflow() + test_multiplied_reporting_overflow() + test_divided_reporting_overflow() + test_remainder_reporting_overflow() // TODO: // These tests assume they are running on a 64-bit architecture. From 4782f51e8798cd21b6f1454620af47041fc1a073 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 16:48:07 +0200 Subject: [PATCH 021/157] Transpile unsigned division to LLVM --- Sources/CodeGen/LLVM/Transpilation.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index d7dec4fe7..08acf4778 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -791,6 +791,11 @@ extension LLVM.Module { let r = llvm(s.operands[1]) register[.register(i)] = insertSignedDiv(exact: e, l, r, at: insertionPoint) + case .udiv(let e, _): + let l = llvm(s.operands[0]) + let r = llvm(s.operands[1]) + register[.register(i)] = insertUnsignedDiv(exact: e, l, r, at: insertionPoint) + case .srem(_): let l = llvm(s.operands[0]) let r = llvm(s.operands[1]) From 1c5b8f99ea3488ecee9b197c2b6469e8325b78a5 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 16:49:15 +0200 Subject: [PATCH 022/157] Remove needless access modifiers --- Library/Hylo/Core/Integers/FixedWidthInteger.hylo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Library/Hylo/Core/Integers/FixedWidthInteger.hylo b/Library/Hylo/Core/Integers/FixedWidthInteger.hylo index b7317c390..248d65307 100644 --- a/Library/Hylo/Core/Integers/FixedWidthInteger.hylo +++ b/Library/Hylo/Core/Integers/FixedWidthInteger.hylo @@ -27,12 +27,12 @@ public trait FixedWidthInteger { ) -> {partial_value: Self, overflow: Bool} /// Returns the number of bits in the representation of an instance of this type. - public static fun bit_width() -> Int + static fun bit_width() -> Int /// Returns the maximum value representable by this type. - public static fun max() -> Self + static fun max() -> Self /// Returns the minimum value representable by this type. - public static fun min() -> Self + static fun min() -> Self } \ No newline at end of file From 5f8659d86c56d1f4ba847b989a629c4bd29e49fa Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 17:10:34 +0200 Subject: [PATCH 023/157] Transpile unsigned remainder to LLVM --- Sources/CodeGen/LLVM/Transpilation.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index 08acf4778..97b49ba55 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -796,11 +796,16 @@ extension LLVM.Module { let r = llvm(s.operands[1]) register[.register(i)] = insertUnsignedDiv(exact: e, l, r, at: insertionPoint) - case .srem(_): + case .srem: let l = llvm(s.operands[0]) let r = llvm(s.operands[1]) register[.register(i)] = insertSignedRem(l, r, at: insertionPoint) + case .urem: + let l = llvm(s.operands[0]) + let r = llvm(s.operands[1]) + register[.register(i)] = insertUnsignedRem(l, r, at: insertionPoint) + case .signedAdditionWithOverflow(let t): let l = llvm(s.operands[0]) let r = llvm(s.operands[1]) From 131d47332a5d411de55c5c39e140c4933f784acc Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 17:11:06 +0200 Subject: [PATCH 024/157] Implement bitwise operations on 'UInt' --- Library/Hylo/Core/Integers/UInt.hylo | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Library/Hylo/Core/Integers/UInt.hylo b/Library/Hylo/Core/Integers/UInt.hylo index c7016affb..793b9fe5b 100644 --- a/Library/Hylo/Core/Integers/UInt.hylo +++ b/Library/Hylo/Core/Integers/UInt.hylo @@ -30,6 +30,41 @@ public type UInt { Bool(value: Builtin.icmp_ne_word(value, other.value)) } + /// Returns the bitwise AND of `self` and `other`. + public fun infix& (_ other: Self) -> Self { + UInt(value: Builtin.and_word(value, other.value)) + } + + /// Writes the bitwise AND of `self` and `other` to `self`. + public fun infix&= (_ other: Self) inout { + &self.value = Builtin.and_word(value, other.value) + } + + /// Returns the bitwise OR of `self` and `other`. + public fun infix| (_ other: Self) -> Self { + UInt(value: Builtin.or_word(value, other.value)) + } + + /// Writes the bitwise OR of `self` and `other` to `self`. + public fun infix|= (_ other: Self) inout { + &self.value = Builtin.or_word(value, other.value) + } + + /// Returns the bitwise XOR of `self` and `other`. + public fun infix^ (_ other: Self) -> Self { + UInt(value: Builtin.xor_word(value, other.value)) + } + + /// Writes the bitwise XOR of `self` and `other` to `self`. + public fun infix^= (_ other: Self) inout { + &self.value = Builtin.xor_word(value, other.value) + } + + /// Returns the bitwise inverse of `self`. + public fun prefix~ () -> Self { + self ^ UInt(bit_pattern: -1) + } + } public conformance UInt: ExpressibleByIntegerLiteral {} From 2847e2f0c2383d4f5ccb6725f247cc53c6577c23 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 17:11:43 +0200 Subject: [PATCH 025/157] Make 'UInt' conform to 'FixedWidthInteger' --- Library/Hylo/Core/Integers/Int.hylo | 4 +-- Library/Hylo/Core/Integers/UInt.hylo | 51 ++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index 886ba3e2e..85c1288a0 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -218,11 +218,11 @@ public conformance Int: FixedWidthInteger { MemoryLayout.size() * 8 } - public static fun max() -> Int { + public static fun max() -> Self { ~0 >> 1 } - public static fun min() -> Int { + public static fun min() -> Self { 1 << (bit_width() - 1) } diff --git a/Library/Hylo/Core/Integers/UInt.hylo b/Library/Hylo/Core/Integers/UInt.hylo index 793b9fe5b..4181daa49 100644 --- a/Library/Hylo/Core/Integers/UInt.hylo +++ b/Library/Hylo/Core/Integers/UInt.hylo @@ -78,3 +78,54 @@ public conformance UInt: Copyable { } } + +public conformance UInt: FixedWidthInteger { + + public static fun bit_width() -> Int { + MemoryLayout.size() * 8 + } + + public static fun max() -> Self { + ~UInt() + } + + public static fun min() -> Self { + 0 + } + + public fun adding_reporting_overflow(_ other: Self) -> {partial_value: Self, overflow: Bool} { + let r = Builtin.uadd_with_overflow_word(value, other.value) + return (partial_value: UInt(value: r.0), overflow: Bool(value: r.1)) + } + + public fun subtracting_reporting_overflow( + _ other: Self + ) -> {partial_value: Self, overflow: Bool} { + let r = Builtin.usub_with_overflow_word(value, other.value) + return (partial_value: UInt(value: r.0), overflow: Bool(value: r.1)) + } + + public fun multiplied_reporting_overflow( + by other: Self + ) -> {partial_value: Self, overflow: Bool} { + let r = Builtin.umul_with_overflow_word(value, other.value) + return (partial_value: UInt(value: r.0), overflow: Bool(value: r.1)) + } + + public fun divided_reporting_overflow(by other: Self) -> {partial_value: Self, overflow: Bool} { + if other == UInt() { + return (partial_value: self.copy(), overflow: true) + } + return (partial_value: UInt(value: Builtin.udiv_word(value, other.value)), overflow: false) + } + + public fun remainder_reporting_overflow( + dividing_by other: Self + ) -> {partial_value: Self, overflow: Bool} { + if other == UInt() { + return (partial_value: self.copy(), overflow: true) + } + return (partial_value: UInt(value: Builtin.urem_word(value, other.value)), overflow: false) + } + +} From 468dfacc5372ca02671cb2aaaa13186ef0d0877e Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 17:27:21 +0200 Subject: [PATCH 026/157] Add masking shift operators to 'Int' --- Library/Hylo/Core/Integers/Int.hylo | 22 ++++++++++++++++++++-- Library/Hylo/Core/Operators.hylo | 2 ++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index 85c1288a0..d2238c36b 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -183,6 +183,15 @@ public type Int { Int(value: Builtin.shl_word(value, n.value)) } + /// Returns `self` with its binary representation shifted by `n` digits to the left, masking the + /// shift amount to the bit width of `Self`. + /// + /// Use this operator when you need to perform a shift and are sure that the shift amount is in + /// the range `0 ..< Self.bit_width()`. + public fun infix&<< (_ n: Int) -> Self { + Int(value: Builtin.shl_word(value, n.value)) + } + /// Shifts the binary representation of `self` by `n` digits to the left. public fun infix<<= (_ n: Int) inout { &self.value = Builtin.shl_word(value, n.value) @@ -193,6 +202,15 @@ public type Int { Int(value: Builtin.lshr_word(value, n.value)) } + /// Returns `self` with its binary representation shifted by `n` digits to the right, masking the + /// shift amount to the bit width of `Self`. + /// + /// Use this operator when you need to perform a shift and are sure that the shift amount is in + /// the range `0 ..< Self.bit_width()`. + public fun infix&>> (_ n: Int) -> Self { + Int(value: Builtin.lshr_word(value, n.value)) + } + /// Shifts the binary representation of `self` by `n` digits to the right. public fun infix>>= (_ n: Int) inout { &self.value = Builtin.lshr_word(value, n.value) @@ -219,11 +237,11 @@ public conformance Int: FixedWidthInteger { } public static fun max() -> Self { - ~0 >> 1 + ~0 &>> 1 } public static fun min() -> Self { - 1 << (bit_width() - 1) + 1 &<< (bit_width() - 1) } public fun adding_reporting_overflow(_ other: Self) -> {partial_value: Self, overflow: Bool} { diff --git a/Library/Hylo/Core/Operators.hylo b/Library/Hylo/Core/Operators.hylo index 60f18f22d..557069367 100644 --- a/Library/Hylo/Core/Operators.hylo +++ b/Library/Hylo/Core/Operators.hylo @@ -1,5 +1,7 @@ public operator infix<< : shift +public operator infix&<< : shift public operator infix>> : shift +public operator infix&>> : shift public operator infix* : multiplication public operator infix*! : multiplication From 74167ed69e9cac1c9ff7c733246a42ed3f999e28 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 18:07:03 +0200 Subject: [PATCH 027/157] Transpile arithmetic shift to LLVM --- Sources/CodeGen/LLVM/Transpilation.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index 97b49ba55..6487165df 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -786,6 +786,11 @@ extension LLVM.Module { let r = llvm(s.operands[1]) register[.register(i)] = insertLShr(l, r, at: insertionPoint) + case .ashr: + let l = llvm(s.operands[0]) + let r = llvm(s.operands[1]) + register[.register(i)] = insertAShr(l, r, at: insertionPoint) + case .sdiv(let e, _): let l = llvm(s.operands[0]) let r = llvm(s.operands[1]) From 51d8bed6dac7786153d1961a9e65596ca541885a Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 18:07:23 +0200 Subject: [PATCH 028/157] Transpile zero extension to LLVM --- Sources/CodeGen/LLVM/Transpilation.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index 6487165df..5bcdc82a5 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -878,6 +878,11 @@ extension LLVM.Module { let source = llvm(s.operands[0]) register[.register(i)] = insertTrunc(source, to: target, at: insertionPoint) + case .zext(_, let t): + let target = ir.llvm(builtinType: t, in: &self) + let source = llvm(s.operands[0]) + register[.register(i)] = insertZeroExtend(source, to: target, at: insertionPoint) + case .inttoptr(_): let source = llvm(s.operands[0]) register[.register(i)] = insertIntToPtr(source, at: insertionPoint) From 38c61e02110ecfa7856b169b15da6bcdbb01ed10 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 18:08:05 +0200 Subject: [PATCH 029/157] Fix the implementation of 'Int.infix&>>' --- Library/Hylo/Core/Integers/Int.hylo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index d2238c36b..8c5b159da 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -199,7 +199,7 @@ public type Int { /// Returns `self` with its binary representation shifted by `n` digits to the right. public fun infix>> (_ n: Int) -> Self { - Int(value: Builtin.lshr_word(value, n.value)) + Int(value: Builtin.ashr_word(value, n.value)) } /// Returns `self` with its binary representation shifted by `n` digits to the right, masking the @@ -208,12 +208,12 @@ public type Int { /// Use this operator when you need to perform a shift and are sure that the shift amount is in /// the range `0 ..< Self.bit_width()`. public fun infix&>> (_ n: Int) -> Self { - Int(value: Builtin.lshr_word(value, n.value)) + Int(value: Builtin.ashr_word(value, n.value)) } /// Shifts the binary representation of `self` by `n` digits to the right. public fun infix>>= (_ n: Int) inout { - &self.value = Builtin.lshr_word(value, n.value) + &self.value = Builtin.ashr_word(value, n.value) } } From 1c06d55e548f3eb307a1cad90d77b8b987b5fa7f Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 18:08:34 +0200 Subject: [PATCH 030/157] Add 'BinaryInteger' to the standard library --- Library/Hylo/Core/Integers/BinaryInteger.hylo | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Library/Hylo/Core/Integers/BinaryInteger.hylo diff --git a/Library/Hylo/Core/Integers/BinaryInteger.hylo b/Library/Hylo/Core/Integers/BinaryInteger.hylo new file mode 100644 index 000000000..f74ab808d --- /dev/null +++ b/Library/Hylo/Core/Integers/BinaryInteger.hylo @@ -0,0 +1,16 @@ +/// An integer type with a binary representation. +public trait BinaryInteger { + + /// Creates an instance with value `0`. + init() + + /// Returns `-1` if `self` is negative, `1` if it is positive, or `0` otherwise. + fun signum() -> Int + + /// Returns `true` if this type is a signed integer. + static fun is_signed() -> Bool + + /// Returns the number of bits in the representation of an instance of this type. + static fun bit_width() -> Int + +} From 4077b40564d574c011f89214284134c2fb9cfb13 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 18:08:56 +0200 Subject: [PATCH 031/157] Make 'Int' conform to 'BinaryInteger' --- Library/Hylo/Core/Integers/Int.hylo | 13 +++++++++++++ Tests/LibraryTests/TestCases/IntTests.hylo | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index 8c5b159da..ef6759d75 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -230,6 +230,19 @@ public conformance Int: Copyable { } +public conformance Int: BinaryInteger { + + public static fun is_signed() -> Bool { + true + } + + public fun signum() -> Int { + let positive = Int(value: Builtin.zext_i1_word((self > 0).value)) + return positive | (self &>> (Self.bit_width() - 1)) + } + +} + public conformance Int: FixedWidthInteger { public static fun bit_width() -> Int { diff --git a/Tests/LibraryTests/TestCases/IntTests.hylo b/Tests/LibraryTests/TestCases/IntTests.hylo index 1e1559bd5..b64d8701d 100644 --- a/Tests/LibraryTests/TestCases/IntTests.hylo +++ b/Tests/LibraryTests/TestCases/IntTests.hylo @@ -12,6 +12,12 @@ fun test_max() { precondition(max[2, 1] == 2) } +fun test_signum() { + precondition(Int().signum() == 0) + precondition((+10).signum() == +1) + precondition((-10).signum() == -1) +} + fun test_adding_reporting_overflow() { let a0 = (1).adding_reporting_overflow(1) precondition(a0.0 == 2) @@ -48,8 +54,12 @@ public fun main() { // Int.init() precondition(Int() == 0) + // Int.is_signed() + precondition(Int.is_signed()) + test_min() test_max() + test_signum() test_adding_reporting_overflow() test_multiplied_reporting_overflow() test_divided_reporting_overflow() From fadb172c626f4d2526939db130db3534b50158cc Mon Sep 17 00:00:00 2001 From: Nils Hjelte Date: Wed, 27 Sep 2023 19:44:13 +0200 Subject: [PATCH 032/157] Add introducers for statements and expressions --- Sources/Core/AST/AST+Walk.swift | 10 ++-- Sources/Core/AST/Decl/TraitDecl.swift | 6 +++ Sources/Core/AST/Decl/WhereClause.swift | 9 +++- Sources/Core/AST/Expr/CastExpr.swift | 12 ++++- Sources/Core/AST/Expr/ConditionalExpr.swift | 4 +- .../Core/AST/Expr/ExistentialTypeExpr.swift | 5 ++ Sources/Core/AST/Expr/MatchExpr.swift | 11 ++++- Sources/Core/AST/Introduced.swift | 23 +++++++++ Sources/Core/AST/Stmt/ConditionalStmt.swift | 12 +++-- Sources/Core/AST/Stmt/DoWhileStmt.swift | 8 ++- Sources/Core/AST/Stmt/ForStmt.swift | 14 ++++-- Sources/Core/AST/Stmt/ReturnStmt.swift | 10 +++- Sources/Core/AST/Stmt/WhileStmt.swift | 11 ++++- Sources/Core/AST/Stmt/YieldStmt.swift | 6 ++- Sources/Core/ScopedProgram.swift | 6 +-- Sources/FrontEnd/Parse/Parser.swift | 49 +++++++++++++------ .../FrontEnd/TypeChecking/TypeChecker.swift | 6 +-- Sources/IR/Emitter.swift | 6 +-- 18 files changed, 162 insertions(+), 46 deletions(-) create mode 100644 Sources/Core/AST/Introduced.swift diff --git a/Sources/Core/AST/AST+Walk.swift b/Sources/Core/AST/AST+Walk.swift index 6c26d9256..cdaa42ed9 100644 --- a/Sources/Core/AST/AST+Walk.swift +++ b/Sources/Core/AST/AST+Walk.swift @@ -451,7 +451,7 @@ extension AST { ) { walk(conditionItems: n.condition, notifying: &o) walk(n.success, notifying: &o) - walk(n.failure, notifying: &o) + walk(n.failure.value, notifying: &o) } /// Visits the children of `n` in pre-order, notifying `o` when a node is entered or left. @@ -706,7 +706,7 @@ extension AST { ) { walk(conditionItems: n.condition, notifying: &o) walk(n.success, notifying: &o) - walk(n.failure, notifying: &o) + walk(n.failure?.value, notifying: &o) } /// Visits the children of `n` in pre-order, notifying `o` when a node is entered or left. @@ -733,7 +733,7 @@ extension AST { _ n: DoWhileStmt, notifying o: inout O ) { walk(n.body, notifying: &o) - walk(n.condition, notifying: &o) + walk(n.condition.value, notifying: &o) } /// Visits the children of `n` in pre-order, notifying `o` when a node is entered or left. @@ -748,8 +748,8 @@ extension AST { _ n: ForStmt, notifying o: inout O ) { walk(n.binding, notifying: &o) - walk(n.domain, notifying: &o) - walk(n.filter, notifying: &o) + walk(n.domain.value, notifying: &o) + walk(n.filter?.value, notifying: &o) walk(n.body, notifying: &o) } diff --git a/Sources/Core/AST/Decl/TraitDecl.swift b/Sources/Core/AST/Decl/TraitDecl.swift index 5c3aabd5d..2f53eff95 100644 --- a/Sources/Core/AST/Decl/TraitDecl.swift +++ b/Sources/Core/AST/Decl/TraitDecl.swift @@ -7,6 +7,10 @@ public struct TraitDecl: ExposableDecl, SingleEntityDecl, LexicalScope { public let site: SourceRange + /// The site of the `trait` introducer. + public let introducerSite: SourceRange + + /// The access modifier of the declaration, if any. public let accessModifier: SourceRepresentable @@ -24,6 +28,7 @@ public struct TraitDecl: ExposableDecl, SingleEntityDecl, LexicalScope { /// Creates an instance with the given properties. public init( + introducerSite: SourceRange, accessModifier: SourceRepresentable, identifier: SourceRepresentable, refinements: [NameExpr.ID], @@ -33,6 +38,7 @@ public struct TraitDecl: ExposableDecl, SingleEntityDecl, LexicalScope { ) { precondition(members.contains(AnyDeclID(selfParameterDecl))) + self.introducerSite = introducerSite self.site = site self.accessModifier = accessModifier self.identifier = identifier diff --git a/Sources/Core/AST/Decl/WhereClause.swift b/Sources/Core/AST/Decl/WhereClause.swift index 5649043bc..930202f0d 100644 --- a/Sources/Core/AST/Decl/WhereClause.swift +++ b/Sources/Core/AST/Decl/WhereClause.swift @@ -3,6 +3,9 @@ import Utils /// A where clause. public struct WhereClause: Codable { + /// The site of the `where` introducer. + public let introducerSite: SourceRange + /// The expression of a type constraint defined in a generic clause. public enum ConstraintExpr: Codable { @@ -24,7 +27,11 @@ public struct WhereClause: Codable { /// The constraint expressions in the clause. public let constraints: [SourceRepresentable] - public init(constraints: [SourceRepresentable]) { + public init( + introducerSite: SourceRange, + constraints: [SourceRepresentable] + ) { + self.introducerSite = introducerSite self.constraints = constraints } diff --git a/Sources/Core/AST/Expr/CastExpr.swift b/Sources/Core/AST/Expr/CastExpr.swift index 4712d4095..b2737bbc7 100644 --- a/Sources/Core/AST/Expr/CastExpr.swift +++ b/Sources/Core/AST/Expr/CastExpr.swift @@ -20,6 +20,9 @@ public struct CastExpr: Expr { public let site: SourceRange + /// The site of the `as` keyword. + public let introducerSite: SourceRange + /// The left operand. public let left: AnyExprID @@ -30,8 +33,15 @@ public struct CastExpr: Expr { public let direction: Direction /// Creates an instance with the given properties. - public init(left: AnyExprID, right: AnyExprID, direction: Direction, site: SourceRange) { + public init( + introducerSite: SourceRange, + left: AnyExprID, + right: AnyExprID, + direction: Direction, + site: SourceRange + ) { self.site = site + self.introducerSite = introducerSite self.left = left self.right = right self.direction = direction diff --git a/Sources/Core/AST/Expr/ConditionalExpr.swift b/Sources/Core/AST/Expr/ConditionalExpr.swift index c0abed1b9..63689181c 100644 --- a/Sources/Core/AST/Expr/ConditionalExpr.swift +++ b/Sources/Core/AST/Expr/ConditionalExpr.swift @@ -15,13 +15,13 @@ public struct ConditionalExpr: Expr, LexicalScope { public let success: AnyExprID /// The the expression that's executed if the condition does not hold. - public let failure: AnyExprID + public let failure: Introduced public init( introducerSite: SourceRange, condition: [ConditionItem], success: AnyExprID, - failure: AnyExprID, + failure: Introduced, site: SourceRange ) { precondition(condition.count > 0) diff --git a/Sources/Core/AST/Expr/ExistentialTypeExpr.swift b/Sources/Core/AST/Expr/ExistentialTypeExpr.swift index ccc439de2..238207722 100644 --- a/Sources/Core/AST/Expr/ExistentialTypeExpr.swift +++ b/Sources/Core/AST/Expr/ExistentialTypeExpr.swift @@ -3,6 +3,9 @@ public struct ExistentialTypeExpr: Expr { public let site: SourceRange + /// The site of the `any` keyword. + public let introducerSite: SourceRange + /// The traits to which the witness conforms. public let traits: TraitComposition @@ -10,11 +13,13 @@ public struct ExistentialTypeExpr: Expr { public let whereClause: SourceRepresentable? public init( + introducerSite: SourceRange, traits: TraitComposition, whereClause: SourceRepresentable?, site: SourceRange ) { self.site = site + self.introducerSite = introducerSite self.traits = traits self.whereClause = whereClause } diff --git a/Sources/Core/AST/Expr/MatchExpr.swift b/Sources/Core/AST/Expr/MatchExpr.swift index 2a6da5bc9..3d8687da0 100644 --- a/Sources/Core/AST/Expr/MatchExpr.swift +++ b/Sources/Core/AST/Expr/MatchExpr.swift @@ -3,14 +3,23 @@ public struct MatchExpr: Expr { public let site: SourceRange + /// The site of the `match` keyword. + public let introducerSite: SourceRange + /// The subject of the match. public let subject: AnyExprID /// The cases of the match. public let cases: [MatchCase.ID] - public init(subject: AnyExprID, cases: [MatchCase.ID], site: SourceRange) { + public init( + introducerSite: SourceRange, + subject: AnyExprID, + cases: [MatchCase.ID], + site: SourceRange + ) { self.site = site + self.introducerSite = introducerSite self.subject = subject self.cases = cases } diff --git a/Sources/Core/AST/Introduced.swift b/Sources/Core/AST/Introduced.swift new file mode 100644 index 000000000..6725415b5 --- /dev/null +++ b/Sources/Core/AST/Introduced.swift @@ -0,0 +1,23 @@ +// Child node with introducer keyword, eg else clause in if-else +// The NodeIDProtocol conformance is used for BundledNode compability +// eg DoWhileStmt in Emitter.swift: `program[s].condition.site`` +public struct Introduced: NodeIDProtocol { + public let introducerSite: SourceRange + public let value: T + + public var rawValue: NodeID.RawValue { value.rawValue } + public var kind: NodeKind { value.kind } + + public init?(_ x: Other) { + if let i = x as? Introduced { + self = i + } else { + return nil + } + } + + public init(introducerSite: SourceRange, value: T) { + self.introducerSite = introducerSite + self.value = value + } +} diff --git a/Sources/Core/AST/Stmt/ConditionalStmt.swift b/Sources/Core/AST/Stmt/ConditionalStmt.swift index 00cdfb86b..91574c40a 100644 --- a/Sources/Core/AST/Stmt/ConditionalStmt.swift +++ b/Sources/Core/AST/Stmt/ConditionalStmt.swift @@ -1,3 +1,4 @@ + /// A conditional statement. /// /// Unlike a `ConditionalExpr`, the branches of a conditional statement are represented by brace @@ -6,6 +7,9 @@ public struct ConditionalStmt: Stmt, LexicalScope { public let site: SourceRange + /// The site of the `if` introducer. + public let introducerSite: SourceRange + /// The condition of the expression. /// /// - Requires: `condition.count > 0` @@ -17,20 +21,22 @@ public struct ConditionalStmt: Stmt, LexicalScope { /// The branch that's executed if the condition does not hold. /// /// - Requires: `failure` is either a `ConditionalStmt`, a `BraceStmt`, or `nil`. - public let failure: AnyStmtID? + public let failure: Introduced? public init( + introducerSite: SourceRange, condition: [ConditionItem], success: BraceStmt.ID, - failure: AnyStmtID?, + failure: Introduced?, site: SourceRange ) { precondition(condition.count > 0) if let f = failure { - precondition(f.kind == ConditionalStmt.self || f.kind == BraceStmt.self) + precondition(f.value.kind == ConditionalStmt.self || f.value.kind == BraceStmt.self) } self.site = site + self.introducerSite = introducerSite self.condition = condition self.success = success self.failure = failure diff --git a/Sources/Core/AST/Stmt/DoWhileStmt.swift b/Sources/Core/AST/Stmt/DoWhileStmt.swift index 7f8cda916..3864bd081 100644 --- a/Sources/Core/AST/Stmt/DoWhileStmt.swift +++ b/Sources/Core/AST/Stmt/DoWhileStmt.swift @@ -3,16 +3,20 @@ public struct DoWhileStmt: Stmt { public let site: SourceRange + /// The site of the `do` introducer. + public let introducerSite: SourceRange + /// The body of the loop. public let body: BraceStmt.ID /// The condition of the loop. /// /// - Note: The condition is evaluated in the lexical scope of the body. - public let condition: AnyExprID + public let condition: Introduced - public init(body: BraceStmt.ID, condition: AnyExprID, site: SourceRange) { + public init(introducerSite: SourceRange, body: BraceStmt.ID, condition: Introduced, site: SourceRange) { self.site = site + self.introducerSite = introducerSite self.body = body self.condition = condition } diff --git a/Sources/Core/AST/Stmt/ForStmt.swift b/Sources/Core/AST/Stmt/ForStmt.swift index ccb738424..808f2ebb7 100644 --- a/Sources/Core/AST/Stmt/ForStmt.swift +++ b/Sources/Core/AST/Stmt/ForStmt.swift @@ -1,29 +1,33 @@ /// A for loop. public struct ForStmt: Stmt, LexicalScope { - public let site: SourceRange + /// The site of the `for` introducer. + public let introducerSite: SourceRange + /// The conditional binding of the loop. public let binding: BindingDecl.ID /// The iteration domain of the loop. - public let domain: AnyExprID + public let domain: Introduced /// The filter of the loop, if any. - public let filter: AnyExprID? + public let filter: Introduced? /// The body of the loop. public let body: BraceStmt.ID /// Creates an instance having the given properties. public init( + introducerSite: SourceRange, binding: BindingDecl.ID, - domain: AnyExprID, - filter: AnyExprID?, + domain: Introduced, + filter: Introduced?, body: BraceStmt.ID, site: SourceRange ) { self.site = site + self.introducerSite = introducerSite self.binding = binding self.domain = domain self.filter = filter diff --git a/Sources/Core/AST/Stmt/ReturnStmt.swift b/Sources/Core/AST/Stmt/ReturnStmt.swift index 5ebd4fa85..eda0479f8 100644 --- a/Sources/Core/AST/Stmt/ReturnStmt.swift +++ b/Sources/Core/AST/Stmt/ReturnStmt.swift @@ -3,11 +3,19 @@ public struct ReturnStmt: Stmt { public let site: SourceRange + /// The site of the `return` introducer. + public let introducerSite: SourceRange + /// The return value, if any. public let value: AnyExprID? - public init(value: AnyExprID?, site: SourceRange) { + public init( + introducerSite: SourceRange, + value: AnyExprID?, + site: SourceRange + ) { self.site = site + self.introducerSite = introducerSite self.value = value } diff --git a/Sources/Core/AST/Stmt/WhileStmt.swift b/Sources/Core/AST/Stmt/WhileStmt.swift index 47d67c0ca..c04b5a3e6 100644 --- a/Sources/Core/AST/Stmt/WhileStmt.swift +++ b/Sources/Core/AST/Stmt/WhileStmt.swift @@ -3,6 +3,9 @@ public struct WhileStmt: Stmt, LexicalScope { public let site: SourceRange + /// The site of the `while` introducer. + public let introducerSite: SourceRange + /// The condition of the loop. /// /// - Requires `condition.count > 0` @@ -11,10 +14,16 @@ public struct WhileStmt: Stmt, LexicalScope { /// The body of the loop. public let body: BraceStmt.ID - public init(condition: [ConditionItem], body: BraceStmt.ID, site: SourceRange) { + public init( + introducerSite: SourceRange, + condition: [ConditionItem], + body: BraceStmt.ID, + site: SourceRange + ) { precondition(condition.count > 0) self.site = site + self.introducerSite = introducerSite self.condition = condition self.body = body } diff --git a/Sources/Core/AST/Stmt/YieldStmt.swift b/Sources/Core/AST/Stmt/YieldStmt.swift index e8e702f3e..bf3d1ab48 100644 --- a/Sources/Core/AST/Stmt/YieldStmt.swift +++ b/Sources/Core/AST/Stmt/YieldStmt.swift @@ -3,11 +3,15 @@ public struct YieldStmt: Stmt { public let site: SourceRange + /// The site of the `yield` introducer. + public let introducerSite: SourceRange + /// The yielded value. public let value: AnyExprID - public init(value: AnyExprID, site: SourceRange) { + public init(introducerSite: SourceRange, value: AnyExprID, site: SourceRange) { self.site = site + self.introducerSite = introducerSite self.value = value } diff --git a/Sources/Core/ScopedProgram.swift b/Sources/Core/ScopedProgram.swift index b383a86f8..863cbe464 100644 --- a/Sources/Core/ScopedProgram.swift +++ b/Sources/Core/ScopedProgram.swift @@ -197,7 +197,7 @@ private struct ScopeVisitor: ASTWalkObserver { // The failure branch is not in the scope of the conditional expression. innermost = nodeToScope[e]! - ast.walk(ast[e].failure, notifying: &self) + ast.walk(ast[e].failure.value, notifying: &self) return false } @@ -209,7 +209,7 @@ private struct ScopeVisitor: ASTWalkObserver { // The failure branch is not in the scope of the conditional expression. innermost = nodeToScope[s]! - ast.walk(ast[s].failure, notifying: &self) + ast.walk(ast[s].failure?.value, notifying: &self) return false } @@ -220,7 +220,7 @@ private struct ScopeVisitor: ASTWalkObserver { ast.walk(roots: ast[ast[s].body].stmts, notifying: &self) // The condition is in the same scope as the body. - ast.walk(ast[s].condition, notifying: &self) + ast.walk(ast[s].condition.value, notifying: &self) innermost = nodeToScope[ast[s].body]! return false } diff --git a/Sources/FrontEnd/Parse/Parser.swift b/Sources/FrontEnd/Parse/Parser.swift index 32aca4bfd..efb6d13c1 100644 --- a/Sources/FrontEnd/Parse/Parser.swift +++ b/Sources/FrontEnd/Parse/Parser.swift @@ -959,7 +959,7 @@ public enum Parser { withPrologue prologue: DeclPrologue, in state: inout ParserState ) throws -> TraitDecl.ID? { - if state.take(.trait) == nil { return nil } + guard let introducer = state.take(.trait) else { return nil } // Parse the parts of the declaration. let name = try state.expect("identifier", using: { $0.take(.name) }) @@ -985,6 +985,7 @@ public enum Parser { // Create a new `TraitDecl`. return state.insert( TraitDecl( + introducerSite: introducer.site, accessModifier: declAccessModifier(ofDeclPrologue: prologue, in: &state), identifier: state.token(name), refinements: refinements, @@ -1376,6 +1377,7 @@ public enum Parser { return AnyExprID( state.insert( CastExpr( + introducerSite: infixOperator.site, left: lhs, right: rhs, direction: castKind, @@ -1746,6 +1748,7 @@ public enum Parser { return state.insert( ExistentialTypeExpr( + introducerSite: introducer.site, traits: traits, whereClause: clause, site: introducer.site.extended( @@ -1980,16 +1983,18 @@ public enum Parser { let c = try state.expect("condition", using: conditionalClause) let a = try state.expect("'{'", using: parseBracedExpr(in:)) - _ = try state.expect("'else'", using: { $0.take(.else) }) + let elseIntroducer = try state.expect("'else'", using: { $0.take(.else) }) let b: AnyExprID = try state.expect( "expression", using: { (s) in try parseConditionalExpr(in: &s).map(AnyExprID.init(_:)) ?? parseBracedExpr(in: &s) }) + let elseClause = Introduced(introducerSite: elseIntroducer.site, value: b) + return state.insert( ConditionalExpr( - introducerSite: introducer.site, condition: c, success: a, failure: b, + introducerSite: introducer.site, condition: c, success: a, failure: elseClause, site: state.range(from: introducer.site.start))) } @@ -2015,6 +2020,7 @@ public enum Parser { return state.insert( MatchExpr( + introducerSite: introducer.site, subject: subject, cases: cases, site: state.range(from: introducer.site.start))) @@ -2727,6 +2733,21 @@ public enum Parser { DiscardStmt(expr: tree.1, site: tree.0.0.site.extended(upTo: state.currentIndex))) })) + private static func parseElseClause( + in state: inout ParserState + ) throws -> Introduced? { + guard let introducer = state.take(.else) else { return nil } + + if let s = try parseConditionalStmt(in: &state) { + let s = AnyStmtID(s) + return Introduced(introducerSite: introducer.site, value: s) + } + else { + let s = AnyStmtID(try state.expect("'{'", using: braceStmt)) + return Introduced(introducerSite: introducer.site, value: s) + } + } + /// Parses a conditional statement. private static func parseConditionalStmt( in state: inout ParserState @@ -2735,16 +2756,11 @@ public enum Parser { let c = try state.expect("condition", using: conditionalClause) let a = try state.expect("'{'", using: braceStmt) - let b = try state.take(.else).map({ _ in - if let s = try parseConditionalStmt(in: &state) { - return AnyStmtID(s) - } else { - return AnyStmtID(try state.expect("'{'", using: braceStmt)) - } - }) + let b = try parseElseClause(in: &state) return state.insert( ConditionalStmt( + introducerSite: introducer.site, condition: c, success: a, failure: b, site: state.range(from: introducer.site.start))) } @@ -2754,7 +2770,8 @@ public enum Parser { .map({ (state, tree) -> DoWhileStmt.ID in state.insert( DoWhileStmt( - body: tree.0.0.1, condition: tree.1, + introducerSite: tree.0.0.0.site, + body: tree.0.0.1, condition: Introduced(introducerSite: tree.0.1.site, value: tree.1), site: tree.0.0.0.site.extended(upTo: state.currentIndex))) })) @@ -2763,6 +2780,7 @@ public enum Parser { .map({ (state, tree) -> WhileStmt.ID in state.insert( WhileStmt( + introducerSite: tree.0.0.site, condition: tree.0.1, body: tree.1, site: tree.0.0.site.extended(upTo: state.currentIndex))) })) @@ -2779,13 +2797,14 @@ public enum Parser { site: startOfBindingDecl)) return state.insert( ForStmt( + introducerSite: tree.0.0.0.0.site, binding: decl, domain: tree.0.0.1, filter: tree.0.1, body: tree.1, site: tree.0.0.0.0.site.extended(upTo: state.currentIndex))) })) - static let forSite = (take(.in).and(expr).second) + static let forSite = (take(.in).and(expr).map({ (state, tree) in Introduced(introducerSite: tree.0.site, value: tree.1)})) - static let forFilter = (take(.where).and(expr).second) + static let forFilter = (take(.where).and(expr).map({ (state, tree) in Introduced(introducerSite: tree.0.site, value: tree.1)})) static let loopBody = inContext(.loopBody, apply: braceStmt) @@ -2794,6 +2813,7 @@ public enum Parser { .map({ (state, tree) -> ReturnStmt.ID in state.insert( ReturnStmt( + introducerSite: tree.0.site, value: tree.1, site: tree.0.site.extended(upTo: state.currentIndex))) })) @@ -2803,6 +2823,7 @@ public enum Parser { .map({ (state, tree) -> YieldStmt.ID in state.insert( YieldStmt( + introducerSite: tree.0.site, value: tree.1, site: tree.0.site.extended(upTo: state.currentIndex))) })) @@ -2973,7 +2994,7 @@ public enum Parser { (take(.where).and(whereClauseConstraintList) .map({ (state, tree) -> SourceRepresentable in SourceRepresentable( - value: WhereClause(constraints: tree.1), + value: WhereClause(introducerSite: tree.0.site, constraints: tree.1), range: tree.0.site.extended(upTo: state.currentIndex)) })) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 864374761..843d0d9b8 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -837,7 +837,7 @@ struct TypeChecker { check(program[s].condition) check(program[s].success) if let b = program[s].failure { - check(b) + check(b.value) } } @@ -874,7 +874,7 @@ struct TypeChecker { /// Type checks `s`. private mutating func check(_ s: DoWhileStmt.ID) { check(program[s].body) - check(program[s].condition, coercibleTo: ^program.ast.coreType("Bool")!) + check(program[s].condition.value, coercibleTo: ^program.ast.coreType("Bool")!) } /// Type checks `s`. @@ -3705,7 +3705,7 @@ struct TypeChecker { check(program[e].condition) let a = inferredType(of: program[e].success, withHint: hint, updating: &obligations) - let b = inferredType(of: program[e].failure, withHint: hint, updating: &obligations) + let b = inferredType(of: program[e].failure.value, withHint: hint, updating: &obligations) let t = ^freshVariable() obligations.insert( MergingConstraint(t, [a, b], origin: .init(.branchMerge, at: program[e].introducerSite))) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 2bba51372..331334baa 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -931,7 +931,7 @@ struct Emitter { return .next } - switch emit(stmt: failure) { + switch emit(stmt: failure.value) { case .next: insert(module.makeBranch(to: tail, at: ast[s].site)) case .return(let s): @@ -988,7 +988,7 @@ struct Emitter { } } - let c = emit(branchCondition: ast[s].condition) + let c = emit(branchCondition: ast[s].condition.value) emitDeallocTopFrame(at: ast[s].site) frames.pop() @@ -1188,7 +1188,7 @@ struct Emitter { // Emit the failure branch. insertionPoint = .end(of: failure) - pushing(Frame(), { $0.emitStore(value: $0.ast[e].failure, to: storage) }) + pushing(Frame(), { $0.emitStore(value: $0.ast[e].failure.value, to: storage) }) insert(module.makeBranch(to: tail, at: ast[e].site)) insertionPoint = .end(of: tail) From 81e1f17b80728c6f07d3e2e231352bca2c70139f Mon Sep 17 00:00:00 2001 From: Nils Hjelte Date: Wed, 27 Sep 2023 19:49:47 +0200 Subject: [PATCH 033/157] Fix spelling --- Sources/Core/AST/Introduced.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Core/AST/Introduced.swift b/Sources/Core/AST/Introduced.swift index 6725415b5..c8481515e 100644 --- a/Sources/Core/AST/Introduced.swift +++ b/Sources/Core/AST/Introduced.swift @@ -1,5 +1,5 @@ // Child node with introducer keyword, eg else clause in if-else -// The NodeIDProtocol conformance is used for BundledNode compability +// The NodeIDProtocol conformance is used for BundledNode compatibility // eg DoWhileStmt in Emitter.swift: `program[s].condition.site`` public struct Introduced: NodeIDProtocol { public let introducerSite: SourceRange From 346a756f8ec7e1750e1b3a5706fd08e058d840b8 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 21:09:36 +0200 Subject: [PATCH 034/157] Expose the 'llvm.ctpop.*' as a built-in function --- Sources/CodeGen/LLVM/Transpilation.swift | 6 ++++++ Sources/Core/BuiltinFunction.swift | 4 ++++ Sources/Core/NativeInstruction.swift | 6 ++++++ Tests/HyloTests/BuiltinFunctionTests.swift | 8 ++++++++ 4 files changed, 24 insertions(+) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index 5bcdc82a5..ef472bf2b 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -927,6 +927,12 @@ extension LLVM.Module { let source = llvm(s.operands[0]) register[.register(i)] = insertFPTrunc(source, to: target, at: insertionPoint) + case .ctpop(let t): + let source = llvm(s.operands[0]) + let f = intrinsic( + named: Intrinsic.llvm.ctpop, for: [ir.llvm(builtinType: t, in: &self)])! + register[.register(i)] = insertCall(LLVM.Function(f)!, on: [source], at: insertionPoint) + case .zeroinitializer(let t): register[.register(i)] = ir.llvm(builtinType: t, in: &self).null diff --git a/Sources/Core/BuiltinFunction.swift b/Sources/Core/BuiltinFunction.swift index eae88053d..02357a662 100644 --- a/Sources/Core/BuiltinFunction.swift +++ b/Sources/Core/BuiltinFunction.swift @@ -229,6 +229,10 @@ extension BuiltinFunction { guard let (s, d) = (builtinType ++ builtinType)(&tokens) else { return nil } self = .init(name: .llvm(.fptosi(s, d))) + case "ctpop": + guard let t = builtinType(&tokens) else { return nil } + self = .init(name: .llvm(.ctpop(t))) + case "zeroinitializer": guard let t = builtinType(&tokens) else { return nil } self = .init(name: .llvm(.zeroinitializer(t))) diff --git a/Sources/Core/NativeInstruction.swift b/Sources/Core/NativeInstruction.swift index 33d5d10c4..77327c675 100644 --- a/Sources/Core/NativeInstruction.swift +++ b/Sources/Core/NativeInstruction.swift @@ -107,6 +107,8 @@ public enum NativeInstruction: Hashable { case fptosi(BuiltinType, BuiltinType) + case ctpop(BuiltinType) + case zeroinitializer(BuiltinType) // Corresponding LLVM instruction: get_elementptr_inbounds. @@ -222,6 +224,8 @@ extension NativeInstruction { return .init(^s, to: ^d) case .fptosi(let s, let d): return .init(^s, to: ^d) + case .ctpop(let t): + return .init(^t, to: ^t) case .zeroinitializer(let t): return .init(to: ^t) case .advancedByBytes(let byteOffset): @@ -309,6 +313,8 @@ extension NativeInstruction: CustomStringConvertible { return "fptoui_\(l)_\(r)" case .fptosi(let l, let r): return "fptosi_\(l)_\(r)" + case .ctpop(let t): + return "ctpop_\(t)" case .zeroinitializer(let t): return "zeroinitializer_\(t)" case .advancedByBytes(let t): diff --git a/Tests/HyloTests/BuiltinFunctionTests.swift b/Tests/HyloTests/BuiltinFunctionTests.swift index 3e53a9d4f..9aad969e9 100644 --- a/Tests/HyloTests/BuiltinFunctionTests.swift +++ b/Tests/HyloTests/BuiltinFunctionTests.swift @@ -174,6 +174,14 @@ final class BuiltinFunctionTests: XCTestCase { createInstanceWithType: expectedType) } + func testCountOnes() throws { + let expectedType = LambdaType(.builtin(.i(16)), to: .builtin(.i(16))) + try assertParse( + instructions: ["ctpop"], + parameterizedBy: [["i16"]], + createInstanceWithType: expectedType) + } + func testZeroInitializer() throws { let expectedType = LambdaType(to: .builtin(.i(64))) try assertParse( From 4f493dc57d52fb8dd5a826740bd0d616d514e67f Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 21:34:20 +0200 Subject: [PATCH 035/157] Expose the 'llvm.ctlz.*' as a built-in function --- Sources/CodeGen/LLVM/Transpilation.swift | 9 +++++++-- Sources/Core/BuiltinFunction.swift | 4 ++++ Sources/Core/NativeInstruction.swift | 6 ++++++ Tests/HyloTests/BuiltinFunctionTests.swift | 8 ++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index ef472bf2b..5f8a4cebf 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -929,10 +929,15 @@ extension LLVM.Module { case .ctpop(let t): let source = llvm(s.operands[0]) - let f = intrinsic( - named: Intrinsic.llvm.ctpop, for: [ir.llvm(builtinType: t, in: &self)])! + let f = intrinsic(named: Intrinsic.llvm.ctpop, for: [ir.llvm(builtinType: t, in: &self)])! register[.register(i)] = insertCall(LLVM.Function(f)!, on: [source], at: insertionPoint) + case .ctlz(let t): + let source = llvm(s.operands[0]) + let f = intrinsic(named: Intrinsic.llvm.ctlz, for: [ir.llvm(builtinType: t, in: &self)])! + register[.register(i)] = insertCall( + LLVM.Function(f)!, on: [source, i1.zero], at: insertionPoint) + case .zeroinitializer(let t): register[.register(i)] = ir.llvm(builtinType: t, in: &self).null diff --git a/Sources/Core/BuiltinFunction.swift b/Sources/Core/BuiltinFunction.swift index 02357a662..ada0d76f2 100644 --- a/Sources/Core/BuiltinFunction.swift +++ b/Sources/Core/BuiltinFunction.swift @@ -233,6 +233,10 @@ extension BuiltinFunction { guard let t = builtinType(&tokens) else { return nil } self = .init(name: .llvm(.ctpop(t))) + case "ctlz": + guard let t = builtinType(&tokens) else { return nil } + self = .init(name: .llvm(.ctlz(t))) + case "zeroinitializer": guard let t = builtinType(&tokens) else { return nil } self = .init(name: .llvm(.zeroinitializer(t))) diff --git a/Sources/Core/NativeInstruction.swift b/Sources/Core/NativeInstruction.swift index 77327c675..d2674cfd6 100644 --- a/Sources/Core/NativeInstruction.swift +++ b/Sources/Core/NativeInstruction.swift @@ -109,6 +109,8 @@ public enum NativeInstruction: Hashable { case ctpop(BuiltinType) + case ctlz(BuiltinType) + case zeroinitializer(BuiltinType) // Corresponding LLVM instruction: get_elementptr_inbounds. @@ -226,6 +228,8 @@ extension NativeInstruction { return .init(^s, to: ^d) case .ctpop(let t): return .init(^t, to: ^t) + case .ctlz(let t): + return .init(^t, to: ^t) case .zeroinitializer(let t): return .init(to: ^t) case .advancedByBytes(let byteOffset): @@ -315,6 +319,8 @@ extension NativeInstruction: CustomStringConvertible { return "fptosi_\(l)_\(r)" case .ctpop(let t): return "ctpop_\(t)" + case .ctlz(let t): + return "ctlz_\(t)" case .zeroinitializer(let t): return "zeroinitializer_\(t)" case .advancedByBytes(let t): diff --git a/Tests/HyloTests/BuiltinFunctionTests.swift b/Tests/HyloTests/BuiltinFunctionTests.swift index 9aad969e9..0b002251e 100644 --- a/Tests/HyloTests/BuiltinFunctionTests.swift +++ b/Tests/HyloTests/BuiltinFunctionTests.swift @@ -182,6 +182,14 @@ final class BuiltinFunctionTests: XCTestCase { createInstanceWithType: expectedType) } + func testCountLeadingZeros() throws { + let expectedType = LambdaType(.builtin(.i(16)), to: .builtin(.i(16))) + try assertParse( + instructions: ["ctlz"], + parameterizedBy: [["i16"]], + createInstanceWithType: expectedType) + } + func testZeroInitializer() throws { let expectedType = LambdaType(to: .builtin(.i(64))) try assertParse( From 2cb6fb00077db4220b5e4f484c1eb5554e4ee0af Mon Sep 17 00:00:00 2001 From: Walter Smuts Date: Tue, 26 Sep 2023 16:20:12 +0200 Subject: [PATCH 036/157] Add documentation on compiler architecture --- Docs/CompilerArchitecture.md | 100 +++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 Docs/CompilerArchitecture.md diff --git a/Docs/CompilerArchitecture.md b/Docs/CompilerArchitecture.md new file mode 100644 index 000000000..8218176b7 --- /dev/null +++ b/Docs/CompilerArchitecture.md @@ -0,0 +1,100 @@ +# Compiler Architecture + +## Project Overview + +The hylo compiler is written in [Swift] and uses [LLVM] as it's code generation +backend. It conforms to the the standard swift project layout: +* [Package.swift] the manifest file used by SPM +* [Sources] all the source code for the compiler +* [Tests] all the test code + +Then there are some extra directories specific to the hylo project: +* [Tools] Scripts to aid in development +* [Examples] Some real world hylo programs +* [Library] The hylo standard (and core) library + +## Stages of compilation + +The hylo compiler goes through the standard stages of compilation: + +1. Tokenisation: Transforms hylo source code (Strings) to stream of distinct + tokens +1. Parsing: Creates an [abstract syntax tree] from the token stream +1. Type-checking: Inspects the abstract syntax tree for type errors +1. IR-lowering: Generates the [intermediate representation] from the abstract + syntax tree +1. LLVM IR generation: Convert hylo IR into [LLVM] IR +1. Machine Code Generation: This is completely handled by [LLVM] + +These top-level stages of the compiler are laid out in [Driver] where you +can see the outline of the compilation phases with their entry points. +Depending on the flags passed to the compiler, the compiler can exit early at +some of these stages. + +### Interesting parts + +Most of the compiler does what you'd expect from the compiltion stages above +but some are worth a deeper look: + +#### Abstract syntax tree + +The abstract syntax tree is made up of an **append-only** array that produces +`NodeID` objects as indices into the array. The `NodeID` allows nodes to refer +to other nodes using their `NodeID`. `NodeID` is generic over node types and +allows us to constrain which nodes are allowed as leaves of other nodes. + +The use of `NodeID` types as indices into an array allows us to define the +existence of a node, by it's `NodeID`, without providing access to the node. +For access you still need the array. This is in contrast to traditional +references that provide existence AND access without allowing separation. + +The use of `NodeID` types are ubiquitous and is often aliased to `.ID` of a +new type (e.g., `FunctionDecl.ID`). + +#### Program Protocol + +After the AST is created the compiler creates property maps that associate +properties to the nodes of the AST. Currently there are two distinct phases of +property creation for these property maps. The first is creating the +connections between scopes and nodes, stored in the `ScopedProgram` struct. The +second is where the majority of the type-checking happens, associating a type +for each expression, declaration etc. Each of these stages is composed of the +previous stage: + +[AST] < [ScopedProgram] < [TypedProgram] < [IR/Program] + +A successfully created `TypedProgram` means the hylo program is well typed. + +#### Hylo IR + +The Hylo IR is composed of instructions defined in the [Instruction] module. +The [Emitter] is the component responsible for creating the `IR` an inserting +it into the [IR/Module], module-by-module and creating an [IR/Program]. + +The hylo IR is only valid after it has gone through some mandatory passes +defined in `Module+*` files of [IR/Analysis]. After these passes the IR should +be valid and executable by a *theortical* hylo VM. Some [more passes] may be +necessary dependent on the target. + +[Swift]: https://en.wikipedia.org/wiki/Swift_(programming_language) +[LLVM]: https://en.wikipedia.org/wiki/LLVM +[SPM]: https://www.swift.org/package-manager/ +[intermediate representation]: https://en.wikipedia.org/wiki/Intermediate_representation +[abstract syntax tree]: https://en.wikipedia.org/wiki/Abstract_syntax_tree + +[Driver]: ../Sources/Driver/Driver.swift +[Package.swift]: ../Package.swift +[Sources]: ../Sources +[Tests]: ../Tests +[Tools]: ../Tools +[Examples]: ../Examples +[Library]: ../Library +[AST]: ../Sources/Core/AST/AST.swift +[ScopedProgram]: ../Sources/Core/ScopedProgram.swift +[TypedProgram]: ../Sources/FrontEnd/TypedProgram.swift +[Instruction]: ../Sources/IR/Operands/Instruction/ +[Emitter]: ../Sources/IR/Emitter.swift +[IR/Module]: ../Sources/IR/Module.swift +[IR/Program]: ../Sources/IR/Program.swift +[IR/Analysis]: ../Sources/IR/Analysis/ +[more passes]: ../Sources/IR/Analysis/Module+Depolymorphize.swift From d045c4f7fe7ede63d639ca48d1e3c78fa0c9ea23 Mon Sep 17 00:00:00 2001 From: Walter Smuts Date: Wed, 27 Sep 2023 11:28:52 +0200 Subject: [PATCH 037/157] Link to CompilerArchitecture from CONTRIBUTING --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e98ea0094..8ce9f8291 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,7 +36,8 @@ We greatly appreciate it if you can isolate the problem and provide a minimal re ## Contributing code -Please get familiar with our [CONVENTIONS.md](project conventions). +Please get familiar with our [CONVENTIONS.md](project conventions). See +[CompilerArchitecture.md] for an overview on the design of the compiler. We use the standard GitHub workflow to merge code contributions: 1. Fork this repository. @@ -65,3 +66,4 @@ Do not hesitate to reach out if you are lost in the process. [typos-action]: https://github.com/marketplace/actions/typos-action [install typos]: https://github.com/crate-ci/typos#install [typos false positives documentation]: https://github.com/crate-ci/typos#false-positives +[CompilerArchitecture.md]: Docs/CompilerArchitecture.md From 107776ba3ba3840b9c635fadefbc02b674d1609f Mon Sep 17 00:00:00 2001 From: Walter Smuts Date: Wed, 27 Sep 2023 11:44:41 +0200 Subject: [PATCH 038/157] Fix bug in markdown link --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8ce9f8291..e53d4a492 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,7 +36,7 @@ We greatly appreciate it if you can isolate the problem and provide a minimal re ## Contributing code -Please get familiar with our [CONVENTIONS.md](project conventions). See +Please get familiar with our [project conventions](CONVENTIONS.md). See [CompilerArchitecture.md] for an overview on the design of the compiler. We use the standard GitHub workflow to merge code contributions: From fa262305ffd9d5391ecf2afac5b847c2b5c9080a Mon Sep 17 00:00:00 2001 From: Walter Smuts Date: Wed, 27 Sep 2023 11:33:10 +0200 Subject: [PATCH 039/157] Add workflow to check for broken markdown links See details and instructions here: https://github.com/marketplace/actions/markdown-link-check --- .github/workflows/markdown-link-check.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/workflows/markdown-link-check.yml diff --git a/.github/workflows/markdown-link-check.yml b/.github/workflows/markdown-link-check.yml new file mode 100644 index 000000000..0c7360b24 --- /dev/null +++ b/.github/workflows/markdown-link-check.yml @@ -0,0 +1,10 @@ +name: Check Markdown links + +on: [pull_request, push] + +jobs: + markdown-link-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - uses: gaurav-nelson/github-action-markdown-link-check@v1 From 2df6a45c25e675dc89440c0c24a4c726e4c9ad28 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 22:08:15 +0200 Subject: [PATCH 040/157] Expose the 'llvm.cttz.*' as a built-in function --- Sources/CodeGen/LLVM/Transpilation.swift | 6 ++++++ Sources/Core/BuiltinFunction.swift | 4 ++++ Sources/Core/NativeInstruction.swift | 6 ++++++ Tests/HyloTests/BuiltinFunctionTests.swift | 8 ++++++++ 4 files changed, 24 insertions(+) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index 5f8a4cebf..24ad2e7e5 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -938,6 +938,12 @@ extension LLVM.Module { register[.register(i)] = insertCall( LLVM.Function(f)!, on: [source, i1.zero], at: insertionPoint) + case .cttz(let t): + let source = llvm(s.operands[0]) + let f = intrinsic(named: Intrinsic.llvm.cttz, for: [ir.llvm(builtinType: t, in: &self)])! + register[.register(i)] = insertCall( + LLVM.Function(f)!, on: [source, i1.zero], at: insertionPoint) + case .zeroinitializer(let t): register[.register(i)] = ir.llvm(builtinType: t, in: &self).null diff --git a/Sources/Core/BuiltinFunction.swift b/Sources/Core/BuiltinFunction.swift index ada0d76f2..0c2793213 100644 --- a/Sources/Core/BuiltinFunction.swift +++ b/Sources/Core/BuiltinFunction.swift @@ -237,6 +237,10 @@ extension BuiltinFunction { guard let t = builtinType(&tokens) else { return nil } self = .init(name: .llvm(.ctlz(t))) + case "cttz": + guard let t = builtinType(&tokens) else { return nil } + self = .init(name: .llvm(.cttz(t))) + case "zeroinitializer": guard let t = builtinType(&tokens) else { return nil } self = .init(name: .llvm(.zeroinitializer(t))) diff --git a/Sources/Core/NativeInstruction.swift b/Sources/Core/NativeInstruction.swift index d2674cfd6..bb58ed82e 100644 --- a/Sources/Core/NativeInstruction.swift +++ b/Sources/Core/NativeInstruction.swift @@ -111,6 +111,8 @@ public enum NativeInstruction: Hashable { case ctlz(BuiltinType) + case cttz(BuiltinType) + case zeroinitializer(BuiltinType) // Corresponding LLVM instruction: get_elementptr_inbounds. @@ -230,6 +232,8 @@ extension NativeInstruction { return .init(^t, to: ^t) case .ctlz(let t): return .init(^t, to: ^t) + case .cttz(let t): + return .init(^t, to: ^t) case .zeroinitializer(let t): return .init(to: ^t) case .advancedByBytes(let byteOffset): @@ -321,6 +325,8 @@ extension NativeInstruction: CustomStringConvertible { return "ctpop_\(t)" case .ctlz(let t): return "ctlz_\(t)" + case .cttz(let t): + return "cttz_\(t)" case .zeroinitializer(let t): return "zeroinitializer_\(t)" case .advancedByBytes(let t): diff --git a/Tests/HyloTests/BuiltinFunctionTests.swift b/Tests/HyloTests/BuiltinFunctionTests.swift index 0b002251e..6065ca4dc 100644 --- a/Tests/HyloTests/BuiltinFunctionTests.swift +++ b/Tests/HyloTests/BuiltinFunctionTests.swift @@ -190,6 +190,14 @@ final class BuiltinFunctionTests: XCTestCase { createInstanceWithType: expectedType) } + func testCountTrailingZeros() throws { + let expectedType = LambdaType(.builtin(.i(16)), to: .builtin(.i(16))) + try assertParse( + instructions: ["cttz"], + parameterizedBy: [["i16"]], + createInstanceWithType: expectedType) + } + func testZeroInitializer() throws { let expectedType = LambdaType(to: .builtin(.i(64))) try assertParse( From b52a2cfd4d0adc61e4947aec6e687bf7085e436e Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 22:09:41 +0200 Subject: [PATCH 041/157] Rename 'BinaryInteger.bit_width' to 'instance_bit_width' --- Library/Hylo/Core/Integers/BinaryInteger.hylo | 7 ++++--- Library/Hylo/Core/Integers/Int.hylo | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Library/Hylo/Core/Integers/BinaryInteger.hylo b/Library/Hylo/Core/Integers/BinaryInteger.hylo index f74ab808d..f9734e3aa 100644 --- a/Library/Hylo/Core/Integers/BinaryInteger.hylo +++ b/Library/Hylo/Core/Integers/BinaryInteger.hylo @@ -4,13 +4,14 @@ public trait BinaryInteger { /// Creates an instance with value `0`. init() + // TODO: Rename to `bit_width` when #1041 is fixed. + /// Returns the number of bits in the representation of `self`. + fun instance_bit_width() -> Int + /// Returns `-1` if `self` is negative, `1` if it is positive, or `0` otherwise. fun signum() -> Int /// Returns `true` if this type is a signed integer. static fun is_signed() -> Bool - /// Returns the number of bits in the representation of an instance of this type. - static fun bit_width() -> Int - } diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index ef6759d75..34c833196 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -232,8 +232,8 @@ public conformance Int: Copyable { public conformance Int: BinaryInteger { - public static fun is_signed() -> Bool { - true + public fun instance_bit_width() -> Int { + Self.bit_width() } public fun signum() -> Int { From 2ae4b38cb4dce83c98cae4d6cede7932d69774fd Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 22:11:20 +0200 Subject: [PATCH 042/157] Reorder declarations for consistency --- Library/Hylo/Core/Integers/Int.hylo | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index 34c833196..bdacd9aa2 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -245,18 +245,6 @@ public conformance Int: BinaryInteger { public conformance Int: FixedWidthInteger { - public static fun bit_width() -> Int { - MemoryLayout.size() * 8 - } - - public static fun max() -> Self { - ~0 &>> 1 - } - - public static fun min() -> Self { - 1 &<< (bit_width() - 1) - } - public fun adding_reporting_overflow(_ other: Self) -> {partial_value: Self, overflow: Bool} { let r = Builtin.sadd_with_overflow_word(value, other.value) return (partial_value: Int(value: r.0), overflow: Bool(value: r.1)) @@ -298,6 +286,18 @@ public conformance Int: FixedWidthInteger { return (partial_value: Int(value: Builtin.srem_word(value, other.value)), overflow: false) } + public static fun bit_width() -> Int { + MemoryLayout.size() * 8 + } + + public static fun max() -> Self { + ~0 &>> 1 + } + + public static fun min() -> Self { + 1 &<< (bit_width() - 1) + } + } public conformance Int: Comparable {} From 288d79217704bd72848d5f8a756b2235e538fb36 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 22:12:18 +0200 Subject: [PATCH 043/157] Add bit manipulation APIs to 'BinaryInteger' --- Library/Hylo/Core/Integers/BinaryInteger.hylo | 11 +++++++++++ Library/Hylo/Core/Integers/Int.hylo | 16 ++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/Library/Hylo/Core/Integers/BinaryInteger.hylo b/Library/Hylo/Core/Integers/BinaryInteger.hylo index f9734e3aa..2acd843d8 100644 --- a/Library/Hylo/Core/Integers/BinaryInteger.hylo +++ b/Library/Hylo/Core/Integers/BinaryInteger.hylo @@ -11,6 +11,17 @@ public trait BinaryInteger { /// Returns `-1` if `self` is negative, `1` if it is positive, or `0` otherwise. fun signum() -> Int + /// Returns the number of bits equal to `1` in the representation of `self`. + fun nonzero_bit_count() -> Int + + /// Returns the number of bits equal to `0` on the left of the most significant bit equal to + /// `1` in the representation of `self`. + fun leading_zeros() -> Int + + /// Returns the number of bits equal to `0` on the right of the least significant bit equal to + /// `1` in the representation of `self`. + fun trailing_zeros() -> Int + /// Returns `true` if this type is a signed integer. static fun is_signed() -> Bool diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index bdacd9aa2..4b7b8bb6e 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -241,6 +241,22 @@ public conformance Int: BinaryInteger { return positive | (self &>> (Self.bit_width() - 1)) } + public fun nonzero_bit_count() -> Int { + Int(value: Builtin.ctpop_word(value)) + } + + public fun leading_zeros() -> Int { + Int(value: Builtin.ctlz_word(value)) + } + + public fun trailing_zeros() -> Int { + Int(value: Builtin.cttz_word(value)) + } + + public static fun is_signed() -> Bool { + true + } + } public conformance Int: FixedWidthInteger { From 0f6ef21fb10b94bde0ff1602eecfc07c8c5c66d3 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 22:32:50 +0200 Subject: [PATCH 044/157] Fix 'Int.min' and 'Int.max' --- Library/Hylo/Core/Integers/Int.hylo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index 4b7b8bb6e..2700d1a1b 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -307,11 +307,11 @@ public conformance Int: FixedWidthInteger { } public static fun max() -> Self { - ~0 &>> 1 + Int(value: Builtin.lshr_word((~0).value, (1).value)) } public static fun min() -> Self { - 1 &<< (bit_width() - 1) + Int(value: Builtin.shl_word((1).value, (bit_width() - 1).value)) } } From 27d410577890b65b708774437138c3395e54d5e5 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 22:33:38 +0200 Subject: [PATCH 045/157] Test bit manipulation APIs on 'Int' --- Tests/LibraryTests/TestCases/IntTests.hylo | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tests/LibraryTests/TestCases/IntTests.hylo b/Tests/LibraryTests/TestCases/IntTests.hylo index b64d8701d..86f6e2792 100644 --- a/Tests/LibraryTests/TestCases/IntTests.hylo +++ b/Tests/LibraryTests/TestCases/IntTests.hylo @@ -71,4 +71,7 @@ public fun main() { precondition(Int.bit_width() == 64) precondition(Int.max() == 9223372036854775807) precondition(Int.min() == -9223372036854775808) + precondition((-1).nonzero_bit_count() == 64) + precondition((10).leading_zeros() == 60) + precondition((10).trailing_zeros() == 1) } From b86891c0f65233cbaf34492cac6f91f58c83d846 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 22:38:00 +0200 Subject: [PATCH 046/157] Make 'UInt' conform to 'BinaryInteger' --- Library/Hylo/Core/Integers/UInt.hylo | 47 +++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/Library/Hylo/Core/Integers/UInt.hylo b/Library/Hylo/Core/Integers/UInt.hylo index 4181daa49..ed2cca1b7 100644 --- a/Library/Hylo/Core/Integers/UInt.hylo +++ b/Library/Hylo/Core/Integers/UInt.hylo @@ -20,6 +20,11 @@ public type UInt { &self.value = Builtin.ptrtoint_word(address.base) } + /// Returns `true` if `self` is greater than `other`. Otherwise, returns `false`. + public fun infix> (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ugt_word(value, other.value)) + } + /// Returns `true` if `self` is equal to `other`. Otherwise, returns `false`. public fun infix== (_ other: Self) -> Bool { Bool(value: Builtin.icmp_eq_word(value, other.value)) @@ -79,20 +84,36 @@ public conformance UInt: Copyable { } -public conformance UInt: FixedWidthInteger { +public conformance UInt: BinaryInteger { - public static fun bit_width() -> Int { - MemoryLayout.size() * 8 + public fun instance_bit_width() -> Int { + Self.bit_width() } - public static fun max() -> Self { - ~UInt() + public fun signum() -> Int { + Int(value: Builtin.zext_i1_word((self > UInt()).value)) } - public static fun min() -> Self { - 0 + public fun nonzero_bit_count() -> Int { + Int(value: Builtin.ctpop_word(value)) + } + + public fun leading_zeros() -> Int { + Int(value: Builtin.ctlz_word(value)) + } + + public fun trailing_zeros() -> Int { + Int(value: Builtin.cttz_word(value)) + } + + public static fun is_signed() -> Bool { + false } +} + +public conformance UInt: FixedWidthInteger { + public fun adding_reporting_overflow(_ other: Self) -> {partial_value: Self, overflow: Bool} { let r = Builtin.uadd_with_overflow_word(value, other.value) return (partial_value: UInt(value: r.0), overflow: Bool(value: r.1)) @@ -128,4 +149,16 @@ public conformance UInt: FixedWidthInteger { return (partial_value: UInt(value: Builtin.urem_word(value, other.value)), overflow: false) } + public static fun bit_width() -> Int { + MemoryLayout.size() * 8 + } + + public static fun max() -> Self { + ~UInt() + } + + public static fun min() -> Self { + 0 + } + } From dcace42bdc466a207a74f4e87956a4f41f799859 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 27 Sep 2023 22:38:52 +0200 Subject: [PATCH 047/157] Make 'FixedWidthInteger' a refinement of 'BinaryInteger' --- Library/Hylo/Core/Integers/FixedWidthInteger.hylo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Hylo/Core/Integers/FixedWidthInteger.hylo b/Library/Hylo/Core/Integers/FixedWidthInteger.hylo index 248d65307..fbff70984 100644 --- a/Library/Hylo/Core/Integers/FixedWidthInteger.hylo +++ b/Library/Hylo/Core/Integers/FixedWidthInteger.hylo @@ -2,7 +2,7 @@ /// /// Use this trait to write algorithms that depend on bit shifting, perform bitwise opeperations, /// catch overflows, or access the minimum or maximum representable values of an integer type. -public trait FixedWidthInteger { +public trait FixedWidthInteger: BinaryInteger { /// Returns the sum of `self` and `other` along with a flag indicating whether overflow occurred /// in the operation. From feeec53b2a1b2114c494996820c3c44605c02f6a Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 08:25:02 +0200 Subject: [PATCH 048/157] Improve documentation --- .../FrontEnd/TypeChecking/TypeChecker.swift | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 864374761..e1d083ecb 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -1092,11 +1092,17 @@ struct TypeChecker { } } - /// Returns the implementation of `r` in `model`, with `r` a requirement of `concept` with type - /// `t` and name `n`, or returns `nil` if no such implementation exist. + /// Returns the implementation of `requirement` in `model` or returns `nil` if no such + /// implementation exist. + /// + /// `requirement` is defined by `concept` and `t` is the type it is expected to have when + /// implemented by `model`. `idKind` specifies the kinds of declarations that are considered + /// as candidate implementations. `appendDefinitions` is called for each candidate in the + /// declaration space of `model` to gather those that are definitions (i.e., declarations with + /// a body) of type `t`. func implementation( of requirement: AnyDeclID, typed t: AnyType, named n: Name, - identifiedBy: D.Type = D.self, + identifiedBy idKind: D.Type = D.self, collectingCandidatesWith appendDefinitions: (D, AnyType, inout [AnyDeclID]) -> Void ) -> AnyDeclID? { guard !t[.hasError] else { return nil } @@ -1110,7 +1116,7 @@ struct TypeChecker { return viable.uniqueElement } - /// Appends to `s` the function definitions of `d` that have type `t`. + /// Appends the function definitions of `d` that have type `t` to `s` . func collectFunction(of d: AnyDeclID, matching t: AnyType, to s: inout [AnyDeclID]) { switch d.kind { case FunctionDecl.self: @@ -1122,12 +1128,12 @@ struct TypeChecker { } } - /// Appends each variant of `c` to `candidates` that is has type `t`. + /// Appends each variant of `c` to `candidates` that is has type `t` to `s`. func appendDefinitions(of d: MethodDecl.ID, matching t: AnyType, in s: inout [AnyDeclID]) { for v in program[d].impls { appendIfDefinition(v, matching: t, in: &s) } } - /// Appends `d` to `s` iff it's a a definition with type `t`. + /// Appends `d` to `s` iff `d` is a definition with type `t`. func appendIfDefinition(_ d: D.ID, matching t: AnyType, in s: inout [AnyDeclID]) { let u = type(ofMember: AnyDeclID(d)) if program[d].isDefinition && areEquivalent(t, u, in: scopeOfExposition) { From 2d2c92d341e01b1043ab6da55938629f01d8672d Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 08:54:58 +0200 Subject: [PATCH 049/157] Support subscript requirements during conformance checking --- .../FrontEnd/TypeChecking/TypeChecker.swift | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index e1d083ecb..3db4e4ec2 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -1072,7 +1072,7 @@ struct TypeChecker { case FunctionDecl.self: return implementation( of: requirement, typed: t, named: n, - collectingCandidatesWith: collectFunction) + collectingCandidatesWith: appendFunctionDefinitions) case InitializerDecl.self: return implementation( @@ -1082,10 +1082,12 @@ struct TypeChecker { case MethodImpl.self: return implementation( of: requirement, typed: t, named: n, - collectingCandidatesWith: collectFunction) + collectingCandidatesWith: appendFunctionDefinitions) case SubscriptImpl.self: - UNIMPLEMENTED() + return implementation( + of: requirement, typed: t, named: n, identifiedBy: SubscriptDecl.ID.self, + collectingCandidatesWith: appendDefinitions) default: unexpected(requirement, in: program.ast) @@ -1117,24 +1119,29 @@ struct TypeChecker { } /// Appends the function definitions of `d` that have type `t` to `s` . - func collectFunction(of d: AnyDeclID, matching t: AnyType, to s: inout [AnyDeclID]) { + func appendFunctionDefinitions(of d: AnyDeclID, matching t: AnyType, to s: inout [AnyDeclID]) { switch d.kind { case FunctionDecl.self: - appendIfDefinition(FunctionDecl.ID(d)!, matching: t, in: &s) + appendIfDefinition(FunctionDecl.ID(d)!, matching: t, to: &s) case MethodDecl.self: - appendDefinitions(of: MethodDecl.ID(d)!, matching: t, in: &s) + appendDefinitions(of: MethodDecl.ID(d)!, matching: t, to: &s) default: break } } /// Appends each variant of `c` to `candidates` that is has type `t` to `s`. - func appendDefinitions(of d: MethodDecl.ID, matching t: AnyType, in s: inout [AnyDeclID]) { - for v in program[d].impls { appendIfDefinition(v, matching: t, in: &s) } + func appendDefinitions(of d: MethodDecl.ID, matching t: AnyType, to s: inout [AnyDeclID]) { + for v in program[d].impls { appendIfDefinition(v, matching: t, to: &s) } + } + + /// Appends each variant of `c` to `candidates` that is has type `t` to `s`. + func appendDefinitions(of d: SubscriptDecl.ID, matching t: AnyType, to s: inout [AnyDeclID]) { + for v in program[d].impls { appendIfDefinition(v, matching: t, to: &s) } } /// Appends `d` to `s` iff `d` is a definition with type `t`. - func appendIfDefinition(_ d: D.ID, matching t: AnyType, in s: inout [AnyDeclID]) { + func appendIfDefinition(_ d: D.ID, matching t: AnyType, to s: inout [AnyDeclID]) { let u = type(ofMember: AnyDeclID(d)) if program[d].isDefinition && areEquivalent(t, u, in: scopeOfExposition) { s.append(AnyDeclID(d)) From 5f842451504ee9f7c4fc2ed7828ffe04771d8bf3 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 09:11:48 +0200 Subject: [PATCH 050/157] Document an unimplemented feature --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 3db4e4ec2..2e00cc1ac 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -3488,7 +3488,7 @@ struct TypeChecker { func instantiate(mutating me: inout Self, type: AnyType) -> TypeTransformAction { switch type.base { case is AssociatedTypeType: - UNIMPLEMENTED() + UNIMPLEMENTED("quantifier elimination for associated types (#1043)") case let p as GenericTypeParameterType: if let t = substitutions[p.decl] { From 4624f020b5b93cc6f4901de4ac3792e141e2bcdc Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 09:39:39 +0200 Subject: [PATCH 051/157] Test conformance to trait with a subscript requirement --- .../TypeChecking/ConformanceWithSubscript.hylo | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Tests/HyloTests/TestCases/TypeChecking/ConformanceWithSubscript.hylo diff --git a/Tests/HyloTests/TestCases/TypeChecking/ConformanceWithSubscript.hylo b/Tests/HyloTests/TestCases/TypeChecking/ConformanceWithSubscript.hylo new file mode 100644 index 000000000..13254cf1e --- /dev/null +++ b/Tests/HyloTests/TestCases/TypeChecking/ConformanceWithSubscript.hylo @@ -0,0 +1,11 @@ +//- typeCheck expecting: success + +trait P { + type X + subscript(_ i: Int): X { let } +} + +type B: P { + typealias X = Bool + subscript(_ i: Int): Bool { true } +} From a9c7bef8b1748b25749c32f5451807a6faf1841f Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 09:40:26 +0200 Subject: [PATCH 052/157] Add a bare bone 'Collection' trait to the standard library --- Library/Hylo/Core/Collection.hylo | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Library/Hylo/Core/Collection.hylo diff --git a/Library/Hylo/Core/Collection.hylo b/Library/Hylo/Core/Collection.hylo new file mode 100644 index 000000000..ae433e4c0 --- /dev/null +++ b/Library/Hylo/Core/Collection.hylo @@ -0,0 +1,25 @@ +/// A collection of elements accessible via an indexed subscript. +public trait Collection { + + /// The type of the elements contained in `Self`. + type Element + + /// The type of the positions in `Self`. + type Index: SemiRegular + + /// Returns the position of the first element in `self`, or `end_index()` if `self` is empty. + fun start_index() -> Index + + /// Returns the "past the end" position in `self`, that is, the position immediately after the + /// last element in `self`. + fun end_index() -> Index + + /// Returns the position immediately after `i`. + /// + /// - Requires: `i != end_index()`. + fun index(after i: Index) -> Index + + /// Accesses the elment at position `i`. + subscript(_ i: Index): Element { let } + +} From cafef6a786ddfc8de5648e3059d0508c87358a38 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 11:32:07 +0200 Subject: [PATCH 053/157] Add 'CollectionOfOne' to the standard library --- Library/Hylo/Core/CollectionOfOne.hylo | 27 +++++++++++++++++++ .../TestCases/CollectionOfOneTests.hylo | 6 +++++ 2 files changed, 33 insertions(+) create mode 100644 Library/Hylo/Core/CollectionOfOne.hylo create mode 100644 Tests/LibraryTests/TestCases/CollectionOfOneTests.hylo diff --git a/Library/Hylo/Core/CollectionOfOne.hylo b/Library/Hylo/Core/CollectionOfOne.hylo new file mode 100644 index 000000000..b723f7811 --- /dev/null +++ b/Library/Hylo/Core/CollectionOfOne.hylo @@ -0,0 +1,27 @@ +/// A collection containing a single element. +public type CollectionOfOne: Deinitializable { + + public typealias Index = Bool + + /// The element contained in `self`. + var contents: Element + + /// Creates a collection containing just `contents`. + public init(_ contents: sink Element) { + &self.contents = contents + } + + public fun start_index() -> Bool { true } + + public fun end_index() -> Bool { false } + + public fun index(after i: Bool) -> Bool { false } + + public subscript(_ position: Bool): Element { + let { + precondition(position) + yield contents + } + } + +} diff --git a/Tests/LibraryTests/TestCases/CollectionOfOneTests.hylo b/Tests/LibraryTests/TestCases/CollectionOfOneTests.hylo new file mode 100644 index 000000000..8532bf240 --- /dev/null +++ b/Tests/LibraryTests/TestCases/CollectionOfOneTests.hylo @@ -0,0 +1,6 @@ +//- compileAndRun expecting: success + +public fun main() { + let c = CollectionOfOne(42) + precondition(c[true] == 42) +} From 0d4e04df5e1d17b27319ad0fa6d026f6b66ccce1 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 13:18:45 +0200 Subject: [PATCH 054/157] Add 'BinaryInteger.words' to the standard library --- Library/Hylo/Core/Integers/BinaryInteger.hylo | 7 +++++++ Library/Hylo/Core/Integers/Int.hylo | 4 ++++ Library/Hylo/Core/Integers/UInt.hylo | 4 ++++ 3 files changed, 15 insertions(+) diff --git a/Library/Hylo/Core/Integers/BinaryInteger.hylo b/Library/Hylo/Core/Integers/BinaryInteger.hylo index 2acd843d8..c6b80b102 100644 --- a/Library/Hylo/Core/Integers/BinaryInteger.hylo +++ b/Library/Hylo/Core/Integers/BinaryInteger.hylo @@ -22,6 +22,13 @@ public trait BinaryInteger { /// `1` in the representation of `self`. fun trailing_zeros() -> Int + // TODO: Define an associated type when #1042 is fixed. + /// Returns the words of in the representation of `self`, from the least to most significant. + /// + /// If `self.instance_bit_width()` is smaller than `UInt.bit_width()`, the returned collection + /// contains a single word obtained by sign-extending the representation of `self`. + fun words() -> CollectionOfOne + /// Returns `true` if this type is a signed integer. static fun is_signed() -> Bool diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index 2700d1a1b..a8dba53ed 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -253,6 +253,10 @@ public conformance Int: BinaryInteger { Int(value: Builtin.cttz_word(value)) } + public fun words() -> CollectionOfOne { + CollectionOfOne(UInt(bit_pattern: self)) + } + public static fun is_signed() -> Bool { true } diff --git a/Library/Hylo/Core/Integers/UInt.hylo b/Library/Hylo/Core/Integers/UInt.hylo index ed2cca1b7..1d38a99c3 100644 --- a/Library/Hylo/Core/Integers/UInt.hylo +++ b/Library/Hylo/Core/Integers/UInt.hylo @@ -106,6 +106,10 @@ public conformance UInt: BinaryInteger { Int(value: Builtin.cttz_word(value)) } + public fun words() -> CollectionOfOne { + CollectionOfOne(self.copy()) + } + public static fun is_signed() -> Bool { false } From 6a76950b2f9840e31454a22a743493191805fbe0 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 13:19:18 +0200 Subject: [PATCH 055/157] Implement 'Int.init(truncating_or_extending)' --- Library/Hylo/Core/Integers/Int.hylo | 5 +++++ Library/Hylo/Core/Integers/UInt.hylo | 5 +++++ Tests/LibraryTests/TestCases/IntTests.hylo | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index a8dba53ed..c6b7d1588 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -232,6 +232,11 @@ public conformance Int: Copyable { public conformance Int: BinaryInteger { + public init(truncating_or_extending source: T) { + let w = source.words() + &self.value = w[w.start_index()].value + } + public fun instance_bit_width() -> Int { Self.bit_width() } diff --git a/Library/Hylo/Core/Integers/UInt.hylo b/Library/Hylo/Core/Integers/UInt.hylo index 1d38a99c3..010293fec 100644 --- a/Library/Hylo/Core/Integers/UInt.hylo +++ b/Library/Hylo/Core/Integers/UInt.hylo @@ -86,6 +86,11 @@ public conformance UInt: Copyable { public conformance UInt: BinaryInteger { + public init(truncating_or_extending source: T) { + let w = source.words() + &self.value = w[w.start_index()].value + } + public fun instance_bit_width() -> Int { Self.bit_width() } diff --git a/Tests/LibraryTests/TestCases/IntTests.hylo b/Tests/LibraryTests/TestCases/IntTests.hylo index 86f6e2792..6feea671f 100644 --- a/Tests/LibraryTests/TestCases/IntTests.hylo +++ b/Tests/LibraryTests/TestCases/IntTests.hylo @@ -12,6 +12,11 @@ fun test_max() { precondition(max[2, 1] == 2) } +fun test_init_truncating_or_extending() { + let a = Int(truncating_or_extending: UInt.max()) + precondition(a == -1) +} + fun test_signum() { precondition(Int().signum() == 0) precondition((+10).signum() == +1) From f0166f1c0d754b752ba4de5af156a7c0fb6ff176 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 13:29:52 +0200 Subject: [PATCH 056/157] Define 'init(truncating_or_extending:)' as a requirement of 'BinaryInteger' --- Library/Hylo/Core/Integers/BinaryInteger.hylo | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Library/Hylo/Core/Integers/BinaryInteger.hylo b/Library/Hylo/Core/Integers/BinaryInteger.hylo index c6b80b102..0aa271565 100644 --- a/Library/Hylo/Core/Integers/BinaryInteger.hylo +++ b/Library/Hylo/Core/Integers/BinaryInteger.hylo @@ -4,6 +4,11 @@ public trait BinaryInteger { /// Creates an instance with value `0`. init() + // TODO: Uncomment when #1045 is fixed. + // /// Creates an instance with the bit representation of `source`, truncating or sign-extending it + // // to fit the bit representation of `Self`. + // init(truncating_or_extending source: T) + // TODO: Rename to `bit_width` when #1041 is fixed. /// Returns the number of bits in the representation of `self`. fun instance_bit_width() -> Int From f53a91c4655c94153ee25f5f9b5017401629e5bb Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 15:29:27 +0200 Subject: [PATCH 057/157] Add 'quotient_and_remainder(diiding_by:)' to 'BinaryInteger' --- Library/Hylo/Core/Integers/BinaryInteger.hylo | 5 +++++ Library/Hylo/Core/Integers/Int.hylo | 4 ++++ Library/Hylo/Core/Integers/UInt.hylo | 11 +++++++++++ 3 files changed, 20 insertions(+) diff --git a/Library/Hylo/Core/Integers/BinaryInteger.hylo b/Library/Hylo/Core/Integers/BinaryInteger.hylo index 0aa271565..b2e76ccd8 100644 --- a/Library/Hylo/Core/Integers/BinaryInteger.hylo +++ b/Library/Hylo/Core/Integers/BinaryInteger.hylo @@ -27,6 +27,11 @@ public trait BinaryInteger { /// `1` in the representation of `self`. fun trailing_zeros() -> Int + /// Returns the quotient and remainder of dividing `self` by `other`. + /// + /// - Requires: `other` is different from `0`. + fun quotient_and_remainder(dividing_by other: Self) -> {quotient: Self, remainder: Self} + // TODO: Define an associated type when #1042 is fixed. /// Returns the words of in the representation of `self`, from the least to most significant. /// diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index c6b7d1588..c9f4f2f6d 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -258,6 +258,10 @@ public conformance Int: BinaryInteger { Int(value: Builtin.cttz_word(value)) } + public fun quotient_and_remainder(dividing_by other: Self) -> {quotient: Self, remainder: Self} { + (quotient: self / other, remainder: self & other) + } + public fun words() -> CollectionOfOne { CollectionOfOne(UInt(bit_pattern: self)) } diff --git a/Library/Hylo/Core/Integers/UInt.hylo b/Library/Hylo/Core/Integers/UInt.hylo index 010293fec..8f48ce49d 100644 --- a/Library/Hylo/Core/Integers/UInt.hylo +++ b/Library/Hylo/Core/Integers/UInt.hylo @@ -20,6 +20,13 @@ public type UInt { &self.value = Builtin.ptrtoint_word(address.base) } + /// Returns the quotient of dividing `self` by `other`. + /// + /// - Requires: `other` is different from `0`. + public fun infix/ (_ other: Self) -> Self { + UInt(value: Builtin.udiv_word(value, other.value)) + } + /// Returns `true` if `self` is greater than `other`. Otherwise, returns `false`. public fun infix> (_ other: Self) -> Bool { Bool(value: Builtin.icmp_ugt_word(value, other.value)) @@ -111,6 +118,10 @@ public conformance UInt: BinaryInteger { Int(value: Builtin.cttz_word(value)) } + public fun quotient_and_remainder(dividing_by other: Self) -> {quotient: Self, remainder: Self} { + (quotient: self / other, remainder: self & other) + } + public fun words() -> CollectionOfOne { CollectionOfOne(self.copy()) } From 6f5144728efb317ca15e63b7f7a6bb8b84b90d74 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 15:57:33 +0200 Subject: [PATCH 058/157] Add common binary operations as requirements of 'BinaryInteger' --- Library/Hylo/Core/Integers/BinaryInteger.hylo | 62 +++++++ Library/Hylo/Core/Integers/Int.hylo | 170 ++++++++---------- Library/Hylo/Core/Integers/UInt.hylo | 130 +++++++++----- 3 files changed, 223 insertions(+), 139 deletions(-) diff --git a/Library/Hylo/Core/Integers/BinaryInteger.hylo b/Library/Hylo/Core/Integers/BinaryInteger.hylo index b2e76ccd8..a25b15be6 100644 --- a/Library/Hylo/Core/Integers/BinaryInteger.hylo +++ b/Library/Hylo/Core/Integers/BinaryInteger.hylo @@ -39,6 +39,68 @@ public trait BinaryInteger { /// contains a single word obtained by sign-extending the representation of `self`. fun words() -> CollectionOfOne + /// Returns the quotient of dividing `self` by `other`. + /// + /// - Requires: `other` is different from `0`. + fun infix/ (_ other: Self) -> Self + + /// Writes the quotient of dividing `self` by `other` to `self`. + /// + /// - Requires: `other` is different from `0`. + fun infix/= (_ other: Self) inout + + /// Returns the remainder of dividing `self` by `other`. + /// + /// - Requires: `other` is different from `0`. + fun infix% (_ other: Self) -> Self + + /// Writes the remainder of dividing `self` by `other` to `self`. + /// + /// - Requires: `other` is different from `0`. + fun infix%= (_ other: Self) inout + + /// Returns `true` if `self` is smaller than `other`. Otherwise, returns `false`. + fun infix< (_ other: Self) -> Bool + + /// Returns `true` if `self` is smaller than or equal to `other`. Otherwise, returns `false`. + fun infix<= (_ other: Self) -> Bool + + /// Returns `true` if `self` is greater than `other`. Otherwise, returns `false`. + fun infix> (_ other: Self) -> Bool + + /// Returns `true` if `self` is greater than or equal to `other`. Otherwise, returns `false`. + fun infix>= (_ other: Self) -> Bool + + /// Returns the bitwise AND of `self` and `other`. + fun infix& (_ other: Self) -> Self + + /// Writes the bitwise AND of `self` and `other` to `self`. + fun infix&= (_ other: Self) inout + + /// Returns the bitwise OR of `self` and `other`. + fun infix| (_ other: Self) -> Self + + /// Writes the bitwise OR of `self` and `other` to `self`. + fun infix|= (_ other: Self) inout + + /// Returns the bitwise XOR of `self` and `other`. + fun infix^ (_ other: Self) -> Self + + /// Writes the bitwise XOR of `self` and `other` to `self`. + fun infix^= (_ other: Self) inout + + /// Returns `self` with its binary representation shifted by `n` digits to the left. + fun infix<< (_ n: Int) -> Self + + /// Shifts the binary representation of `self` by `n` digits to the left. + fun infix<<= (_ n: Int) inout + + /// Returns `self` with its binary representation shifted by `n` digits to the right. + fun infix>> (_ n: Int) -> Self + + /// Shifts the binary representation of `self` by `n` digits to the right. + fun infix>>= (_ n: Int) inout + /// Returns `true` if this type is a signed integer. static fun is_signed() -> Bool diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index c9f4f2f6d..9d8f6c01e 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -75,34 +75,6 @@ public type Int { &self.value = Builtin.mul_word(value, other.value) } - /// Returns the quotient of dividing `self` by `other`. - /// - /// - Requires: `other` is different from `0`. - public fun infix/ (_ other: Self) -> Self { - Int(value: Builtin.sdiv_word(value, other.value)) - } - - /// Writes the quotient of dividing `self` by `other` to `self`. - /// - /// - Requires: `other` is different from `0`. - public fun infix/= (_ other: Self) inout { - &self.value = Builtin.sdiv_word(value, other.value) - } - - /// Returns the remainder of dividing `self` by `other`. - /// - /// - Requires: `other` is different from `0`. - public fun infix% (_ other: Self) -> Self { - Int(value: Builtin.srem_word(value, other.value)) - } - - /// Writes the remainder of dividing `self` by `other` to `self`. - /// - /// - Requires: `other` is different from `0`. - public fun infix%= (_ other: Self) inout { - &self.value = Builtin.srem_word(value, other.value) - } - /// Returns the additive inverse of `self`. public fun prefix- () -> Self { Int() - self @@ -113,26 +85,6 @@ public type Int { self.copy() } - /// Returns `true` if `self` is smaller than `other`. Otherwise, returns `false`. - public fun infix< (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_slt_word(value, other.value)) - } - - /// Returns `true` if `self` is smaller than or equal to `other`. Otherwise, returns `false`. - public fun infix<= (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_sle_word(value, other.value)) - } - - /// Returns `true` if `self` is greater than `other`. Otherwise, returns `false`. - public fun infix> (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_sgt_word(value, other.value)) - } - - /// Returns `true` if `self` is greater than or equal to `other`. Otherwise, returns `false`. - public fun infix>= (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_sge_word(value, other.value)) - } - /// Returns `true` if `self` is equal to `other`. Otherwise, returns `false`. public fun infix== (_ other: Self) -> Bool { Bool(value: Builtin.icmp_eq_word(value, other.value)) @@ -143,46 +95,11 @@ public type Int { Bool(value: Builtin.icmp_ne_word(value, other.value)) } - /// Returns the bitwise AND of `self` and `other`. - public fun infix& (_ other: Self) -> Self { - Int(value: Builtin.and_word(value, other.value)) - } - - /// Writes the bitwise AND of `self` and `other` to `self`. - public fun infix&= (_ other: Self) inout { - &self.value = Builtin.and_word(value, other.value) - } - - /// Returns the bitwise OR of `self` and `other`. - public fun infix| (_ other: Self) -> Self { - Int(value: Builtin.or_word(value, other.value)) - } - - /// Writes the bitwise OR of `self` and `other` to `self`. - public fun infix|= (_ other: Self) inout { - &self.value = Builtin.or_word(value, other.value) - } - - /// Returns the bitwise XOR of `self` and `other`. - public fun infix^ (_ other: Self) -> Self { - Int(value: Builtin.xor_word(value, other.value)) - } - - /// Writes the bitwise XOR of `self` and `other` to `self`. - public fun infix^= (_ other: Self) inout { - &self.value = Builtin.xor_word(value, other.value) - } - /// Returns the bitwise inverse of `self`. public fun prefix~ () -> Self { self ^ -1 } - /// Returns `self` with its binary representation shifted by `n` digits to the left. - public fun infix<< (_ n: Int) -> Self { - Int(value: Builtin.shl_word(value, n.value)) - } - /// Returns `self` with its binary representation shifted by `n` digits to the left, masking the /// shift amount to the bit width of `Self`. /// @@ -192,16 +109,6 @@ public type Int { Int(value: Builtin.shl_word(value, n.value)) } - /// Shifts the binary representation of `self` by `n` digits to the left. - public fun infix<<= (_ n: Int) inout { - &self.value = Builtin.shl_word(value, n.value) - } - - /// Returns `self` with its binary representation shifted by `n` digits to the right. - public fun infix>> (_ n: Int) -> Self { - Int(value: Builtin.ashr_word(value, n.value)) - } - /// Returns `self` with its binary representation shifted by `n` digits to the right, masking the /// shift amount to the bit width of `Self`. /// @@ -211,11 +118,6 @@ public type Int { Int(value: Builtin.ashr_word(value, n.value)) } - /// Shifts the binary representation of `self` by `n` digits to the right. - public fun infix>>= (_ n: Int) inout { - &self.value = Builtin.ashr_word(value, n.value) - } - } public conformance Int: ExpressibleByIntegerLiteral {} @@ -266,6 +168,78 @@ public conformance Int: BinaryInteger { CollectionOfOne(UInt(bit_pattern: self)) } + public fun infix/ (_ other: Self) -> Self { + Int(value: Builtin.sdiv_word(value, other.value)) + } + + public fun infix/= (_ other: Self) inout { + &self.value = Builtin.sdiv_word(value, other.value) + } + + public fun infix% (_ other: Self) -> Self { + Int(value: Builtin.srem_word(value, other.value)) + } + + public fun infix%= (_ other: Self) inout { + &self.value = Builtin.srem_word(value, other.value) + } + + public fun infix< (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_slt_word(value, other.value)) + } + + public fun infix<= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_sle_word(value, other.value)) + } + + public fun infix> (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_sgt_word(value, other.value)) + } + + public fun infix>= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_sge_word(value, other.value)) + } + + public fun infix& (_ other: Self) -> Self { + Int(value: Builtin.and_word(value, other.value)) + } + + public fun infix&= (_ other: Self) inout { + &self.value = Builtin.and_word(value, other.value) + } + + public fun infix| (_ other: Self) -> Self { + Int(value: Builtin.or_word(value, other.value)) + } + + public fun infix|= (_ other: Self) inout { + &self.value = Builtin.or_word(value, other.value) + } + + public fun infix^ (_ other: Self) -> Self { + Int(value: Builtin.xor_word(value, other.value)) + } + + public fun infix^= (_ other: Self) inout { + &self.value = Builtin.xor_word(value, other.value) + } + + public fun infix<< (_ n: Int) -> Self { + Int(value: Builtin.shl_word(value, n.value)) + } + + public fun infix<<= (_ n: Int) inout { + &self.value = Builtin.shl_word(value, n.value) + } + + public fun infix>> (_ n: Int) -> Self { + Int(value: Builtin.ashr_word(value, n.value)) + } + + public fun infix>>= (_ n: Int) inout { + &self.value = Builtin.ashr_word(value, n.value) + } + public static fun is_signed() -> Bool { true } diff --git a/Library/Hylo/Core/Integers/UInt.hylo b/Library/Hylo/Core/Integers/UInt.hylo index 8f48ce49d..b42866f45 100644 --- a/Library/Hylo/Core/Integers/UInt.hylo +++ b/Library/Hylo/Core/Integers/UInt.hylo @@ -20,18 +20,6 @@ public type UInt { &self.value = Builtin.ptrtoint_word(address.base) } - /// Returns the quotient of dividing `self` by `other`. - /// - /// - Requires: `other` is different from `0`. - public fun infix/ (_ other: Self) -> Self { - UInt(value: Builtin.udiv_word(value, other.value)) - } - - /// Returns `true` if `self` is greater than `other`. Otherwise, returns `false`. - public fun infix> (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_ugt_word(value, other.value)) - } - /// Returns `true` if `self` is equal to `other`. Otherwise, returns `false`. public fun infix== (_ other: Self) -> Bool { Bool(value: Builtin.icmp_eq_word(value, other.value)) @@ -42,39 +30,27 @@ public type UInt { Bool(value: Builtin.icmp_ne_word(value, other.value)) } - /// Returns the bitwise AND of `self` and `other`. - public fun infix& (_ other: Self) -> Self { - UInt(value: Builtin.and_word(value, other.value)) - } - - /// Writes the bitwise AND of `self` and `other` to `self`. - public fun infix&= (_ other: Self) inout { - &self.value = Builtin.and_word(value, other.value) - } - - /// Returns the bitwise OR of `self` and `other`. - public fun infix| (_ other: Self) -> Self { - UInt(value: Builtin.or_word(value, other.value)) - } - - /// Writes the bitwise OR of `self` and `other` to `self`. - public fun infix|= (_ other: Self) inout { - &self.value = Builtin.or_word(value, other.value) - } - - /// Returns the bitwise XOR of `self` and `other`. - public fun infix^ (_ other: Self) -> Self { - UInt(value: Builtin.xor_word(value, other.value)) + /// Returns the bitwise inverse of `self`. + public fun prefix~ () -> Self { + self ^ UInt(bit_pattern: -1) } - /// Writes the bitwise XOR of `self` and `other` to `self`. - public fun infix^= (_ other: Self) inout { - &self.value = Builtin.xor_word(value, other.value) + /// Returns `self` with its binary representation shifted by `n` digits to the left, masking the + /// shift amount to the bit width of `Self`. + /// + /// Use this operator when you need to perform a shift and are sure that the shift amount is in + /// the range `0 ..< Self.bit_width()`. + public fun infix&<< (_ n: Int) -> Self { + UInt(value: Builtin.shl_word(value, n.value)) } - /// Returns the bitwise inverse of `self`. - public fun prefix~ () -> Self { - self ^ UInt(bit_pattern: -1) + /// Returns `self` with its binary representation shifted by `n` digits to the right, masking the + /// shift amount to the bit width of `Self`. + /// + /// Use this operator when you need to perform a shift and are sure that the shift amount is in + /// the range `0 ..< Self.bit_width()`. + public fun infix&>> (_ n: Int) -> Self { + UInt(value: Builtin.ashr_word(value, n.value)) } } @@ -126,6 +102,78 @@ public conformance UInt: BinaryInteger { CollectionOfOne(self.copy()) } + public fun infix/ (_ other: Self) -> Self { + UInt(value: Builtin.udiv_word(value, other.value)) + } + + public fun infix/= (_ other: Self) inout { + &self.value = Builtin.udiv_word(value, other.value) + } + + public fun infix% (_ other: Self) -> Self { + UInt(value: Builtin.urem_word(value, other.value)) + } + + public fun infix%= (_ other: Self) inout { + &self.value = Builtin.urem_word(value, other.value) + } + + public fun infix< (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ult_word(value, other.value)) + } + + public fun infix<= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ule_word(value, other.value)) + } + + public fun infix> (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ugt_word(value, other.value)) + } + + public fun infix>= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_uge_word(value, other.value)) + } + + public fun infix& (_ other: Self) -> Self { + UInt(value: Builtin.and_word(value, other.value)) + } + + public fun infix&= (_ other: Self) inout { + &self.value = Builtin.and_word(value, other.value) + } + + public fun infix| (_ other: Self) -> Self { + UInt(value: Builtin.or_word(value, other.value)) + } + + public fun infix|= (_ other: Self) inout { + &self.value = Builtin.or_word(value, other.value) + } + + public fun infix^ (_ other: Self) -> Self { + UInt(value: Builtin.xor_word(value, other.value)) + } + + public fun infix^= (_ other: Self) inout { + &self.value = Builtin.xor_word(value, other.value) + } + + public fun infix<< (_ n: Int) -> Self { + UInt(value: Builtin.shl_word(value, n.value)) + } + + public fun infix<<= (_ n: Int) inout { + &self.value = Builtin.shl_word(value, n.value) + } + + public fun infix>> (_ n: Int) -> Self { + UInt(value: Builtin.lshr_word(value, n.value)) + } + + public fun infix>>= (_ n: Int) inout { + &self.value = Builtin.lshr_word(value, n.value) + } + public static fun is_signed() -> Bool { false } From 34a03b8df0f692b0b6f1f449a3ac7ef1b78573ea Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 16:49:34 +0200 Subject: [PATCH 059/157] Fix the implementation of 'CollectionOfOne' to support --no-std --- Library/Hylo/Core/CollectionOfOne.hylo | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Library/Hylo/Core/CollectionOfOne.hylo b/Library/Hylo/Core/CollectionOfOne.hylo index b723f7811..7faced852 100644 --- a/Library/Hylo/Core/CollectionOfOne.hylo +++ b/Library/Hylo/Core/CollectionOfOne.hylo @@ -19,7 +19,8 @@ public type CollectionOfOne: Deinitializable public subscript(_ position: Bool): Element { let { - precondition(position) + // TODO: uncomment when #1046 is implemented + // precondition(position, "index is out of bounds") yield contents } } From 2ff5ee645ad7855b5e4522edb2e90517275319cd Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 16:56:20 +0200 Subject: [PATCH 060/157] Move 'nonzero_bit_count' and 'leading_zeros' to 'FixedWidthInteger' --- Library/Hylo/Core/Integers/BinaryInteger.hylo | 7 ------- .../Hylo/Core/Integers/FixedWidthInteger.hylo | 7 +++++++ Library/Hylo/Core/Integers/Int.hylo | 16 ++++++++-------- Library/Hylo/Core/Integers/UInt.hylo | 16 ++++++++-------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Library/Hylo/Core/Integers/BinaryInteger.hylo b/Library/Hylo/Core/Integers/BinaryInteger.hylo index a25b15be6..035dc8f48 100644 --- a/Library/Hylo/Core/Integers/BinaryInteger.hylo +++ b/Library/Hylo/Core/Integers/BinaryInteger.hylo @@ -16,13 +16,6 @@ public trait BinaryInteger { /// Returns `-1` if `self` is negative, `1` if it is positive, or `0` otherwise. fun signum() -> Int - /// Returns the number of bits equal to `1` in the representation of `self`. - fun nonzero_bit_count() -> Int - - /// Returns the number of bits equal to `0` on the left of the most significant bit equal to - /// `1` in the representation of `self`. - fun leading_zeros() -> Int - /// Returns the number of bits equal to `0` on the right of the least significant bit equal to /// `1` in the representation of `self`. fun trailing_zeros() -> Int diff --git a/Library/Hylo/Core/Integers/FixedWidthInteger.hylo b/Library/Hylo/Core/Integers/FixedWidthInteger.hylo index fbff70984..3c09d58af 100644 --- a/Library/Hylo/Core/Integers/FixedWidthInteger.hylo +++ b/Library/Hylo/Core/Integers/FixedWidthInteger.hylo @@ -26,6 +26,13 @@ public trait FixedWidthInteger: BinaryInteger { dividing_by other: Self ) -> {partial_value: Self, overflow: Bool} + /// Returns the number of bits equal to `1` in the representation of `self`. + fun nonzero_bit_count() -> Int + + /// Returns the number of bits equal to `0` on the left of the most significant bit equal to + /// `1` in the representation of `self`. + fun leading_zeros() -> Int + /// Returns the number of bits in the representation of an instance of this type. static fun bit_width() -> Int diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index 9d8f6c01e..1a99f465c 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -148,14 +148,6 @@ public conformance Int: BinaryInteger { return positive | (self &>> (Self.bit_width() - 1)) } - public fun nonzero_bit_count() -> Int { - Int(value: Builtin.ctpop_word(value)) - } - - public fun leading_zeros() -> Int { - Int(value: Builtin.ctlz_word(value)) - } - public fun trailing_zeros() -> Int { Int(value: Builtin.cttz_word(value)) } @@ -289,6 +281,14 @@ public conformance Int: FixedWidthInteger { return (partial_value: Int(value: Builtin.srem_word(value, other.value)), overflow: false) } + public fun nonzero_bit_count() -> Int { + Int(value: Builtin.ctpop_word(value)) + } + + public fun leading_zeros() -> Int { + Int(value: Builtin.ctlz_word(value)) + } + public static fun bit_width() -> Int { MemoryLayout.size() * 8 } diff --git a/Library/Hylo/Core/Integers/UInt.hylo b/Library/Hylo/Core/Integers/UInt.hylo index b42866f45..51aea4edf 100644 --- a/Library/Hylo/Core/Integers/UInt.hylo +++ b/Library/Hylo/Core/Integers/UInt.hylo @@ -82,14 +82,6 @@ public conformance UInt: BinaryInteger { Int(value: Builtin.zext_i1_word((self > UInt()).value)) } - public fun nonzero_bit_count() -> Int { - Int(value: Builtin.ctpop_word(value)) - } - - public fun leading_zeros() -> Int { - Int(value: Builtin.ctlz_word(value)) - } - public fun trailing_zeros() -> Int { Int(value: Builtin.cttz_word(value)) } @@ -217,6 +209,14 @@ public conformance UInt: FixedWidthInteger { return (partial_value: UInt(value: Builtin.urem_word(value, other.value)), overflow: false) } + public fun nonzero_bit_count() -> Int { + Int(value: Builtin.ctpop_word(value)) + } + + public fun leading_zeros() -> Int { + Int(value: Builtin.ctlz_word(value)) + } + public static fun bit_width() -> Int { MemoryLayout.size() * 8 } From 2a65c57b191df12f78e8516d70e6160e7c660535 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 17:01:26 +0200 Subject: [PATCH 061/157] Define masking shift operators as requirements of 'FixedWidthInteger' --- .../Hylo/Core/Integers/FixedWidthInteger.hylo | 14 ++++++++++ Library/Hylo/Core/Integers/Int.hylo | 26 ++++++------------- Library/Hylo/Core/Integers/UInt.hylo | 26 ++++++------------- 3 files changed, 30 insertions(+), 36 deletions(-) diff --git a/Library/Hylo/Core/Integers/FixedWidthInteger.hylo b/Library/Hylo/Core/Integers/FixedWidthInteger.hylo index 3c09d58af..68024d6f7 100644 --- a/Library/Hylo/Core/Integers/FixedWidthInteger.hylo +++ b/Library/Hylo/Core/Integers/FixedWidthInteger.hylo @@ -33,6 +33,20 @@ public trait FixedWidthInteger: BinaryInteger { /// `1` in the representation of `self`. fun leading_zeros() -> Int + /// Returns `self` with its binary representation shifted by `n` digits to the left, masking the + /// shift amount to the bit width of `Self`. + /// + /// Use this operator when you need to perform a shift and are sure that the shift amount is in + /// the range `0 ..< Self.bit_width()`. + fun infix&<< (_ n: Int) -> Self + + /// Returns `self` with its binary representation shifted by `n` digits to the right, masking the + /// shift amount to the bit width of `Self`. + /// + /// Use this operator when you need to perform a shift and are sure that the shift amount is in + /// the range `0 ..< Self.bit_width()`. + fun infix&>> (_ n: Int) -> Self + /// Returns the number of bits in the representation of an instance of this type. static fun bit_width() -> Int diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index 1a99f465c..76dcfa3aa 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -100,24 +100,6 @@ public type Int { self ^ -1 } - /// Returns `self` with its binary representation shifted by `n` digits to the left, masking the - /// shift amount to the bit width of `Self`. - /// - /// Use this operator when you need to perform a shift and are sure that the shift amount is in - /// the range `0 ..< Self.bit_width()`. - public fun infix&<< (_ n: Int) -> Self { - Int(value: Builtin.shl_word(value, n.value)) - } - - /// Returns `self` with its binary representation shifted by `n` digits to the right, masking the - /// shift amount to the bit width of `Self`. - /// - /// Use this operator when you need to perform a shift and are sure that the shift amount is in - /// the range `0 ..< Self.bit_width()`. - public fun infix&>> (_ n: Int) -> Self { - Int(value: Builtin.ashr_word(value, n.value)) - } - } public conformance Int: ExpressibleByIntegerLiteral {} @@ -289,6 +271,14 @@ public conformance Int: FixedWidthInteger { Int(value: Builtin.ctlz_word(value)) } + public fun infix&<< (_ n: Int) -> Self { + Int(value: Builtin.shl_word(value, n.value)) + } + + public fun infix&>> (_ n: Int) -> Self { + Int(value: Builtin.ashr_word(value, n.value)) + } + public static fun bit_width() -> Int { MemoryLayout.size() * 8 } diff --git a/Library/Hylo/Core/Integers/UInt.hylo b/Library/Hylo/Core/Integers/UInt.hylo index 51aea4edf..202da17ed 100644 --- a/Library/Hylo/Core/Integers/UInt.hylo +++ b/Library/Hylo/Core/Integers/UInt.hylo @@ -35,24 +35,6 @@ public type UInt { self ^ UInt(bit_pattern: -1) } - /// Returns `self` with its binary representation shifted by `n` digits to the left, masking the - /// shift amount to the bit width of `Self`. - /// - /// Use this operator when you need to perform a shift and are sure that the shift amount is in - /// the range `0 ..< Self.bit_width()`. - public fun infix&<< (_ n: Int) -> Self { - UInt(value: Builtin.shl_word(value, n.value)) - } - - /// Returns `self` with its binary representation shifted by `n` digits to the right, masking the - /// shift amount to the bit width of `Self`. - /// - /// Use this operator when you need to perform a shift and are sure that the shift amount is in - /// the range `0 ..< Self.bit_width()`. - public fun infix&>> (_ n: Int) -> Self { - UInt(value: Builtin.ashr_word(value, n.value)) - } - } public conformance UInt: ExpressibleByIntegerLiteral {} @@ -217,6 +199,14 @@ public conformance UInt: FixedWidthInteger { Int(value: Builtin.ctlz_word(value)) } + public fun infix&<< (_ n: Int) -> Self { + UInt(value: Builtin.shl_word(value, n.value)) + } + + public fun infix&>> (_ n: Int) -> Self { + UInt(value: Builtin.ashr_word(value, n.value)) + } + public static fun bit_width() -> Int { MemoryLayout.size() * 8 } From 64dc38fe6043b3447bf2f5f2bbb6d815f02e9ef1 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 17:07:10 +0200 Subject: [PATCH 062/157] Define self-assigning variants of masking shift operators --- Library/Hylo/Core/Integers/FixedWidthInteger.hylo | 8 ++++++++ Library/Hylo/Core/Integers/Int.hylo | 8 ++++++++ Library/Hylo/Core/Integers/UInt.hylo | 10 +++++++++- Library/Hylo/Core/Operators.hylo | 2 ++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Library/Hylo/Core/Integers/FixedWidthInteger.hylo b/Library/Hylo/Core/Integers/FixedWidthInteger.hylo index 68024d6f7..2f8ee8001 100644 --- a/Library/Hylo/Core/Integers/FixedWidthInteger.hylo +++ b/Library/Hylo/Core/Integers/FixedWidthInteger.hylo @@ -40,6 +40,10 @@ public trait FixedWidthInteger: BinaryInteger { /// the range `0 ..< Self.bit_width()`. fun infix&<< (_ n: Int) -> Self + /// Shifts the binary representation of `self` by `n` digits to the left, masking the shift + /// amount to the bit width of `Self`. + fun infix&<<= (_ n: Int) inout + /// Returns `self` with its binary representation shifted by `n` digits to the right, masking the /// shift amount to the bit width of `Self`. /// @@ -47,6 +51,10 @@ public trait FixedWidthInteger: BinaryInteger { /// the range `0 ..< Self.bit_width()`. fun infix&>> (_ n: Int) -> Self + /// Shifts the binary representation of `self` by `n` digits to the right, masking the shift + /// amount to the bit width of `Self`. + fun infix&>>= (_ n: Int) inout + /// Returns the number of bits in the representation of an instance of this type. static fun bit_width() -> Int diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index 76dcfa3aa..a95a5f0e6 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -275,10 +275,18 @@ public conformance Int: FixedWidthInteger { Int(value: Builtin.shl_word(value, n.value)) } + public fun infix&<<= (_ n: Int) inout { + &self.value = Builtin.shl_word(value, n.value) + } + public fun infix&>> (_ n: Int) -> Self { Int(value: Builtin.ashr_word(value, n.value)) } + public fun infix&>>= (_ n: Int) inout { + &self.value = Builtin.ashr_word(value, n.value) + } + public static fun bit_width() -> Int { MemoryLayout.size() * 8 } diff --git a/Library/Hylo/Core/Integers/UInt.hylo b/Library/Hylo/Core/Integers/UInt.hylo index 202da17ed..0952c1d9c 100644 --- a/Library/Hylo/Core/Integers/UInt.hylo +++ b/Library/Hylo/Core/Integers/UInt.hylo @@ -203,8 +203,16 @@ public conformance UInt: FixedWidthInteger { UInt(value: Builtin.shl_word(value, n.value)) } + public fun infix&<<= (_ n: Int) inout { + &self.value = Builtin.shl_word(value, n.value) + } + public fun infix&>> (_ n: Int) -> Self { - UInt(value: Builtin.ashr_word(value, n.value)) + UInt(value: Builtin.lshr_word(value, n.value)) + } + + public fun infix&>>= (_ n: Int) inout { + &self.value = Builtin.lshr_word(value, n.value) } public static fun bit_width() -> Int { diff --git a/Library/Hylo/Core/Operators.hylo b/Library/Hylo/Core/Operators.hylo index 557069367..da4a99b41 100644 --- a/Library/Hylo/Core/Operators.hylo +++ b/Library/Hylo/Core/Operators.hylo @@ -33,7 +33,9 @@ public operator infix| : disjunction public operator infix|| : disjunction public operator infix<<= : assignment +public operator infix&<<= : assignment public operator infix>>= : assignment +public operator infix&>>= : assignment public operator infix*= : assignment public operator infix/= : assignment public operator infix%= : assignment From 26863f00ac935987c3e88e645cda8bd26fc9b3d6 Mon Sep 17 00:00:00 2001 From: Nils Hjelte Date: Thu, 28 Sep 2023 17:07:25 +0200 Subject: [PATCH 063/157] Whitespace Co-authored-by: Dimi Racordon --- Sources/Core/AST/Decl/TraitDecl.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Sources/Core/AST/Decl/TraitDecl.swift b/Sources/Core/AST/Decl/TraitDecl.swift index 2f53eff95..2e169d237 100644 --- a/Sources/Core/AST/Decl/TraitDecl.swift +++ b/Sources/Core/AST/Decl/TraitDecl.swift @@ -9,8 +9,6 @@ public struct TraitDecl: ExposableDecl, SingleEntityDecl, LexicalScope { /// The site of the `trait` introducer. public let introducerSite: SourceRange - - /// The access modifier of the declaration, if any. public let accessModifier: SourceRepresentable From ea8c83cabc3692bf9cf17253a55691eb0acf7259 Mon Sep 17 00:00:00 2001 From: Nils Hjelte Date: Thu, 28 Sep 2023 17:08:08 +0200 Subject: [PATCH 064/157] More concise doc comment for Introduced Co-authored-by: Dimi Racordon --- Sources/Core/AST/Introduced.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Sources/Core/AST/Introduced.swift b/Sources/Core/AST/Introduced.swift index c8481515e..69b086ff1 100644 --- a/Sources/Core/AST/Introduced.swift +++ b/Sources/Core/AST/Introduced.swift @@ -1,6 +1,4 @@ -// Child node with introducer keyword, eg else clause in if-else -// The NodeIDProtocol conformance is used for BundledNode compatibility -// eg DoWhileStmt in Emitter.swift: `program[s].condition.site`` +/// A node and the site of its introducer in program sources. public struct Introduced: NodeIDProtocol { public let introducerSite: SourceRange public let value: T From 378f199e7f5cfca8a65397e13cb25b6b65b70b4e Mon Sep 17 00:00:00 2001 From: Nils Hjelte Date: Thu, 28 Sep 2023 17:11:34 +0200 Subject: [PATCH 065/157] Remove NodeIDProtocol conformance from Introduced --- Sources/Core/AST/Introduced.swift | 13 +------------ Sources/IR/Emitter.swift | 6 ++++-- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/Sources/Core/AST/Introduced.swift b/Sources/Core/AST/Introduced.swift index 69b086ff1..6e54ff35d 100644 --- a/Sources/Core/AST/Introduced.swift +++ b/Sources/Core/AST/Introduced.swift @@ -1,19 +1,8 @@ /// A node and the site of its introducer in program sources. -public struct Introduced: NodeIDProtocol { +public struct Introduced: Codable { public let introducerSite: SourceRange public let value: T - public var rawValue: NodeID.RawValue { value.rawValue } - public var kind: NodeKind { value.kind } - - public init?(_ x: Other) { - if let i = x as? Introduced { - self = i - } else { - return nil - } - } - public init(introducerSite: SourceRange, value: T) { self.introducerSite = introducerSite self.value = value diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 331334baa..1c1266409 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -988,12 +988,14 @@ struct Emitter { } } - let c = emit(branchCondition: ast[s].condition.value) + let condition = ast[s].condition.value + let c = emit(branchCondition: condition) emitDeallocTopFrame(at: ast[s].site) frames.pop() insert( - module.makeCondBranch(if: c, then: loopBody, else: loopTail, at: program[s].condition.site)) + module.makeCondBranch(if: c, then: loopBody, else: loopTail, at: ast[condition].site +)) insertionPoint = .end(of: loopTail) return .next } From 1c776fba06eb84129b1a701553ec894b675bf289 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 17:12:49 +0200 Subject: [PATCH 066/157] Add 'Numeric' to the standard library --- Library/Hylo/Core/Numeric.hylo | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Library/Hylo/Core/Numeric.hylo diff --git a/Library/Hylo/Core/Numeric.hylo b/Library/Hylo/Core/Numeric.hylo new file mode 100644 index 000000000..b139a4044 --- /dev/null +++ b/Library/Hylo/Core/Numeric.hylo @@ -0,0 +1,16 @@ +/// A type whose instances support multiplication. +public trait Numeric { + + /// A type that can represent the absolute value of any instance of `Self`. + type Magnitude: Comparable//, Numeric + + /// Returns the magnitude of `self`. + fun magnitude() -> Magnitude + + /// Returns the product of `self` and `other` + fun infix* (_ other: Self) -> Self + + /// Writes the product of `self` and `other` to `self`. + fun infix*= (_ other: Self) inout + +} \ No newline at end of file From b7f8c7db6ee615a7667bccaa92f616e8f777317f Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 17:13:11 +0200 Subject: [PATCH 067/157] Make 'Int' conform to 'Numeric' --- Library/Hylo/Core/Integers/Int.hylo | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index a95a5f0e6..9dc3bf3e6 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -65,16 +65,6 @@ public type Int { &self.value = Builtin.sub_word(value, other.value) } - /// Returns the product of `self` and `other` - public fun infix* (_ other: Self) -> Self { - Int(value: Builtin.mul_word(value, other.value)) - } - - /// Writes the product of `self` and `other` to `self`. - public fun infix*= (_ other: Self) inout { - &self.value = Builtin.mul_word(value, other.value) - } - /// Returns the additive inverse of `self`. public fun prefix- () -> Self { Int() - self @@ -114,6 +104,24 @@ public conformance Int: Copyable { } +public conformance Int: Numeric { + + public typealias Magnitude = UInt + + public fun magnitude() -> UInt { + UInt(bit_pattern: self) + } + + public fun infix* (_ other: Self) -> Self { + Int(value: Builtin.mul_word(value, other.value)) + } + + public fun infix*= (_ other: Self) inout { + &self.value = Builtin.mul_word(value, other.value) + } + +} + public conformance Int: BinaryInteger { public init(truncating_or_extending source: T) { From ef3de0a56e9a1c592ddebfc09a48761030a89263 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 17:27:43 +0200 Subject: [PATCH 068/157] Document issues blocking the conformance of 'UInt' to 'Numeric' --- Library/Hylo/Core/Integers/BinaryInteger.hylo | 2 ++ Library/Hylo/Core/Integers/UInt.hylo | 19 +++++++++++++++++++ Library/Hylo/Core/Numeric.hylo | 1 + 3 files changed, 22 insertions(+) diff --git a/Library/Hylo/Core/Integers/BinaryInteger.hylo b/Library/Hylo/Core/Integers/BinaryInteger.hylo index 035dc8f48..789697df6 100644 --- a/Library/Hylo/Core/Integers/BinaryInteger.hylo +++ b/Library/Hylo/Core/Integers/BinaryInteger.hylo @@ -1,3 +1,5 @@ +// TODO: Make `BinaryInteger` a refinement of `Numeric` when #1047 is fixed + /// An integer type with a binary representation. public trait BinaryInteger { diff --git a/Library/Hylo/Core/Integers/UInt.hylo b/Library/Hylo/Core/Integers/UInt.hylo index 0952c1d9c..c7b3d8b03 100644 --- a/Library/Hylo/Core/Integers/UInt.hylo +++ b/Library/Hylo/Core/Integers/UInt.hylo @@ -49,6 +49,25 @@ public conformance UInt: Copyable { } +// TODO: Rewrite as a conformance to `Numeric` when #1047 is fixed +public extension UInt { + +// public typealias Magnitude = UInt + + public fun magnitude() -> UInt { + self.copy() + } + + public fun infix* (_ other: Self) -> Self { + UInt(value: Builtin.mul_word(value, other.value)) + } + + public fun infix*= (_ other: Self) inout { + &self.value = Builtin.mul_word(value, other.value) + } + +} + public conformance UInt: BinaryInteger { public init(truncating_or_extending source: T) { diff --git a/Library/Hylo/Core/Numeric.hylo b/Library/Hylo/Core/Numeric.hylo index b139a4044..5d986bd69 100644 --- a/Library/Hylo/Core/Numeric.hylo +++ b/Library/Hylo/Core/Numeric.hylo @@ -1,6 +1,7 @@ /// A type whose instances support multiplication. public trait Numeric { + // TODO: Uncomment the constraint to `Numeric` when #1047 is fixed /// A type that can represent the absolute value of any instance of `Self`. type Magnitude: Comparable//, Numeric From 2035ecd6fa6bb5b69100e23b4722220bdb14476f Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 17:28:27 +0200 Subject: [PATCH 069/157] Make 'Numeric' a refinement of 'Equatable' --- Library/Hylo/Core/Numeric.hylo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Hylo/Core/Numeric.hylo b/Library/Hylo/Core/Numeric.hylo index 5d986bd69..2dc07c8da 100644 --- a/Library/Hylo/Core/Numeric.hylo +++ b/Library/Hylo/Core/Numeric.hylo @@ -1,5 +1,5 @@ /// A type whose instances support multiplication. -public trait Numeric { +public trait Numeric: Equatable { // TODO: Uncomment the constraint to `Numeric` when #1047 is fixed /// A type that can represent the absolute value of any instance of `Self`. From ee80bba369ff6eb25d832abf7eb8386e693476bb Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 17:32:47 +0200 Subject: [PATCH 070/157] Add 'AdditiveArithmetic' to the standard library --- Library/Hylo/Core/AdditiveArithmetic.hylo | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Library/Hylo/Core/AdditiveArithmetic.hylo diff --git a/Library/Hylo/Core/AdditiveArithmetic.hylo b/Library/Hylo/Core/AdditiveArithmetic.hylo new file mode 100644 index 000000000..628fc4862 --- /dev/null +++ b/Library/Hylo/Core/AdditiveArithmetic.hylo @@ -0,0 +1,23 @@ +/// A type whose instances support addition and subtraction. +public trait AdditiveArithmetic: Equatable { + + /// Returns the sum of `self` and `other`. + /// + /// - Requires: The sum of the two arguments is representable in `Self`. + fun infix+ (_ other: Self) -> Self + + /// Writes the sum of `self` and `other` to `self`. + /// + /// - Requires: The sum of the two arguments is representable in `Self`. + fun infix+= (_ other: Self) inout + + /// Returns `self` subtracted by `other`. + fun infix- (_ other: Self) -> Self + + /// Writes the result of `self` subtracted by `other` to `self`. + fun infix-= (_ other: Self) inout + + /// Returns the value `0` represented in `Self`. + static fun zero() -> Self + +} From 83ebc5a0b45a19e9d5c49aad408ed744b9bf0855 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 17:35:04 +0200 Subject: [PATCH 071/157] Make 'Int' conform to 'AdditiveArithmetic' --- Library/Hylo/Core/Integers/Int.hylo | 48 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index 9dc3bf3e6..fd650c3e9 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -41,30 +41,6 @@ public type Int { return self + stride - r; } - /// Returns the sum of `self` and `other`. - /// - /// - Requires: The sum of the two arguments is representable in `Self`. - public fun infix+ (_ other: Self) -> Self { - Int(value: Builtin.add_word(value, other.value)) - } - - /// Writes the sum of `self` and `other` to `self`. - /// - /// - Requires: The sum of the two arguments is representable in `Self`. - public fun infix+= (_ other: Self) inout { - &self.value = Builtin.add_word(value, other.value) - } - - /// Returns `self` subtracted by `other`. - public fun infix- (_ other: Self) -> Self { - Int(value: Builtin.sub_word(value, other.value)) - } - - /// Writes the result of `self` subtracted by `other` to `self`. - public fun infix-= (_ other: Self) inout { - &self.value = Builtin.sub_word(value, other.value) - } - /// Returns the additive inverse of `self`. public fun prefix- () -> Self { Int() - self @@ -104,6 +80,30 @@ public conformance Int: Copyable { } +public conformance Int: AdditiveArithmetic { + + public fun infix+ (_ other: Self) -> Self { + Int(value: Builtin.add_word(value, other.value)) + } + + public fun infix+= (_ other: Self) inout { + &self.value = Builtin.add_word(value, other.value) + } + + public fun infix- (_ other: Self) -> Self { + Int(value: Builtin.sub_word(value, other.value)) + } + + public fun infix-= (_ other: Self) inout { + &self.value = Builtin.sub_word(value, other.value) + } + + public static fun zero() -> Self { + Int() + } + +} + public conformance Int: Numeric { public typealias Magnitude = UInt From e54c5d61e16f2905b59a11a16f9308552d5fb43c Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 17:36:19 +0200 Subject: [PATCH 072/157] Make 'UInt' conform to 'AdditiveArithmetic' --- Library/Hylo/Core/Integers/UInt.hylo | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Library/Hylo/Core/Integers/UInt.hylo b/Library/Hylo/Core/Integers/UInt.hylo index c7b3d8b03..2a5a0fa02 100644 --- a/Library/Hylo/Core/Integers/UInt.hylo +++ b/Library/Hylo/Core/Integers/UInt.hylo @@ -49,6 +49,30 @@ public conformance UInt: Copyable { } +public conformance UInt: AdditiveArithmetic { + + public fun infix+ (_ other: Self) -> Self { + UInt(value: Builtin.add_word(value, other.value)) + } + + public fun infix+= (_ other: Self) inout { + &self.value = Builtin.add_word(value, other.value) + } + + public fun infix- (_ other: Self) -> Self { + UInt(value: Builtin.sub_word(value, other.value)) + } + + public fun infix-= (_ other: Self) inout { + &self.value = Builtin.sub_word(value, other.value) + } + + public static fun zero() -> Self { + UInt() + } + +} + // TODO: Rewrite as a conformance to `Numeric` when #1047 is fixed public extension UInt { From afb28958dea87b581d410d755c16cdd1236629b5 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 17:37:18 +0200 Subject: [PATCH 073/157] Make 'Numeric' a refinement of 'AdditiveArithmetic' --- Library/Hylo/Core/Numeric.hylo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Hylo/Core/Numeric.hylo b/Library/Hylo/Core/Numeric.hylo index 2dc07c8da..c26161878 100644 --- a/Library/Hylo/Core/Numeric.hylo +++ b/Library/Hylo/Core/Numeric.hylo @@ -1,5 +1,5 @@ /// A type whose instances support multiplication. -public trait Numeric: Equatable { +public trait Numeric: AdditiveArithmetic { // TODO: Uncomment the constraint to `Numeric` when #1047 is fixed /// A type that can represent the absolute value of any instance of `Self`. From 58658e5396334938925c39e183da04d5d566a808 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 17:40:24 +0200 Subject: [PATCH 074/157] Add 'SignedNumeric' to the standard library --- Library/Hylo/Core/SignedNumeric.hylo | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Library/Hylo/Core/SignedNumeric.hylo diff --git a/Library/Hylo/Core/SignedNumeric.hylo b/Library/Hylo/Core/SignedNumeric.hylo new file mode 100644 index 000000000..83bc51a87 --- /dev/null +++ b/Library/Hylo/Core/SignedNumeric.hylo @@ -0,0 +1,10 @@ +/// A numeric type with an additive inverse. +public trait SignedNumeric: Numeric { + + /// Returns the additive inverse of `self`. + fun prefix- () -> Self + + /// Replaces `self` with its additive inverse. + fun negate() inout + +} \ No newline at end of file From 308f3a7f1c3246b0b60efbed2111b17a98a5cd1e Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 17:41:56 +0200 Subject: [PATCH 075/157] Make 'Int' conform to 'SignedNumeric' --- Library/Hylo/Core/Integers/Int.hylo | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index fd650c3e9..19b462d67 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -41,11 +41,6 @@ public type Int { return self + stride - r; } - /// Returns the additive inverse of `self`. - public fun prefix- () -> Self { - Int() - self - } - /// Returns `self`. public fun prefix+ () -> Self { self.copy() @@ -122,6 +117,18 @@ public conformance Int: Numeric { } +public conformance Int: SignedNumeric { + + public fun prefix- () -> Self { + Int() - self + } + + public fun negate() inout { + &self = -self + } + +} + public conformance Int: BinaryInteger { public init(truncating_or_extending source: T) { From b056ff38dd88a8910178fb2c79414cd9aa65a615 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 17:58:26 +0200 Subject: [PATCH 076/157] Reorganize declarations by conformance --- Library/Hylo/Core/Integers/Int.hylo | 69 +++++++++++++++------------- Library/Hylo/Core/Integers/UInt.hylo | 67 ++++++++++++++------------- 2 files changed, 72 insertions(+), 64 deletions(-) diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Integers/Int.hylo index 19b462d67..e05b27dd1 100644 --- a/Library/Hylo/Core/Integers/Int.hylo +++ b/Library/Hylo/Core/Integers/Int.hylo @@ -5,11 +5,6 @@ public type Int { memberwise init - /// Creates an instance with value `0`. - public init() { - &self.value = Builtin.zeroinitializer_word() - } - /// Creates an instance with the same memory representation as `other`. public init(bit_pattern other: UInt) { &self.value = other.value @@ -46,16 +41,6 @@ public type Int { self.copy() } - /// Returns `true` if `self` is equal to `other`. Otherwise, returns `false`. - public fun infix== (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_eq_word(value, other.value)) - } - - /// Returns `true` if `self` is not equal to `other`. Otherwise, returns `false`. - public fun infix!= (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_ne_word(value, other.value)) - } - /// Returns the bitwise inverse of `self`. public fun prefix~ () -> Self { self ^ -1 @@ -75,6 +60,38 @@ public conformance Int: Copyable { } +public conformance Int: Equatable { + + public fun infix== (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_eq_word(value, other.value)) + } + + public fun infix!= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ne_word(value, other.value)) + } + +} + +public conformance Int: Comparable { + + public fun infix< (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_slt_word(value, other.value)) + } + + public fun infix<= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_sle_word(value, other.value)) + } + + public fun infix> (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_sgt_word(value, other.value)) + } + + public fun infix>= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_sge_word(value, other.value)) + } + +} + public conformance Int: AdditiveArithmetic { public fun infix+ (_ other: Self) -> Self { @@ -131,6 +148,10 @@ public conformance Int: SignedNumeric { public conformance Int: BinaryInteger { + public init() { + &self.value = Builtin.zeroinitializer_word() + } + public init(truncating_or_extending source: T) { let w = source.words() &self.value = w[w.start_index()].value @@ -173,22 +194,6 @@ public conformance Int: BinaryInteger { &self.value = Builtin.srem_word(value, other.value) } - public fun infix< (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_slt_word(value, other.value)) - } - - public fun infix<= (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_sle_word(value, other.value)) - } - - public fun infix> (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_sgt_word(value, other.value)) - } - - public fun infix>= (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_sge_word(value, other.value)) - } - public fun infix& (_ other: Self) -> Self { Int(value: Builtin.and_word(value, other.value)) } @@ -316,8 +321,6 @@ public conformance Int: FixedWidthInteger { } -public conformance Int: Comparable {} - public conformance Int: ForeignConvertible { public typealias ForeignRepresentation = Builtin.word diff --git a/Library/Hylo/Core/Integers/UInt.hylo b/Library/Hylo/Core/Integers/UInt.hylo index 2a5a0fa02..754fed567 100644 --- a/Library/Hylo/Core/Integers/UInt.hylo +++ b/Library/Hylo/Core/Integers/UInt.hylo @@ -5,11 +5,6 @@ public type UInt { memberwise init - /// Creates an instance with value `0`. - public init() { - &self.value = Builtin.zeroinitializer_word() - } - /// Creates an instance with the same memory representation as `other`. public init(bit_pattern other: Int) { &self.value = other.value @@ -20,16 +15,6 @@ public type UInt { &self.value = Builtin.ptrtoint_word(address.base) } - /// Returns `true` if `self` is equal to `other`. Otherwise, returns `false`. - public fun infix== (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_eq_word(value, other.value)) - } - - /// Returns `true` if `self` is not equal to `other`. Otherwise, returns `false`. - public fun infix!= (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_ne_word(value, other.value)) - } - /// Returns the bitwise inverse of `self`. public fun prefix~ () -> Self { self ^ UInt(bit_pattern: -1) @@ -49,6 +34,38 @@ public conformance UInt: Copyable { } +public conformance UInt: Equatable { + + public fun infix== (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_eq_word(value, other.value)) + } + + public fun infix!= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ne_word(value, other.value)) + } + +} + +public conformance UInt: Comparable { + + public fun infix< (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ult_word(value, other.value)) + } + + public fun infix<= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ule_word(value, other.value)) + } + + public fun infix> (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ugt_word(value, other.value)) + } + + public fun infix>= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_uge_word(value, other.value)) + } + +} + public conformance UInt: AdditiveArithmetic { public fun infix+ (_ other: Self) -> Self { @@ -94,6 +111,10 @@ public extension UInt { public conformance UInt: BinaryInteger { + public init() { + &self.value = Builtin.zeroinitializer_word() + } + public init(truncating_or_extending source: T) { let w = source.words() &self.value = w[w.start_index()].value @@ -135,22 +156,6 @@ public conformance UInt: BinaryInteger { &self.value = Builtin.urem_word(value, other.value) } - public fun infix< (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_ult_word(value, other.value)) - } - - public fun infix<= (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_ule_word(value, other.value)) - } - - public fun infix> (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_ugt_word(value, other.value)) - } - - public fun infix>= (_ other: Self) -> Bool { - Bool(value: Builtin.icmp_uge_word(value, other.value)) - } - public fun infix& (_ other: Self) -> Self { UInt(value: Builtin.and_word(value, other.value)) } From 244b8b841dbba55f34378260b0fdaaac77fee04d Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 18:03:03 +0200 Subject: [PATCH 077/157] Make 'BinaryInteger' a refinement of 'Comparable' --- Library/Hylo/Core/Integers/BinaryInteger.hylo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Hylo/Core/Integers/BinaryInteger.hylo b/Library/Hylo/Core/Integers/BinaryInteger.hylo index 789697df6..ffe6818f2 100644 --- a/Library/Hylo/Core/Integers/BinaryInteger.hylo +++ b/Library/Hylo/Core/Integers/BinaryInteger.hylo @@ -1,7 +1,7 @@ // TODO: Make `BinaryInteger` a refinement of `Numeric` when #1047 is fixed /// An integer type with a binary representation. -public trait BinaryInteger { +public trait BinaryInteger: Comparable { /// Creates an instance with value `0`. init() From 494b46ec98e1b86132787925c7715f2196921c44 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 18:14:49 +0200 Subject: [PATCH 078/157] Move sources related to numbers to a dedicated directory --- Library/Hylo/Core/{ => Numbers}/AdditiveArithmetic.hylo | 0 Library/Hylo/Core/{ => Numbers}/ExpressibleByFloatLiteral.hylo | 0 Library/Hylo/Core/{ => Numbers}/ExpressibleByIntegerLiteral.hylo | 0 Library/Hylo/Core/{ => Numbers/Floats}/Float32.hylo | 0 Library/Hylo/Core/{ => Numbers/Floats}/Float64.hylo | 0 Library/Hylo/Core/{ => Numbers}/Integers/BinaryInteger.hylo | 0 Library/Hylo/Core/{ => Numbers}/Integers/FixedWidthInteger.hylo | 0 Library/Hylo/Core/{ => Numbers}/Integers/Int.hylo | 0 Library/Hylo/Core/{ => Numbers}/Integers/Int32.hylo | 0 Library/Hylo/Core/{ => Numbers}/Integers/Int8.hylo | 0 Library/Hylo/Core/{ => Numbers}/Integers/UInt.hylo | 0 Library/Hylo/Core/{ => Numbers}/Numeric.hylo | 0 Library/Hylo/Core/{ => Numbers}/SignedNumeric.hylo | 0 13 files changed, 0 insertions(+), 0 deletions(-) rename Library/Hylo/Core/{ => Numbers}/AdditiveArithmetic.hylo (100%) rename Library/Hylo/Core/{ => Numbers}/ExpressibleByFloatLiteral.hylo (100%) rename Library/Hylo/Core/{ => Numbers}/ExpressibleByIntegerLiteral.hylo (100%) rename Library/Hylo/Core/{ => Numbers/Floats}/Float32.hylo (100%) rename Library/Hylo/Core/{ => Numbers/Floats}/Float64.hylo (100%) rename Library/Hylo/Core/{ => Numbers}/Integers/BinaryInteger.hylo (100%) rename Library/Hylo/Core/{ => Numbers}/Integers/FixedWidthInteger.hylo (100%) rename Library/Hylo/Core/{ => Numbers}/Integers/Int.hylo (100%) rename Library/Hylo/Core/{ => Numbers}/Integers/Int32.hylo (100%) rename Library/Hylo/Core/{ => Numbers}/Integers/Int8.hylo (100%) rename Library/Hylo/Core/{ => Numbers}/Integers/UInt.hylo (100%) rename Library/Hylo/Core/{ => Numbers}/Numeric.hylo (100%) rename Library/Hylo/Core/{ => Numbers}/SignedNumeric.hylo (100%) diff --git a/Library/Hylo/Core/AdditiveArithmetic.hylo b/Library/Hylo/Core/Numbers/AdditiveArithmetic.hylo similarity index 100% rename from Library/Hylo/Core/AdditiveArithmetic.hylo rename to Library/Hylo/Core/Numbers/AdditiveArithmetic.hylo diff --git a/Library/Hylo/Core/ExpressibleByFloatLiteral.hylo b/Library/Hylo/Core/Numbers/ExpressibleByFloatLiteral.hylo similarity index 100% rename from Library/Hylo/Core/ExpressibleByFloatLiteral.hylo rename to Library/Hylo/Core/Numbers/ExpressibleByFloatLiteral.hylo diff --git a/Library/Hylo/Core/ExpressibleByIntegerLiteral.hylo b/Library/Hylo/Core/Numbers/ExpressibleByIntegerLiteral.hylo similarity index 100% rename from Library/Hylo/Core/ExpressibleByIntegerLiteral.hylo rename to Library/Hylo/Core/Numbers/ExpressibleByIntegerLiteral.hylo diff --git a/Library/Hylo/Core/Float32.hylo b/Library/Hylo/Core/Numbers/Floats/Float32.hylo similarity index 100% rename from Library/Hylo/Core/Float32.hylo rename to Library/Hylo/Core/Numbers/Floats/Float32.hylo diff --git a/Library/Hylo/Core/Float64.hylo b/Library/Hylo/Core/Numbers/Floats/Float64.hylo similarity index 100% rename from Library/Hylo/Core/Float64.hylo rename to Library/Hylo/Core/Numbers/Floats/Float64.hylo diff --git a/Library/Hylo/Core/Integers/BinaryInteger.hylo b/Library/Hylo/Core/Numbers/Integers/BinaryInteger.hylo similarity index 100% rename from Library/Hylo/Core/Integers/BinaryInteger.hylo rename to Library/Hylo/Core/Numbers/Integers/BinaryInteger.hylo diff --git a/Library/Hylo/Core/Integers/FixedWidthInteger.hylo b/Library/Hylo/Core/Numbers/Integers/FixedWidthInteger.hylo similarity index 100% rename from Library/Hylo/Core/Integers/FixedWidthInteger.hylo rename to Library/Hylo/Core/Numbers/Integers/FixedWidthInteger.hylo diff --git a/Library/Hylo/Core/Integers/Int.hylo b/Library/Hylo/Core/Numbers/Integers/Int.hylo similarity index 100% rename from Library/Hylo/Core/Integers/Int.hylo rename to Library/Hylo/Core/Numbers/Integers/Int.hylo diff --git a/Library/Hylo/Core/Integers/Int32.hylo b/Library/Hylo/Core/Numbers/Integers/Int32.hylo similarity index 100% rename from Library/Hylo/Core/Integers/Int32.hylo rename to Library/Hylo/Core/Numbers/Integers/Int32.hylo diff --git a/Library/Hylo/Core/Integers/Int8.hylo b/Library/Hylo/Core/Numbers/Integers/Int8.hylo similarity index 100% rename from Library/Hylo/Core/Integers/Int8.hylo rename to Library/Hylo/Core/Numbers/Integers/Int8.hylo diff --git a/Library/Hylo/Core/Integers/UInt.hylo b/Library/Hylo/Core/Numbers/Integers/UInt.hylo similarity index 100% rename from Library/Hylo/Core/Integers/UInt.hylo rename to Library/Hylo/Core/Numbers/Integers/UInt.hylo diff --git a/Library/Hylo/Core/Numeric.hylo b/Library/Hylo/Core/Numbers/Numeric.hylo similarity index 100% rename from Library/Hylo/Core/Numeric.hylo rename to Library/Hylo/Core/Numbers/Numeric.hylo diff --git a/Library/Hylo/Core/SignedNumeric.hylo b/Library/Hylo/Core/Numbers/SignedNumeric.hylo similarity index 100% rename from Library/Hylo/Core/SignedNumeric.hylo rename to Library/Hylo/Core/Numbers/SignedNumeric.hylo From 2e4937a0f3fec48130853e406ef4ed7388834067 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 28 Sep 2023 19:10:50 +0200 Subject: [PATCH 079/157] Improve docs --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 864374761..353650dd4 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -3670,18 +3670,18 @@ struct TypeChecker { switch program[e].direction { case .down: - // Note: constraining the type of the left operand to be above the right operand wouldn't - // contribute any useful information to the constraint system. + // Note: constraining the type of the LHS to be above the RHS wouldn't contribute any useful + // information to the constraint system. _ = inferredType(of: program[e].left, updating: &obligations) case .up: - // The type of the left operand must be statically known to subtype of the right operand. + // The type of thr LHS must be statically known to subtype of the RHS. let lhs = inferredType( of: program[e].left, withHint: ^freshVariable(), updating: &obligations) obligations.insert(SubtypingConstraint(lhs, rhs.shape, origin: cause)) case .pointerConversion: - // The left operand must be a `Builtin.ptr`. The right operand must be a remote type. + // The LHS be a `Builtin.ptr`. The RHS must be a remote type. if !(rhs.shape.base is RemoteType) { report(.error(invalidPointerConversionAt: program[e].right.site)) return constrain(e, to: .error, in: &obligations) From 58b827976808748ce92f51396bc3fef985c51ad2 Mon Sep 17 00:00:00 2001 From: Nils Hjelte Date: Mon, 2 Oct 2023 10:52:02 +0200 Subject: [PATCH 080/157] Document the Introduced struct Co-authored-by: Dimi Racordon --- Sources/Core/AST/Introduced.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Sources/Core/AST/Introduced.swift b/Sources/Core/AST/Introduced.swift index 6e54ff35d..fea9eb9fc 100644 --- a/Sources/Core/AST/Introduced.swift +++ b/Sources/Core/AST/Introduced.swift @@ -1,10 +1,16 @@ /// A node and the site of its introducer in program sources. public struct Introduced: Codable { + + /// The site of `value`'s introducer. public let introducerSite: SourceRange + + /// A node introduced in program sources at `introducerSite`. public let value: T - public init(introducerSite: SourceRange, value: T) { + /// Creates an instance bundling `n` with the site from which its introducer was parsed. + public init(_ n: T, at introducerSite: SourceRange) { self.introducerSite = introducerSite - self.value = value + self.value = n } + } From c0e48297278bd4a695ad51ca48a7b7d2f9b4492d Mon Sep 17 00:00:00 2001 From: Nils Hjelte Date: Mon, 2 Oct 2023 12:16:15 +0200 Subject: [PATCH 081/157] Fix usage of refactored constructor --- Sources/FrontEnd/Parse/Parser.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/FrontEnd/Parse/Parser.swift b/Sources/FrontEnd/Parse/Parser.swift index efb6d13c1..1309c8eda 100644 --- a/Sources/FrontEnd/Parse/Parser.swift +++ b/Sources/FrontEnd/Parse/Parser.swift @@ -1990,7 +1990,7 @@ public enum Parser { try parseConditionalExpr(in: &s).map(AnyExprID.init(_:)) ?? parseBracedExpr(in: &s) }) - let elseClause = Introduced(introducerSite: elseIntroducer.site, value: b) + let elseClause = Introduced(b, at: elseIntroducer.site) return state.insert( ConditionalExpr( @@ -2740,11 +2740,11 @@ public enum Parser { if let s = try parseConditionalStmt(in: &state) { let s = AnyStmtID(s) - return Introduced(introducerSite: introducer.site, value: s) + return Introduced(s, at: introducer.site) } else { let s = AnyStmtID(try state.expect("'{'", using: braceStmt)) - return Introduced(introducerSite: introducer.site, value: s) + return Introduced(s, at: introducer.site) } } @@ -2771,7 +2771,7 @@ public enum Parser { state.insert( DoWhileStmt( introducerSite: tree.0.0.0.site, - body: tree.0.0.1, condition: Introduced(introducerSite: tree.0.1.site, value: tree.1), + body: tree.0.0.1, condition: Introduced(tree.1, at: tree.0.1.site), site: tree.0.0.0.site.extended(upTo: state.currentIndex))) })) @@ -2802,9 +2802,9 @@ public enum Parser { site: tree.0.0.0.0.site.extended(upTo: state.currentIndex))) })) - static let forSite = (take(.in).and(expr).map({ (state, tree) in Introduced(introducerSite: tree.0.site, value: tree.1)})) + static let forSite = (take(.in).and(expr).map({ (state, tree) in Introduced(tree.1, at: tree.0.site)})) - static let forFilter = (take(.where).and(expr).map({ (state, tree) in Introduced(introducerSite: tree.0.site, value: tree.1)})) + static let forFilter = (take(.where).and(expr).map({ (state, tree) in Introduced(tree.1, at: tree.0.site)})) static let loopBody = inContext(.loopBody, apply: braceStmt) From 3dda8b3f857d4408fbb45c096b9b7696f607cbc4 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Mon, 2 Oct 2023 18:35:10 +0200 Subject: [PATCH 082/157] Fix 'Program.isGlobal' for variable declarations --- Sources/Core/Program.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/Core/Program.swift b/Sources/Core/Program.swift index a4d2e169c..8a2d9c042 100644 --- a/Sources/Core/Program.swift +++ b/Sources/Core/Program.swift @@ -156,6 +156,8 @@ extension Program { return true case SubscriptDecl.self: return ast[SubscriptDecl.ID(decl)!].isStatic + case VarDecl.self: + return isGlobal(varToBinding[VarDecl.ID(decl)!]!) default: return false } From 3fc92f33355cb26def99da7b2674ac28f81a5491 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Mon, 2 Oct 2023 18:36:26 +0200 Subject: [PATCH 083/157] Avoid over-capturing generic arguments in declaration references --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 2e00cc1ac..faade0183 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -3270,14 +3270,18 @@ struct TypeChecker { inScopeIntroducing d: AnyDeclID, resolvedIn context: NameResolutionContext? ) -> GenericArguments { if let a = context?.arguments { return a } - - // References to modules can never capture any generic arguments. - if d.kind == ModuleDecl.self { return [:] } + if !mayCaptureGenericParameters(d) { return [:] } let parameters = accumulatedGenericParameters(in: program[d].scope) return .init(skolemizing: parameters, in: program.ast) } + /// Returns `true` if a reference to `d` may capture generic parameters from the surrounding + /// lookup context. + private func mayCaptureGenericParameters(_ d: AnyDeclID) -> Bool { + d.kind.value is GenericScope.Type + } + /// Associates `parameters`, which are introduced by `name`'s declaration, to corresponding /// values in `arguments` if the two arrays have the same length. Otherwise, returns `nil`, /// reporting diagnostics to `log`. From bf297a5eac082553a396624fd1ad0e97faea159b Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 3 Oct 2023 00:09:58 +0200 Subject: [PATCH 084/157] Implement a helper to lift generic parameters --- Sources/FrontEnd/TypedProgram.swift | 17 ++++++++++++++ .../IR/Analysis/Module+Depolymorphize.swift | 22 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/Sources/FrontEnd/TypedProgram.swift b/Sources/FrontEnd/TypedProgram.swift index 978ed2bf5..e1540a06b 100644 --- a/Sources/FrontEnd/TypedProgram.swift +++ b/Sources/FrontEnd/TypedProgram.swift @@ -264,6 +264,23 @@ public struct TypedProgram { return result } + /// Returns the generic parameters captured in the scope of `d` if `d` is callable. Otherwise, + /// returns an empty collection. + public func liftedGenericParameters(of d: AnyDeclID) -> [GenericParameterDecl.ID] { + switch d.kind { + case FunctionDecl.self: + return Array(accumulatedGenericParameters(in: FunctionDecl.ID(d)!)) + case InitializerDecl.self: + return Array(accumulatedGenericParameters(in: InitializerDecl.ID(d)!)) + case SubscriptImpl.self: + return Array(accumulatedGenericParameters(in: SubscriptImpl.ID(d)!)) + case MethodImpl.self: + return Array(accumulatedGenericParameters(in: MethodImpl.ID(d)!)) + default: + return [] + } + } + /// Returns generic parameters captured by `s` and the scopes semantically containing `s`. public func accumulatedGenericParameters( in s: T diff --git a/Sources/IR/Analysis/Module+Depolymorphize.swift b/Sources/IR/Analysis/Module+Depolymorphize.swift index 70d9ea6cb..bd537f3df 100644 --- a/Sources/IR/Analysis/Module+Depolymorphize.swift +++ b/Sources/IR/Analysis/Module+Depolymorphize.swift @@ -560,3 +560,25 @@ extension Module { } } + +extension TypedProgram { + + /// Returns the generic parameters captured in the scope of `f`. + fileprivate func liftedGenericParameters(of f: Function.ID) -> [GenericParameterDecl.ID] { + switch f.value { + case .lowered(let d): + return liftedGenericParameters(of: d) + + case .synthesized(let d): + if let a = BoundGenericType(d.receiver)?.arguments { + return Array(a.keys) + } else { + return [] + } + + case .existentialized, .monomorphized: + return [] + } + } + +} From 1a241558712908431e43f87e2a398e2e484cb133 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 3 Oct 2023 00:10:40 +0200 Subject: [PATCH 085/157] Add a note about over-monomorphization --- Sources/IR/Analysis/Module+Depolymorphize.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Sources/IR/Analysis/Module+Depolymorphize.swift b/Sources/IR/Analysis/Module+Depolymorphize.swift index bd537f3df..56ce106af 100644 --- a/Sources/IR/Analysis/Module+Depolymorphize.swift +++ b/Sources/IR/Analysis/Module+Depolymorphize.swift @@ -127,6 +127,14 @@ extension Module { _ f: Function.ID, in ir: IR.Program, for specialization: GenericArguments, in scopeOfUse: AnyScopeID ) -> Function.ID { + // TODO: Avoid monomorphizing non-generic entities (# + // let parameters = ir.base.liftedGenericParameters(of: f) + // if parameters.isEmpty { + // return f + // } + // let specialization = GenericArguments( + // uniqueKeysWithValues: parameters.map({ ($0, specialization[$0]!) })) + let result = demandMonomorphizedDeclaration(of: f, in: ir, for: specialization, in: scopeOfUse) if self[result].entry != nil { return result From fe7a5c1798073bb03cf3eab8bd07e5cfa7112a58 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 3 Oct 2023 00:16:08 +0200 Subject: [PATCH 086/157] Apply swift-format --- Sources/CodeGen/LLVM/Transpilation.swift | 4 ++-- Sources/Core/CompileTimeValues/GenericArguments.swift | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index 24ad2e7e5..776d47ea8 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -1,6 +1,6 @@ import Core -import IR import Foundation +import IR import LLVM import Utils @@ -335,7 +335,7 @@ extension LLVM.Module { private mutating func demandMetatype( of t: T, usedIn m: IR.Module, from ir: IR.Program, initializedWith initializeInstance: (inout Self, LLVM.GlobalVariable) -> Void - ) -> LLVM.GlobalVariable{ + ) -> LLVM.GlobalVariable { let globalName = ir.base.mangled(t) if let g = global(named: globalName) { return g } diff --git a/Sources/Core/CompileTimeValues/GenericArguments.swift b/Sources/Core/CompileTimeValues/GenericArguments.swift index 48ba07a40..a850b256e 100644 --- a/Sources/Core/CompileTimeValues/GenericArguments.swift +++ b/Sources/Core/CompileTimeValues/GenericArguments.swift @@ -36,9 +36,10 @@ public struct GenericArguments { public init( skolemizing parameters: S, in ast: AST ) where S.Element == GenericParameterDecl.ID { - self.contents = .init(uniqueKeysWithValues: parameters.map { (p) in - (key: p, value: ^GenericTypeParameterType(p, ast: ast)) - }) + self.contents = .init( + uniqueKeysWithValues: parameters.map { (p) in + (key: p, value: ^GenericTypeParameterType(p, ast: ast)) + }) } /// A collection with the parameters to which arguments are passed. From c7fcf2641f019b88238bba0d647efeed46608cda Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 3 Oct 2023 08:03:01 +0200 Subject: [PATCH 087/157] Fix depolymorphization of subscript requirements This commit fixes #1054. --- .../IR/Analysis/Module+Depolymorphize.swift | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Sources/IR/Analysis/Module+Depolymorphize.swift b/Sources/IR/Analysis/Module+Depolymorphize.swift index 70d9ea6cb..03bcd7351 100644 --- a/Sources/IR/Analysis/Module+Depolymorphize.swift +++ b/Sources/IR/Analysis/Module+Depolymorphize.swift @@ -399,8 +399,7 @@ extension Module { if s.specialization.isEmpty { newCallee = s.callee } else { - let p = program.specialize(s.specialization, for: specialization, in: scopeOfUse) - newCallee = monomorphize(s.callee, in: ir, for: p, in: scopeOfUse) + newCallee = rewritten(s.callee, specializedBy: s.specialization) } let projection = RemoteType( @@ -487,15 +486,18 @@ extension Module { // implicit `Self` parameter. if c.specialization.isEmpty { return c } - let p = program.specialize(c.specialization, for: specialization, in: scopeOfUse) - let f: Function.ID - if let m = program.traitMember(referredBy: c.function) { - f = monomorphize(requirement: m.declaration, of: m.trait, in: ir, for: p, in: scopeOfUse) + let f = rewritten(c.function, specializedBy: c.specialization) + return FunctionReference(to: f, in: self) + } + + func rewritten(_ f: Function.ID, specializedBy a: GenericArguments) -> Function.ID { + let p = program.specialize(a, for: specialization, in: scopeOfUse) + if let m = program.traitMember(referredBy: f) { + return monomorphize( + requirement: m.declaration, of: m.trait, in: ir, for: p, in: scopeOfUse) } else { - f = monomorphize(c.function, in: ir, for: p, in: scopeOfUse) + return monomorphize(f, in: ir, for: p, in: scopeOfUse) } - - return FunctionReference(to: f, in: self) } /// Returns the rewritten form of `o` for use in `scopeOfUse`. From 7a156bb90c8d22896d3e30ee8b52bf2c3057bd7b Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 3 Oct 2023 08:27:14 +0200 Subject: [PATCH 088/157] Improve documentation --- Sources/IR/Analysis/Module+Depolymorphize.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Sources/IR/Analysis/Module+Depolymorphize.swift b/Sources/IR/Analysis/Module+Depolymorphize.swift index 03bcd7351..2e398e63d 100644 --- a/Sources/IR/Analysis/Module+Depolymorphize.swift +++ b/Sources/IR/Analysis/Module+Depolymorphize.swift @@ -468,7 +468,7 @@ extension Module { append(newInstruction, to: b) } - /// Returns the rewritten form of `c` monomorphized for use in `scopeOfuse`. + /// Returns a monomorphized copy of `c` monomorphized for use in `scopeOfuse`. func rewritten(_ c: any Constant) -> any Constant { switch c { case let r as FunctionReference: @@ -480,7 +480,7 @@ extension Module { } } - /// Returns the rewritten form of `c` monomorphized for use in `scopeOfuse`. + /// Returns a monomorphized copy of `c` monomorphized for use in `scopeOfuse`. func rewritten(_ c: FunctionReference) -> FunctionReference { // Unspecialized references cannot refer to trait members, which are specialized for the // implicit `Self` parameter. @@ -490,6 +490,11 @@ extension Module { return FunctionReference(to: f, in: self) } + /// Returns a monomorphized copy of `f` specialized by `a` for use in `scopeOfUse`. + /// + /// If `f` is a trait requirement, the result is a monomorphized version of that requirement's + /// implementation, using `a` to identify the requirement's receiver. Otherwise, the result is + /// a monomorphized copy of `f`. func rewritten(_ f: Function.ID, specializedBy a: GenericArguments) -> Function.ID { let p = program.specialize(a, for: specialization, in: scopeOfUse) if let m = program.traitMember(referredBy: f) { @@ -500,7 +505,7 @@ extension Module { } } - /// Returns the rewritten form of `o` for use in `scopeOfUse`. + /// Returns a monomorphized copy of `o` for use in `scopeOfUse`. func rewritten(_ o: Operand) -> Operand { switch o { case .constant(let c): From 0c14a2ed92d2463c431d0e2cd8b1702c75e1036e Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 3 Oct 2023 08:27:58 +0200 Subject: [PATCH 089/157] Show function linkage in the textual form of the IR --- Sources/IR/Module+Description.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/IR/Module+Description.swift b/Sources/IR/Module+Description.swift index bfa9e9fda..3c3058421 100644 --- a/Sources/IR/Module+Description.swift +++ b/Sources/IR/Module+Description.swift @@ -42,6 +42,8 @@ extension Module: TextOutputStreamable { output.write("// \(function.site)\n") } + output.write("\(function.linkage) ") + if function.isSubscript { output.write("subscript \(f)(") output.write(function.inputs.lazy.descriptions()) From 543b3a08b134e66b05d7a1941cb73706836119bb Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 3 Oct 2023 08:28:20 +0200 Subject: [PATCH 090/157] Test requirement monomorphization --- .../TestCases/MonomorphizeRequirements.hylo | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Tests/EndToEndTests/TestCases/MonomorphizeRequirements.hylo diff --git a/Tests/EndToEndTests/TestCases/MonomorphizeRequirements.hylo b/Tests/EndToEndTests/TestCases/MonomorphizeRequirements.hylo new file mode 100644 index 000000000..f3a1b0081 --- /dev/null +++ b/Tests/EndToEndTests/TestCases/MonomorphizeRequirements.hylo @@ -0,0 +1,18 @@ +//- compileAndRun expecting: success + +trait P { + fun f() -> Int + property n: Int { let } +} + +type A: Deinitializable, P { + var x: Int + memberwise init + public fun f() -> Int { x.copy() } + public property n: Int { let { yield x } } +} + +public fun main() { + precondition(A(x: 42).f() == 42) + precondition(A(x: 42).n.copy() == 42) +} From 738ec9d7690a116fac2278580156b47fcf4933dc Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 3 Oct 2023 23:09:39 +0200 Subject: [PATCH 091/157] Apply swift-format --- Sources/Core/AST/Decl/TraitDecl.swift | 2 +- Sources/Core/AST/Stmt/ConditionalStmt.swift | 1 - Sources/Core/AST/Stmt/DoWhileStmt.swift | 5 ++++- Sources/FrontEnd/Parse/Parser.swift | 9 +++++---- Sources/IR/Emitter.swift | 5 +++-- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Sources/Core/AST/Decl/TraitDecl.swift b/Sources/Core/AST/Decl/TraitDecl.swift index 2e169d237..afd7c7cd3 100644 --- a/Sources/Core/AST/Decl/TraitDecl.swift +++ b/Sources/Core/AST/Decl/TraitDecl.swift @@ -7,7 +7,7 @@ public struct TraitDecl: ExposableDecl, SingleEntityDecl, LexicalScope { public let site: SourceRange - /// The site of the `trait` introducer. + /// The site of the `trait` introducer. public let introducerSite: SourceRange /// The access modifier of the declaration, if any. public let accessModifier: SourceRepresentable diff --git a/Sources/Core/AST/Stmt/ConditionalStmt.swift b/Sources/Core/AST/Stmt/ConditionalStmt.swift index 91574c40a..1d8161417 100644 --- a/Sources/Core/AST/Stmt/ConditionalStmt.swift +++ b/Sources/Core/AST/Stmt/ConditionalStmt.swift @@ -1,4 +1,3 @@ - /// A conditional statement. /// /// Unlike a `ConditionalExpr`, the branches of a conditional statement are represented by brace diff --git a/Sources/Core/AST/Stmt/DoWhileStmt.swift b/Sources/Core/AST/Stmt/DoWhileStmt.swift index 3864bd081..69ac5ca35 100644 --- a/Sources/Core/AST/Stmt/DoWhileStmt.swift +++ b/Sources/Core/AST/Stmt/DoWhileStmt.swift @@ -14,7 +14,10 @@ public struct DoWhileStmt: Stmt { /// - Note: The condition is evaluated in the lexical scope of the body. public let condition: Introduced - public init(introducerSite: SourceRange, body: BraceStmt.ID, condition: Introduced, site: SourceRange) { + public init( + introducerSite: SourceRange, body: BraceStmt.ID, condition: Introduced, + site: SourceRange + ) { self.site = site self.introducerSite = introducerSite self.body = body diff --git a/Sources/FrontEnd/Parse/Parser.swift b/Sources/FrontEnd/Parse/Parser.swift index 1309c8eda..641f202c9 100644 --- a/Sources/FrontEnd/Parse/Parser.swift +++ b/Sources/FrontEnd/Parse/Parser.swift @@ -2741,8 +2741,7 @@ public enum Parser { if let s = try parseConditionalStmt(in: &state) { let s = AnyStmtID(s) return Introduced(s, at: introducer.site) - } - else { + } else { let s = AnyStmtID(try state.expect("'{'", using: braceStmt)) return Introduced(s, at: introducer.site) } @@ -2802,9 +2801,11 @@ public enum Parser { site: tree.0.0.0.0.site.extended(upTo: state.currentIndex))) })) - static let forSite = (take(.in).and(expr).map({ (state, tree) in Introduced(tree.1, at: tree.0.site)})) + static let forSite = + (take(.in).and(expr).map({ (state, tree) in Introduced(tree.1, at: tree.0.site) })) - static let forFilter = (take(.where).and(expr).map({ (state, tree) in Introduced(tree.1, at: tree.0.site)})) + static let forFilter = + (take(.where).and(expr).map({ (state, tree) in Introduced(tree.1, at: tree.0.site) })) static let loopBody = inContext(.loopBody, apply: braceStmt) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 1c1266409..758ad44b1 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -994,8 +994,9 @@ struct Emitter { frames.pop() insert( - module.makeCondBranch(if: c, then: loopBody, else: loopTail, at: ast[condition].site -)) + module.makeCondBranch( + if: c, then: loopBody, else: loopTail, at: ast[condition].site + )) insertionPoint = .end(of: loopTail) return .next } From ea99094f511a2f2630e8f5c058e9e4cd33d8e392 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Mon, 2 Oct 2023 15:12:41 +0200 Subject: [PATCH 092/157] Fix contextual type inference for the RHS of infix operators --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 353650dd4..67d9fa421 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -3960,7 +3960,7 @@ struct TypeChecker { case .infix(let callee, let lhs, let rhs): // Infer the types of the operands. let lhsType = _inferredType(of: lhs, updating: &obligations) - let rhsType = _inferredType(of: rhs, updating: &obligations) + let rhsType = _inferredType(of: rhs, withHint: ^freshVariable(), updating: &obligations) if lhsType.isError || rhsType.isError { return .error From 5253590073bcaedc7059ead710b97f745de2bea4 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 00:15:28 +0200 Subject: [PATCH 093/157] Handle lvalue lowering for upcasts on numeric literals --- Sources/IR/Emitter.swift | 16 +++++++++++++++- .../TestCases/Lowering/NumericLiteral.hylo | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 758ad44b1..8e9ea57b0 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -2082,10 +2082,24 @@ struct Emitter { /// Inserts the IR for lvalue `e`. private mutating func emitLValue(_ e: CastExpr.ID) -> Operand { switch ast[e].direction { + case .up: + return emitLValue(upcast: e) case .pointerConversion: return emitLValue(pointerConversion: e) default: - UNIMPLEMENTED() + UNIMPLEMENTED("lvalue lowering for cast expressions #1049") + } + } + + /// Inserts the IR for lvalue `e`. + private mutating func emitLValue(upcast e: CastExpr.ID) -> Operand { + switch ast[e].left.kind { + case FloatLiteralExpr.self: + emitStore(value: ast[e].left) + case IntegerLiteralExpr.self: + emitStore(value: ast[e].left) + default: + UNIMPLEMENTED("lvalue lowering for cast expressions #1049") } } diff --git a/Tests/HyloTests/TestCases/Lowering/NumericLiteral.hylo b/Tests/HyloTests/TestCases/Lowering/NumericLiteral.hylo index 2c0ad08ff..88277275d 100644 --- a/Tests/HyloTests/TestCases/Lowering/NumericLiteral.hylo +++ b/Tests/HyloTests/TestCases/Lowering/NumericLiteral.hylo @@ -17,4 +17,7 @@ public fun main() { let _: Float32 = 1 let _: Float32 = 1.0 + + let _ = 1 as Int8 + let _ = 1 as Float64 } From 422da2ddb150bd3be1c893a8866490929465ea1f Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 00:16:11 +0200 Subject: [PATCH 094/157] Fix typo --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 1d0c5d102..241f34c57 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -3692,7 +3692,7 @@ struct TypeChecker { _ = inferredType(of: program[e].left, updating: &obligations) case .up: - // The type of thr LHS must be statically known to subtype of the RHS. + // The type of the LHS must be statically known to subtype of the RHS. let lhs = inferredType( of: program[e].left, withHint: ^freshVariable(), updating: &obligations) obligations.insert(SubtypingConstraint(lhs, rhs.shape, origin: cause)) From 70b1faebe8c6c10b72c3e9f902ee4fe2d592214e Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 00:33:12 +0200 Subject: [PATCH 095/157] Add missing return statements --- Sources/IR/Emitter.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 8e9ea57b0..5484d7f0e 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -2095,9 +2095,9 @@ struct Emitter { private mutating func emitLValue(upcast e: CastExpr.ID) -> Operand { switch ast[e].left.kind { case FloatLiteralExpr.self: - emitStore(value: ast[e].left) + return emitStore(value: ast[e].left) case IntegerLiteralExpr.self: - emitStore(value: ast[e].left) + return emitStore(value: ast[e].left) default: UNIMPLEMENTED("lvalue lowering for cast expressions #1049") } From bd4cb7ab77b594f814a9c869da576f41a147321f Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sat, 30 Sep 2023 06:06:07 +0200 Subject: [PATCH 096/157] Fix live-range computation --- Sources/IR/Analysis/Lifetime.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/IR/Analysis/Lifetime.swift b/Sources/IR/Analysis/Lifetime.swift index 6844c92c8..1b5602779 100644 --- a/Sources/IR/Analysis/Lifetime.swift +++ b/Sources/IR/Analysis/Lifetime.swift @@ -193,7 +193,7 @@ extension Module { case (.closed(let lhs), .liveIn(let rhs)): return .liveIn(lastUse: last(lhs, rhs)) case (.closed(let lhs), .closed(let rhs)): - return .liveIn(lastUse: last(lhs, rhs)) + return .closed(lastUse: last(lhs, rhs)) } } return .init(operand: left.operand, coverage: coverage) From 0fc36ee86871263391ac78e92a22c91279e69bfa Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 00:51:02 +0200 Subject: [PATCH 097/157] Remove needless semicolons --- Library/Hylo/Core/Numbers/Integers/Int.hylo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Library/Hylo/Core/Numbers/Integers/Int.hylo b/Library/Hylo/Core/Numbers/Integers/Int.hylo index e05b27dd1..38b2fd7ca 100644 --- a/Library/Hylo/Core/Numbers/Integers/Int.hylo +++ b/Library/Hylo/Core/Numbers/Integers/Int.hylo @@ -30,10 +30,10 @@ public type Int { if r == 0 { return self.copy() } else if r < 0 { - return -(abs() - r); + return -(abs() - r) } - return self + stride - r; + return self + stride - r } /// Returns `self`. From 35be0f019930b3cc73922fcd55cbfaa776552ff7 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 00:52:01 +0200 Subject: [PATCH 098/157] Make 'Int8' conform to 'Equatable' --- Library/Hylo/Core/Numbers/Integers/Int8.hylo | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Library/Hylo/Core/Numbers/Integers/Int8.hylo b/Library/Hylo/Core/Numbers/Integers/Int8.hylo index 41d32b01f..b74bffbff 100644 --- a/Library/Hylo/Core/Numbers/Integers/Int8.hylo +++ b/Library/Hylo/Core/Numbers/Integers/Int8.hylo @@ -30,3 +30,15 @@ public conformance Int8: Copyable { } } + +public conformance Int8: Equatable { + + public fun infix== (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_eq_i8(value, other.value)) + } + + public fun infix!= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ne_i8(value, other.value)) + } + +} From ecc720ec97ed7f99c082f3673041bf3c9d4d78b2 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 00:52:43 +0200 Subject: [PATCH 099/157] Make 'Int8' conform to 'Comparable' --- Library/Hylo/Core/Numbers/Integers/Int8.hylo | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Library/Hylo/Core/Numbers/Integers/Int8.hylo b/Library/Hylo/Core/Numbers/Integers/Int8.hylo index b74bffbff..560af4033 100644 --- a/Library/Hylo/Core/Numbers/Integers/Int8.hylo +++ b/Library/Hylo/Core/Numbers/Integers/Int8.hylo @@ -42,3 +42,23 @@ public conformance Int8: Equatable { } } + +public conformance Int8: Comparable { + + public fun infix< (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_slt_i8(value, other.value)) + } + + public fun infix<= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_sle_i8(value, other.value)) + } + + public fun infix> (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_sgt_i8(value, other.value)) + } + + public fun infix>= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_sge_i8(value, other.value)) + } + +} From 58e987c3fb06031288a89fc811817884d9d6ce5a Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 00:53:31 +0200 Subject: [PATCH 100/157] Make 'Int8' conform to 'AdditiveArithmetic' --- Library/Hylo/Core/Numbers/Integers/Int8.hylo | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Library/Hylo/Core/Numbers/Integers/Int8.hylo b/Library/Hylo/Core/Numbers/Integers/Int8.hylo index 560af4033..b96aab005 100644 --- a/Library/Hylo/Core/Numbers/Integers/Int8.hylo +++ b/Library/Hylo/Core/Numbers/Integers/Int8.hylo @@ -62,3 +62,27 @@ public conformance Int8: Comparable { } } + +public conformance Int8: AdditiveArithmetic { + + public fun infix+ (_ other: Self) -> Self { + Int8(value: Builtin.add_i8(value, other.value)) + } + + public fun infix+= (_ other: Self) inout { + &self.value = Builtin.add_i8(value, other.value) + } + + public fun infix- (_ other: Self) -> Self { + Int8(value: Builtin.sub_i8(value, other.value)) + } + + public fun infix-= (_ other: Self) inout { + &self.value = Builtin.sub_i8(value, other.value) + } + + public static fun zero() -> Self { + Int8() + } + +} From d63595cb38bc13c04cf41a6c847e89b8b8b03068 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 00:57:12 +0200 Subject: [PATCH 101/157] Add 'UInt8' to the standard library --- Library/Hylo/Core/Numbers/Integers/Int8.hylo | 5 ++++ Library/Hylo/Core/Numbers/Integers/UInt8.hylo | 30 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 Library/Hylo/Core/Numbers/Integers/UInt8.hylo diff --git a/Library/Hylo/Core/Numbers/Integers/Int8.hylo b/Library/Hylo/Core/Numbers/Integers/Int8.hylo index b96aab005..9be768c5e 100644 --- a/Library/Hylo/Core/Numbers/Integers/Int8.hylo +++ b/Library/Hylo/Core/Numbers/Integers/Int8.hylo @@ -10,6 +10,11 @@ public type Int8 { &self.value = Builtin.zeroinitializer_i8() } + /// Creates an instance with the same memory representation as `other`. + public init(bit_pattern other: UInt8) { + &self.value = other.value + } + /// Creates a copy of `other`. /// /// - Requires: The value of `other` must be representable in this type. diff --git a/Library/Hylo/Core/Numbers/Integers/UInt8.hylo b/Library/Hylo/Core/Numbers/Integers/UInt8.hylo new file mode 100644 index 000000000..cee1987a0 --- /dev/null +++ b/Library/Hylo/Core/Numbers/Integers/UInt8.hylo @@ -0,0 +1,30 @@ +/// A 8-bit unsigned integer value. +public type UInt8 { + + var value: Builtin.i8 + + memberwise init + + /// Creates an instance with value `0`. + public init() { + &self.value = Builtin.zeroinitializer_i8() + } + + /// Creates an instance with the same memory representation as `other`. + public init(bit_pattern other: Int8) { + &self.value = other.value + } + +} + +public conformance UInt8: ExpressibleByIntegerLiteral {} + +public conformance UInt8: Deinitializable {} + +public conformance UInt8: Copyable { + + public fun copy() -> Self { + UInt8(value: value) + } + +} From ac1d48db34e26faaf673af8d156fddf669668722 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 00:58:30 +0200 Subject: [PATCH 102/157] Make 'Int8' conform to 'Numeric' --- Library/Hylo/Core/Numbers/Integers/Int8.hylo | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Library/Hylo/Core/Numbers/Integers/Int8.hylo b/Library/Hylo/Core/Numbers/Integers/Int8.hylo index 9be768c5e..fc493780d 100644 --- a/Library/Hylo/Core/Numbers/Integers/Int8.hylo +++ b/Library/Hylo/Core/Numbers/Integers/Int8.hylo @@ -91,3 +91,21 @@ public conformance Int8: AdditiveArithmetic { } } + +public conformance Int8: Numeric { + + public typealias Magnitude = UInt8 + + public fun magnitude() -> UInt8 { + UInt8(bit_pattern: self) + } + + public fun infix* (_ other: Self) -> Self { + Int8(value: Builtin.mul_i8(value, other.value)) + } + + public fun infix*= (_ other: Self) inout { + &self.value = Builtin.mul_i8(value, other.value) + } + +} From 1d3d9f26f77ad0a6e17e1204917779c55e4b01c9 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 01:01:24 +0200 Subject: [PATCH 103/157] Make 'Int8' conform to 'SignedNumeric' --- Library/Hylo/Core/Numbers/Integers/Int8.hylo | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Library/Hylo/Core/Numbers/Integers/Int8.hylo b/Library/Hylo/Core/Numbers/Integers/Int8.hylo index fc493780d..3a83935b4 100644 --- a/Library/Hylo/Core/Numbers/Integers/Int8.hylo +++ b/Library/Hylo/Core/Numbers/Integers/Int8.hylo @@ -109,3 +109,15 @@ public conformance Int8: Numeric { } } + +public conformance Int8: SignedNumeric { + + public fun prefix- () -> Self { + Int8() - self + } + + public fun negate() inout { + &self = -self + } + +} From 8f428570033982d82872290fd6a80f66dae254e8 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 08:43:58 +0200 Subject: [PATCH 104/157] Implement the initializers of 'FixedWidthInteger' for 'Int8' --- Library/Hylo/Core/Numbers/Integers/Int8.hylo | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Library/Hylo/Core/Numbers/Integers/Int8.hylo b/Library/Hylo/Core/Numbers/Integers/Int8.hylo index 3a83935b4..b9061ca44 100644 --- a/Library/Hylo/Core/Numbers/Integers/Int8.hylo +++ b/Library/Hylo/Core/Numbers/Integers/Int8.hylo @@ -5,11 +5,6 @@ public type Int8 { memberwise init - /// Creates an instance with value `0`. - public init() { - &self.value = Builtin.zeroinitializer_i8() - } - /// Creates an instance with the same memory representation as `other`. public init(bit_pattern other: UInt8) { &self.value = other.value @@ -121,3 +116,16 @@ public conformance Int8: SignedNumeric { } } + +public extension Int8 { + + public init() { + &self.value = Builtin.zeroinitializer_i8() + } + + public init(truncating_or_extending source: T) { + let w = source.words() + &self.value = Builtin.trunc_word_i8(w[w.start_index()].value) + } + +} From 76dfb7e8adc3dce319a7ff6cfc171f6d8a1797b0 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 08:53:59 +0200 Subject: [PATCH 105/157] Use 'false' instead of 'true' as start index of 'CollectionOfOne' --- Library/Hylo/Core/CollectionOfOne.hylo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Library/Hylo/Core/CollectionOfOne.hylo b/Library/Hylo/Core/CollectionOfOne.hylo index 7faced852..bd50b9ba0 100644 --- a/Library/Hylo/Core/CollectionOfOne.hylo +++ b/Library/Hylo/Core/CollectionOfOne.hylo @@ -11,16 +11,16 @@ public type CollectionOfOne: Deinitializable &self.contents = contents } - public fun start_index() -> Bool { true } + public fun start_index() -> Bool { false } - public fun end_index() -> Bool { false } + public fun end_index() -> Bool { true } - public fun index(after i: Bool) -> Bool { false } + public fun index(after i: Bool) -> Bool { true } public subscript(_ position: Bool): Element { let { // TODO: uncomment when #1046 is implemented - // precondition(position, "index is out of bounds") + // precondition(!position, "index is out of bounds") yield contents } } From 904b06db8db4ae4bb6e357d8f45eb4c8c3057a44 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 08:54:39 +0200 Subject: [PATCH 106/157] Add 'matches(_:)' to 'FixedWidthInteger' --- Library/Hylo/Core/Numbers/Integers/FixedWidthInteger.hylo | 3 +++ Library/Hylo/Core/Numbers/Integers/Int.hylo | 4 ++++ Library/Hylo/Core/Numbers/Integers/UInt.hylo | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/Library/Hylo/Core/Numbers/Integers/FixedWidthInteger.hylo b/Library/Hylo/Core/Numbers/Integers/FixedWidthInteger.hylo index 2f8ee8001..93c95bc38 100644 --- a/Library/Hylo/Core/Numbers/Integers/FixedWidthInteger.hylo +++ b/Library/Hylo/Core/Numbers/Integers/FixedWidthInteger.hylo @@ -4,6 +4,9 @@ /// catch overflows, or access the minimum or maximum representable values of an integer type. public trait FixedWidthInteger: BinaryInteger { + /// Returns `true` if the bits set in `mask` are also set in `self`. + fun matches(_ mask: Self) -> Bool + /// Returns the sum of `self` and `other` along with a flag indicating whether overflow occurred /// in the operation. fun adding_reporting_overflow(_ other: Self) -> {partial_value: Self, overflow: Bool} diff --git a/Library/Hylo/Core/Numbers/Integers/Int.hylo b/Library/Hylo/Core/Numbers/Integers/Int.hylo index 38b2fd7ca..3db5bb2e9 100644 --- a/Library/Hylo/Core/Numbers/Integers/Int.hylo +++ b/Library/Hylo/Core/Numbers/Integers/Int.hylo @@ -242,6 +242,10 @@ public conformance Int: BinaryInteger { public conformance Int: FixedWidthInteger { + public fun matches(_ mask: Self) -> Bool { + (self & mask) == mask + } + public fun adding_reporting_overflow(_ other: Self) -> {partial_value: Self, overflow: Bool} { let r = Builtin.sadd_with_overflow_word(value, other.value) return (partial_value: Int(value: r.0), overflow: Bool(value: r.1)) diff --git a/Library/Hylo/Core/Numbers/Integers/UInt.hylo b/Library/Hylo/Core/Numbers/Integers/UInt.hylo index 754fed567..cef4aced2 100644 --- a/Library/Hylo/Core/Numbers/Integers/UInt.hylo +++ b/Library/Hylo/Core/Numbers/Integers/UInt.hylo @@ -204,6 +204,10 @@ public conformance UInt: BinaryInteger { public conformance UInt: FixedWidthInteger { + public fun matches(_ mask: Self) -> Bool { + (self & mask) == mask + } + public fun adding_reporting_overflow(_ other: Self) -> {partial_value: Self, overflow: Bool} { let r = Builtin.uadd_with_overflow_word(value, other.value) return (partial_value: UInt(value: r.0), overflow: Bool(value: r.1)) From 028c311dc7671b762b163d512bae21c24f84a174 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 08:55:04 +0200 Subject: [PATCH 107/157] Implement 'Pointer.init(bit_pattern:)' --- Library/Hylo/Core/Pointer.hylo | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Library/Hylo/Core/Pointer.hylo b/Library/Hylo/Core/Pointer.hylo index cd58565b8..337ae2bf0 100644 --- a/Library/Hylo/Core/Pointer.hylo +++ b/Library/Hylo/Core/Pointer.hylo @@ -14,6 +14,11 @@ public type Pointer: Regular { yield base as* (remote let Pointee) } + /// Creates an instance with the same memory representation as `address`. + public init(bit_pattern address: UInt) { + &self.base = Builtin.inttoptr_word(address.value) + } + /// Creates an instance representing the same address as `p`. public init(_ p: PointerToMutable) { &base = p.base From 2d8458014df1316f1509e5ef7e970a21bf01dfd4 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 08:55:20 +0200 Subject: [PATCH 108/157] Implement 'Pointer.advance(by:)' --- Library/Hylo/Core/Pointer.hylo | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Library/Hylo/Core/Pointer.hylo b/Library/Hylo/Core/Pointer.hylo index 337ae2bf0..85248de90 100644 --- a/Library/Hylo/Core/Pointer.hylo +++ b/Library/Hylo/Core/Pointer.hylo @@ -32,6 +32,13 @@ public type Pointer: Regular { &base = p.base } + /// Returns `self` offset forward by `n` array elements of `Pointee` type. + public fun advance(by n: Int) -> Self { + let offset_in_bytes = MemoryLayout.stride() * n + return Pointer.new( + base: Builtin.advanced_by_bytes_word(base, offset_in_bytes.value)) + } + /// Creates an instance that does not address any usable storage. public static fun null() -> Self { .new(base: Builtin.zeroinitializer_ptr()) From ed2b7051218541045c33486c6f3eae2bffce2a92 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 08:55:41 +0200 Subject: [PATCH 109/157] Implement 'mutable_pointer(to:)' --- Library/Hylo/Core/PointerToMutable.hylo | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Library/Hylo/Core/PointerToMutable.hylo b/Library/Hylo/Core/PointerToMutable.hylo index 17a5bd82f..1e9a7c5aa 100644 --- a/Library/Hylo/Core/PointerToMutable.hylo +++ b/Library/Hylo/Core/PointerToMutable.hylo @@ -103,6 +103,12 @@ public extension PointerToMutable where Pointee: Movable { } +/// The address of `x`. +public subscript mutable_pointer(to x: inout T): PointerToMutable { + 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). From 5dccb41b671d181255afc14fe8d2154292857b67 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 17:09:38 +0200 Subject: [PATCH 110/157] Add missing operator declarations --- Library/Hylo/Core/Operators.hylo | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Library/Hylo/Core/Operators.hylo b/Library/Hylo/Core/Operators.hylo index da4a99b41..473768c64 100644 --- a/Library/Hylo/Core/Operators.hylo +++ b/Library/Hylo/Core/Operators.hylo @@ -41,8 +41,11 @@ public operator infix/= : assignment public operator infix%= : assignment public operator infix+= : assignment public operator infix-= : assignment +public operator infix^= : assignment public operator infix&= : assignment public operator infix&&= : assignment +public operator infix|= : assignment +public operator infix||= : assignment public operator infix** : exponentiation From a91bac504aa638cccb0a797b17bedf2742b4ba43 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 17:10:05 +0200 Subject: [PATCH 111/157] Implement FFIs for 'memcpy' and 'memmove' --- Library/Hylo/LibC.hylo | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Library/Hylo/LibC.hylo b/Library/Hylo/LibC.hylo index 6657da845..d1ce5d176 100644 --- a/Library/Hylo/LibC.hylo +++ b/Library/Hylo/LibC.hylo @@ -22,3 +22,28 @@ public fun fdopen(_ descriptor: Int, _ mode: CVoidPointer) -> CVoidPointer /// returning the number of elements written. @ffi("fwrite") public fun fwrite(_ data: CVoidPointer, _ size: Int, _ count: Int, _ stream: CVoidPointer) -> Int + +/// Copies `count` elements from the object pointed to by `source` to the object pointed to by +/// `destination` and returns `destination`. +/// +/// Both objects are reinterpreted as buffers of `Int8`. +/// +/// If the objects overlap, the behavior is undefined. If either `source` or `destination` is +/// invalid or null, the behavior is undefined. +@ffi("memcpy") +public fun memcpy( + _ destination: CVoidPointer, _ source: CVoidPointer, _ count: Int +) -> CVoidPointer + +/// Copies `count` elements from the object pointed to by `source` to the object pointed to by +/// `destination` and returns `destination`. +/// +/// Both objects are reinterpreted as buffers of `Int8`. +/// +/// Objects may overlap: copying takes place as if the elements from `source` were copied to a +/// temporary buffer and then copied to `destination`. If either `source` or `destination` is +/// invalid or null, the behavior is undefined. +@ffi("memmove") +public fun memmove( + _ destination: CVoidPointer, _ source: CVoidPointer, _ count: Int +) -> CVoidPointer From ace94350f72bf69e0995c5e38b7e18316867d2e5 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 01:28:58 +0200 Subject: [PATCH 112/157] Improve the consistency of Array's documentation --- Library/Hylo/Array.hylo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 577455ca1..bf8d40e5e 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -6,7 +6,7 @@ public type Array: Deinitializable { /// The header of the buffer indicates the number of elements contained in the array. var storage: DynamicBuffer - /// Creates a new, empty array. + /// Creates an empty array. public init() { &storage = .new() } @@ -20,7 +20,7 @@ public type Array: Deinitializable { } } - /// 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() } } @@ -30,7 +30,7 @@ public type Array: Deinitializable { 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 } From c9590040fe3276432b38e52dcd021342beed775b Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 01:29:46 +0200 Subject: [PATCH 113/157] Redefine 'Array.pointer_to_element' as a subscript --- Library/Hylo/Array.hylo | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index bf8d40e5e..6ee1e63c4 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -15,7 +15,7 @@ public type Array: 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 } } @@ -63,7 +63,7 @@ public type Array: Deinitializable { /// 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 } @@ -73,18 +73,18 @@ public type Array: Deinitializable { 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 { + subscript pointer_to_element(at position: Int): PointerToMutable { storage.first_element_address().advance(by: position) } From e69255b349041c83400316fda986023a6a38206f Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 01:30:54 +0200 Subject: [PATCH 114/157] Implement 'Array.is_empty' --- Library/Hylo/Array.hylo | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 6ee1e63c4..6844d2fd0 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -25,6 +25,11 @@ public type Array: Deinitializable { 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() From eadbf78587292ed72152510dea0415805a085c0b Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 01:31:43 +0200 Subject: [PATCH 115/157] Implement unsafe projections of an Array's contiguous storage --- Library/Hylo/Array.hylo | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 6844d2fd0..868df3957 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -65,6 +65,24 @@ public type Array: 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 { + 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( + _ action: inout [E](PointerToMutable) 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) From 2fd606b46451dec92876ffc2bc5543a16264bf20 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 01:47:47 +0200 Subject: [PATCH 116/157] Remove duplicate function --- Library/Hylo/Core/Bitcast.hylo | 2 +- Library/Hylo/Core/PointerToMutable.hylo | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/Library/Hylo/Core/Bitcast.hylo b/Library/Hylo/Core/Bitcast.hylo index 8f6589a0c..9d1c2f06f 100644 --- a/Library/Hylo/Core/Bitcast.hylo +++ b/Library/Hylo/Core/Bitcast.hylo @@ -5,7 +5,7 @@ public subscript unsafe_bitcast(_ value: T): U { yield p.unsafe[] } inout { - sink let p: PointerToMutable = PointerToMutable(type_punning: pointerToMutable[&value]) + sink let p: PointerToMutable = PointerToMutable(type_punning: mutable_pointer[to: &value]) yield &p.unsafe[] } } diff --git a/Library/Hylo/Core/PointerToMutable.hylo b/Library/Hylo/Core/PointerToMutable.hylo index 1e9a7c5aa..b77a11ddb 100644 --- a/Library/Hylo/Core/PointerToMutable.hylo +++ b/Library/Hylo/Core/PointerToMutable.hylo @@ -55,13 +55,6 @@ public type PointerToMutable: Regular { } - -// FIXME: rename pointer[toMutable:] -/// The address of `x`. -public subscript pointerToMutable(_ x: inout T): PointerToMutable { - let { yield PointerToMutable(base: Builtin.address(of: x)) } -} - public conformance PointerToMutable: Copyable { /// Returns an equivalent instance. @@ -103,12 +96,12 @@ public extension PointerToMutable where Pointee: Movable { } +// TODO: Rename to `pointer[to_mutable:]` /// The address of `x`. public subscript mutable_pointer(to x: inout T): PointerToMutable { 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). From d325e87e2aa161cc22c0f1d21c49ae49a9c71751 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 4 Oct 2023 17:13:19 +0200 Subject: [PATCH 117/157] Add 'UTF8Array' to the standard library --- Library/Hylo/Core/UTF8Array.hylo | 211 +++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 Library/Hylo/Core/UTF8Array.hylo diff --git a/Library/Hylo/Core/UTF8Array.hylo b/Library/Hylo/Core/UTF8Array.hylo new file mode 100644 index 000000000..b9f746e88 --- /dev/null +++ b/Library/Hylo/Core/UTF8Array.hylo @@ -0,0 +1,211 @@ +/// A collection of UTF-8 code units. +public type UTF8Array { + + // TODO: Remove when `UInt64` is implemented + typealias UInt64 = UInt + + /// The units in the collection. + /// + /// The two highest bits of `units`, b63 and b62, encode the representation discriminator: + /// + /// ┌──────────────────────╥─────┬─────┐ + /// │ Form ║ b63 │ b62 │ + /// ├──────────────────────╫─────┼─────┤ + /// │ inline, owned ║ 0 │ 0 │ + /// │ out-of-line, owned ║ 1 │ 0 │ + /// │ out-of-line, unowned ║ 1 │ 1 │ + /// └──────────────────────╨─────┴─────┘ + /// + /// b63 indicates whether the payload of the view is stored out-of-line. If it is, `units` with + /// b63 and b62 unset stores a pointer to the out-of-line payload, which is a buffer storing an + /// `Int`, which is the number of units in the view, followed by a contiguous array of bytes, + /// with contains the units themselves, and finally a null terminator. + /// + /// If the payload is inline, the number of units in the view is stored in the 6 lowest bits of + /// `units`'s most significant byte and the units themselves are stored in the following bytes. + /// For example, the inline UTF-8 view of "Salut" is as follows: + /// + /// most significant byte + /// ↓ + /// ┌────┬────┬────┬────┬────┬────┬────┬────┐ + /// | 05 | 53 | 61 | 6C | 75 | 74 | 00 | 00 | + /// └────┴────┴────┴────┴────┴────┴────┴────┘ + /// + /// b62 indicates if the view owns its storage and is responsible for its deallocation if it is + /// out-of-line. Unowned, out-of-line storage typically correspond to static allocations. + let units: UInt64 + + /// Creates an instance with given representation. + memberwise init + +} + +/// A collection of UTF-8 code units. +public extension UTF8Array { + + /// Creates a view taking ownership of the out-of-line payload referred by `p`. + init(taking_ownership_of p: MemoryAddress) { + var u = UInt64(truncating_or_extending: UInt(bit_pattern: p)) + &u |= (0b10 as UInt64) << 62 + &self = .new(units: u) + } + + /// Creates an empty view. + public init() { + &self = .new(units: 0) + } + + /// Projects the units in `self` as a null-terminated buffer. + /// + /// Use this method to read the contents of the view as a C-style null-terminated string. The + /// returned buffer has a size `count() + 1`. It is alive only for the duration of the projection + /// and shall not be mutated. + public property nullterminated: Pointer { + let { + if is_inline() { + var storage: UInt = 0 + let buffer = PointerToMutable(type_punning: mutable_pointer[to: &storage]) + + // Note: The copy could be optimized away if we stored the units in memory the same way + // they would be stored in an array, i.e., in reverse order on big-endian machines. + var i = 0 + while i < 7 { + let s = 8 * (6 - i) + let v = Int8(truncating_or_extending: units >> s) + buffer.unsafe_initialize_pointee(v) + &i += 1 + } + + yield Pointer(buffer) + } else { + yield unsafe_heap_payload.0 + } + } + } + + /// Returns `true` if the payload of the `self` is stored inline. + fun is_inline() -> Bool { + // Note: the flag is stored inversed so that `0` is an empty string. + (units & ((1 as UInt64) << 63)) == 0 + } + + /// Returns `true` if `self` owns its payload. + fun is_owned() -> Bool { + (units & ((1 as UInt64) << 62)) == 0 + } + + /// Projects the address and size of `self`'s payload, assuming it is allocated out-of-line. + /// + /// - Requires: `!is_inline()`. + property unsafe_heap_payload: {start: Pointer, count: Int} { + let { + // TODO: uncomment when #1046 is implemented + // assert(!is_inline()) + let buffer = Pointer( + bit_pattern: UInt(truncating_or_extending: units & ~((0xff as UInt64) << 56))) + yield ( + start: Pointer(type_punning: buffer.advance(by: 1)), + count: buffer.unsafe[].copy()) + } + } + +} + +public conformance UTF8Array: Deinitializable { + + public fun deinit() sink { + if !is_inline() { + PointerToMutable(adding_mutation_to: unsafe_heap_payload.0).deallocate() + } + } + +} + +public conformance UTF8Array: Copyable { + + public fun copy() -> Self { + if is_inline() || !is_owned() { + return .new(units: units.copy()) + } else { + let payload = unsafe_heap_payload + let payload_size = MemoryLayout.stride() + payload.1 + 1 + let payload_clone = MemoryAddress.allocate_bytes( + count: payload_size, + aligned_at: MemoryLayout.alignment()) + + // Note: copy the entire payload at once. + let d = CVoidPointer(base: payload_clone.base) + let s = CVoidPointer(base: payload.0.copy().base) + _ = memmove(d, s, payload_size) + + return .new(taking_ownership_of: payload_clone) + } + } + +} + +public conformance UTF8Array: Equatable { + + public fun infix== (_ other: Self) -> Bool { + // If both LHS and RHS are stored inline, their representation are bitwise equal. + if self.is_inline() && other.is_inline() { + return self.units == other.units + } + + // LHS and RHS are equal if they point to the same buffer. + if !self.is_inline() && !other.is_inline() { + return self.unsafe_heap_payload.0 == other.unsafe_heap_payload.0 + } + + // LHS and RHS are equal if they contain the same elements in the same order. + // TODO: Rewrite as `self.elements_equal(other)`. + if self.count() != other.count() { return false } + var i = 0 + while i < self.count() { + if self[i] != other[i] { return false } + &i += 1 + } + return true + } + +} + +// public conformance UTF8Array: Collection { +public extension UTF8Array { + + /// An index in an UTF8Array. + public typealias Index = Int + + /// A single UTF-8 code unit. + public typealias Element = Int + + public fun start_index() -> Int { + 0 + } + + /// Returns the number of elements in `self`. + public fun count() -> Int { + if is_inline() { + Int(truncating_or_extending: units >> 56) + } else { + unsafe_heap_payload.1.copy() + } + } + + /// Accesses the unit at `position` in `self`. + public subscript(_ position: Int): Int8 { + yield 0 + if is_inline() { + // TODO: uncomment when #1046 is implemented + // precondition((0 <= position) && (position < Int(units >> 56))) + let s = 8 * (6 - position) + yield Int8(truncating_or_extending: units >> s) + } else { + let p = unsafe_heap_payload + // TODO: uncomment when #1046 is implemented + // precondition((0 <= position) && (position < p.1)) + yield p.0.advance(by: position).unsafe[] + } + } + +} From f9853786bb7f40ad400f4589c98398dd6deebf11 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 01:48:04 +0200 Subject: [PATCH 118/157] Implement a consuming unsafe bitcast --- Library/Hylo/Core/Bitcast.hylo | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Library/Hylo/Core/Bitcast.hylo b/Library/Hylo/Core/Bitcast.hylo index 9d1c2f06f..fd46d8acb 100644 --- a/Library/Hylo/Core/Bitcast.hylo +++ b/Library/Hylo/Core/Bitcast.hylo @@ -9,3 +9,9 @@ public subscript unsafe_bitcast(_ value: T): U { yield &p.unsafe[] } } + +/// Returns `value` with its memory representation reinterpreted as a value of type `U`. +public fun unsafe_bitcast(consuming value: T) -> U { + sink let p: PointerToMutable = PointerToMutable(type_punning: mutable_pointer[to: &value]) + return p.unsafe_pointee() +} From 2579d68339738f16a78bf402b781aef34fbad384 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 03:09:01 +0200 Subject: [PATCH 119/157] Move 'UTF8Array' out of the core library The current implementation of `UTF8Array` can't be compiled in no-std mode because it uses `PointerToMutable.deallocate`. --- Library/Hylo/{Core => }/UTF8Array.hylo | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Library/Hylo/{Core => }/UTF8Array.hylo (100%) diff --git a/Library/Hylo/Core/UTF8Array.hylo b/Library/Hylo/UTF8Array.hylo similarity index 100% rename from Library/Hylo/Core/UTF8Array.hylo rename to Library/Hylo/UTF8Array.hylo From 9187bd76b14483b4b6c9884d2ce99c373fdb2290 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 04:00:09 +0200 Subject: [PATCH 120/157] Fix the gathering of traits to which generic type parameters conform --- .../FrontEnd/TypeChecking/TypeChecker.swift | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 241f34c57..b0cad6bec 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -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 } @@ -1337,6 +1346,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 From 819d49619a823ef1bec6dd176b0bf14fb8edb83b Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 04:00:29 +0200 Subject: [PATCH 121/157] Implement 'swap(_:_:)' --- Library/Hylo/Core/Movable.hylo | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Library/Hylo/Core/Movable.hylo b/Library/Hylo/Core/Movable.hylo index 6be7a1cf6..7bcceb159 100644 --- a/Library/Hylo/Core/Movable.hylo +++ b/Library/Hylo/Core/Movable.hylo @@ -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(_ a: inout T, _ b: inout T) { + sink let x = a + &a = b + &b = x +} From a49c64093244196c3fee2ce8ce7514f17a1ffdde Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 04:06:20 +0200 Subject: [PATCH 122/157] Implement 'Array.swap_at(_:_:)' --- Library/Hylo/Array.hylo | 8 ++++++++ Tests/LibraryTests/TestCases/ArrayTests.hylo | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 868df3957..e8914b720 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -90,6 +90,14 @@ public type Array: Deinitializable { &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[]) + } + /// Accesses the element at `position`. /// /// - Requires: `position` is in the range `0 ..< count()`. diff --git a/Tests/LibraryTests/TestCases/ArrayTests.hylo b/Tests/LibraryTests/TestCases/ArrayTests.hylo index a975a83b8..cdbab7f0e 100644 --- a/Tests/LibraryTests/TestCases/ArrayTests.hylo +++ b/Tests/LibraryTests/TestCases/ArrayTests.hylo @@ -16,7 +16,16 @@ fun test_append() { precondition(d[2] == 84) } +fun test_swap_at() { + var a = Array() + &a.append(false) + &a.append(true) + &a.swap_at(0, 1) + precondition(a[0]) +} + public fun main() { test_init_empty() test_append() + test_swap_at() } From b8abd6862ee2c5591477e17f478317838e0319f4 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 04:09:50 +0200 Subject: [PATCH 123/157] Implement 'Array.reverse' --- Library/Hylo/Array.hylo | 13 +++++++++++++ Tests/LibraryTests/TestCases/ArrayTests.hylo | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index e8914b720..fbd2abf4d 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -98,6 +98,19 @@ public type Array: Deinitializable { 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()`. diff --git a/Tests/LibraryTests/TestCases/ArrayTests.hylo b/Tests/LibraryTests/TestCases/ArrayTests.hylo index cdbab7f0e..d18038b3c 100644 --- a/Tests/LibraryTests/TestCases/ArrayTests.hylo +++ b/Tests/LibraryTests/TestCases/ArrayTests.hylo @@ -24,8 +24,21 @@ fun test_swap_at() { precondition(a[0]) } +fun test_reverse() { + var a = Array() + &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() } From 8afd1886434a5c3acbc2e160374184ca4601f608 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 14:11:19 +0200 Subject: [PATCH 124/157] Implement 'ConcreteTypeLayout' --- Sources/CodeGen/LLVM/CodeGenError.swift | 8 +++ Sources/CodeGen/LLVM/ConcreteTypeLayout.swift | 65 +++++++++++++++++++ Sources/CodeGen/LLVM/TypeLowering.swift | 7 -- 3 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 Sources/CodeGen/LLVM/CodeGenError.swift create mode 100644 Sources/CodeGen/LLVM/ConcreteTypeLayout.swift diff --git a/Sources/CodeGen/LLVM/CodeGenError.swift b/Sources/CodeGen/LLVM/CodeGenError.swift new file mode 100644 index 000000000..4df3f522e --- /dev/null +++ b/Sources/CodeGen/LLVM/CodeGenError.swift @@ -0,0 +1,8 @@ +import Core + +/// Traps indicating that `t` is not representable in LLVM. +func notLLVMRepresentable( + _ t: T, file: StaticString = #filePath, line: UInt = #line +) -> Never { + preconditionFailure("'\(t)' is not representable in LLVM", file: (file), line: line) +} diff --git a/Sources/CodeGen/LLVM/ConcreteTypeLayout.swift b/Sources/CodeGen/LLVM/ConcreteTypeLayout.swift new file mode 100644 index 000000000..bee19698e --- /dev/null +++ b/Sources/CodeGen/LLVM/ConcreteTypeLayout.swift @@ -0,0 +1,65 @@ +import Core +import IR +import LLVM + +/// The concrete layout of a type, describing the byte offsets of its stored properties. +struct ConcreteTypeLayout { + + /// The contiguous memory footprint of the type's instances, in bytes. + let size: Int + + /// The memory alignment of the type's instances, in bytes. + let alignment: Int + + /// Creates an instance with the given properties. + init(size: Int, alignment: Int) { + self.size = size + self.alignment = alignment + } + + /// Creates the concrete form of the layout `l` of a type defined in `ir`, for use in `m`. + /// + /// - Requires: `l.type` is representable in LLVM. + init(_ l: AbstractTypeLayout, definedIn ir: IR.Program, forUseIn m: inout LLVM.Module) { + if let u = BuiltinType(l.type) { + self.init(of: u, forUseIn: &m) + } else { + let u = ir.llvm(l.type, in: &m) + self.init(size: m.layout.storageSize(of: u), alignment: m.layout.preferredAlignment(of: u)) + } + } + + /// Creates the layout of `t`, which is defined in `ir`, for use in `m`. + /// + /// - Requires: `t` is representable in LLVM. + init(of t: AnyType, definedIn ir: IR.Program, forUseIn m: inout LLVM.Module) { + self.init(AbstractTypeLayout(of: t, definedIn: ir.base), definedIn: ir, forUseIn: &m) + } + + /// Creates the layout of `t` for use in `m`. + /// + /// - Requires: `t` is not `.module`. + init(of t: BuiltinType, forUseIn m: inout LLVM.Module) { + switch t { + case .ptr: + let s = m.layout.storageSize(of: m.ptr) + self.init(size: s, alignment: min(s, 8)) + case .i(let width): + let s = max(1, width / 8) + self.init(size: s, alignment: min(s, 8)) + case .word: + self.init(of: .ptr, forUseIn: &m) + case .float16: + self.init(size: 2, alignment: 2) + case .float32: + self.init(size: 4, alignment: 4) + case .float64: + self.init(size: 8, alignment: 8) + case .float128: + self.init(size: 16, alignment: 8) + case .module: + notLLVMRepresentable(^t) + } + } + +} diff --git a/Sources/CodeGen/LLVM/TypeLowering.swift b/Sources/CodeGen/LLVM/TypeLowering.swift index 66e061857..9f26a4b86 100644 --- a/Sources/CodeGen/LLVM/TypeLowering.swift +++ b/Sources/CodeGen/LLVM/TypeLowering.swift @@ -144,10 +144,3 @@ extension IR.Program { } } - -/// Traps indicating that `t` is not representable in LLVM. -private func notLLVMRepresentable( - _ t: T, file: StaticString = #filePath, line: UInt = #line -) -> Never { - preconditionFailure("'\(t)' is not representable in LLVM", file: (file), line: line) -} From e259e7d644865bdd2d04bce293443ad370901cee Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 14:11:46 +0200 Subject: [PATCH 125/157] Refactor the computation of built-in type layouts --- Sources/CodeGen/LLVM/Transpilation.swift | 43 ++++++++---------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index 776d47ea8..46e5fd293 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -298,9 +298,13 @@ extension LLVM.Module { ) { setLinkage(.linkOnce, for: instance) - let layout = memoryLayout(of: t, from: ir) + let layout = ConcreteTypeLayout(of: ^t, definedIn: ir, forUseIn: &self) let v = LLVM.StructType(instance.valueType)!.constant( - aggregating: [layout.size, layout.preferredAlignment, ptr.null], + aggregating: [ + word().constant(layout.size), + word().constant(layout.alignment), + ptr.null + ], in: &self) setInitializer(v, for: instance) @@ -317,15 +321,19 @@ extension LLVM.Module { if m.id != ir.base.module(containing: t.decl) { return } // If `t` is generic, its metatype is only a stub. - let layout: LLVMMemoryLayout + let layout: ConcreteTypeLayout if !ir.base[t.decl].genericParameters.isEmpty { - layout = .init(size: word().zero, preferredAlignment: word().zero) + layout = ConcreteTypeLayout(size: 0, alignment: 0) } else { - layout = memoryLayout(of: t, from: ir) + layout = ConcreteTypeLayout(of: ^t, definedIn: ir, forUseIn: &self) } let v = LLVM.StructType(instance.valueType)!.constant( - aggregating: [layout.size, layout.preferredAlignment, ptr.null], + aggregating: [ + word().constant(layout.size), + word().constant(layout.alignment), + ptr.null + ], in: &self) setInitializer(v, for: instance) @@ -345,18 +353,6 @@ extension LLVM.Module { return instance } - /// Returns the memory layout of `t`, which is a canonical type in `ir`. - /// - /// - Requires: `t` is representable in LLVM. - private mutating func memoryLayout( - of t: T, from ir: IR.Program - ) -> LLVMMemoryLayout { - let u = ir.llvm(t, in: &self) - return .init( - size: word().constant(layout.storageSize(of: u)), - preferredAlignment: word().constant(layout.preferredAlignment(of: u))) - } - /// Returns the LLVM IR value of `t` used in `m` in `ir`. private mutating func transpiledTrait( _ t: TraitType, usedIn m: IR.Module, from ir: IR.Program @@ -1192,14 +1188,3 @@ private struct LambdaContents { let environment: [LLVM.IRValue] } - -/// The memory layout of a Hylo type represented in LLVM. -private struct LLVMMemoryLayout { - - /// The contiguous memory footprint of the type's instances, in bytes. - let size: LLVM.IRValue - - /// The preferred memory alignment of the `T`'s instances, in bytes. - let preferredAlignment: LLVM.IRValue - -} From 253fa8e7cf5431505accdcd540a46c0e69d8308e Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 14:11:56 +0200 Subject: [PATCH 126/157] Fix typo --- Sources/IR/AbstractTypeLayout.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/IR/AbstractTypeLayout.swift b/Sources/IR/AbstractTypeLayout.swift index 3b52a2450..072e8ffe5 100644 --- a/Sources/IR/AbstractTypeLayout.swift +++ b/Sources/IR/AbstractTypeLayout.swift @@ -1,7 +1,7 @@ import Core import FrontEnd -/// The astract layout of a type, describing the relative offsets of its stored properties. +/// The abstract layout of a type, describing the relative offsets of its stored properties. public struct AbstractTypeLayout { /// The name and type of a stored property. From 0ad8afc2e433e956397ec26d191c12bcf9316f15 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 22:36:41 +0200 Subject: [PATCH 127/157] Ensure built-in wrappers have the expected alignment This commit is implementing a hack to make sure built-in wrappers (e.g., `Int8`) have the alignment of the value they wrap. --- Sources/CodeGen/LLVM/ConcreteTypeLayout.swift | 13 ++++++++++--- Tests/EndToEndTests/TestCases/MemoryLayout.hylo | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Sources/CodeGen/LLVM/ConcreteTypeLayout.swift b/Sources/CodeGen/LLVM/ConcreteTypeLayout.swift index bee19698e..b1d978a40 100644 --- a/Sources/CodeGen/LLVM/ConcreteTypeLayout.swift +++ b/Sources/CodeGen/LLVM/ConcreteTypeLayout.swift @@ -21,9 +21,16 @@ struct ConcreteTypeLayout { /// /// - Requires: `l.type` is representable in LLVM. init(_ l: AbstractTypeLayout, definedIn ir: IR.Program, forUseIn m: inout LLVM.Module) { - if let u = BuiltinType(l.type) { - self.init(of: u, forUseIn: &m) - } else { + switch l.type.base { + case let t as BuiltinType: + self.init(of: t, forUseIn: &m) + + case is ProductType where l.properties.count == 1: + // Note: Hack to get the correct alignment of built-in wrappers until we implement a proper + // layout algorithm (#1067). + self.init(of: l.properties[0].type, definedIn: ir, forUseIn: &m) + + default: let u = ir.llvm(l.type, in: &m) self.init(size: m.layout.storageSize(of: u), alignment: m.layout.preferredAlignment(of: u)) } diff --git a/Tests/EndToEndTests/TestCases/MemoryLayout.hylo b/Tests/EndToEndTests/TestCases/MemoryLayout.hylo index bc515033a..a47eedb70 100644 --- a/Tests/EndToEndTests/TestCases/MemoryLayout.hylo +++ b/Tests/EndToEndTests/TestCases/MemoryLayout.hylo @@ -13,4 +13,7 @@ type Vector2 { public fun main() { let x = Hylo.MemoryLayout.stride() precondition(x == 16) + + precondition(Hylo.MemoryLayout.alignment() == 1) + precondition(Hylo.MemoryLayout.alignment() == 4) } From 2fa25e97f09000cf4f1e4f852ca81f32ae293fea Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 22:39:22 +0200 Subject: [PATCH 128/157] Rename '_val_metatype' to '_hylo_metatype' --- Sources/CodeGen/LLVM/Transpilation.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index 46e5fd293..8114bedac 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -80,7 +80,7 @@ extension LLVM.Module { /// Returns the LLVM type of a metatype instance. private mutating func metatypeType() -> LLVM.StructType { - if let t = type(named: "_val_metatype") { + if let t = type(named: "_hylo_metatype") { return .init(t)! } From 5501c8a392b4b2c43cb70747200328e49bf89c5a Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 09:45:48 +0200 Subject: [PATCH 129/157] Implement a 'print' overload for integers This implementation is temporary. It is meant to help debugging while the necessary components are put in place to support a universal `print` function. --- Library/Hylo/Print.hylo | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Library/Hylo/Print.hylo b/Library/Hylo/Print.hylo index fefe5f60f..fe89ebf83 100644 --- a/Library/Hylo/Print.hylo +++ b/Library/Hylo/Print.hylo @@ -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() + 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 { 97 }))) + } + + // 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) From bb687cabfe60deb84bdaf7dac72b80a0ff2b3e99 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 5 Oct 2023 09:47:32 +0200 Subject: [PATCH 130/157] Fix name resolution for properties with a bound generic type --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 7 +++++++ .../HyloTests/TestCases/TypeChecking/Property.hylo | 14 +++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index b0cad6bec..a28a219de 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -2979,6 +2979,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( @@ -3009,6 +3010,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.f`, 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( diff --git a/Tests/HyloTests/TestCases/TypeChecking/Property.hylo b/Tests/HyloTests/TestCases/TypeChecking/Property.hylo index fc9958a6f..ab849921a 100644 --- a/Tests/HyloTests/TestCases/TypeChecking/Property.hylo +++ b/Tests/HyloTests/TestCases/TypeChecking/Property.hylo @@ -1,12 +1,24 @@ //- typeCheck expecting: success -type A { +type A: Deinitializable { public memberwise init public property x: Int { let { 0 } } } +type B: Deinitializable { + public memberwise init +} + +type C: Deinitializable { + public memberwise init + public property x: B { .new() } +} + fun check(_ x: T) {} public fun main() { check(A().x) + + let a = C().x + check>(a) } From d29af377f0fd92c5d6e085eb0bf84614e5a07d21 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 11:16:16 +0200 Subject: [PATCH 131/157] Implement 'TypedProgram.foreignRepresentation(of:exposedTo:)' --- Sources/FrontEnd/TypedProgram.swift | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Sources/FrontEnd/TypedProgram.swift b/Sources/FrontEnd/TypedProgram.swift index e1540a06b..b3d45068d 100644 --- a/Sources/FrontEnd/TypedProgram.swift +++ b/Sources/FrontEnd/TypedProgram.swift @@ -422,6 +422,28 @@ public struct TypedProgram { site: .empty(at: ast[scopeOfUse].site.first())) } + /// Returns the foreign representation of `t` using its conformance to `ForeignConvertible` in + /// `scopeOfUse`. + /// + /// - Requires: `t` conforms to `ForeignConvertible` in `scopeOfUse`. + public func foreignRepresentation( + of t: AnyType, exposedTo scopeOfUse: AnyScopeID + ) -> AnyType { + let f = ast.coreTrait("ForeignConvertible")! + let d = ast.requirements("ForeignRepresentation", in: f.decl)[0] + + // Since conformances of built-in types are not stored in property maps, we'll exit the loop + // when we assign one to `result`. + var result = t + while let c = conformance(of: result, to: f, exposedTo: scopeOfUse) { + // `d` is an associated type declaration so its implementations must have a metatype. + let i = c.implementations[d]!.decl! + result = MetatypeType(canonical(self[i].type, in: self[d].scope))!.instance + } + + return result + } + /// Returns the scope of the declaration extended by `d`, if any. public func scopeExtended(by d: T.ID) -> AnyScopeID? { var checker = TypeChecker(asContextFor: self) From a27bf7f5c0ba9f1c503e4b0acd2fc02092d0710d Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 11:18:17 +0200 Subject: [PATCH 132/157] Keep track of associated type implementations in conformances --- .../FrontEnd/TypeChecking/TypeChecker.swift | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 241f34c57..2ce52c4fd 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -1018,9 +1018,6 @@ struct TypeChecker { /// Returns a concrete or synthesized implementation of requirement `r` in `concept` for /// `model` exposed to `scopeOfUse`, or `nil` if no such implementation exist. func implementation(of r: AnyDeclID) { - // FIXME: remove me - if r.kind == AssociatedTypeDecl.self { return } - // Note: `t` is used for generating diagnostics, `u` is used for testing equivalences. let t = type(ofMember: r) let u = canonical(t, in: scopeOfExposition) @@ -1048,13 +1045,13 @@ struct TypeChecker { func syntheticImplementation( of requirement: AnyDeclID, typed t: AnyType, named n: Name ) -> SynthesizedFunctionDecl? { - if let k = program.ast.synthesizedKind(of: requirement, definedBy: concept) { - // Note: compiler-known requirement is expected to be well-typed. - let scopeOfDefinition = AnyScopeID(source) ?? program[source].scope - return .init(k, typed: LambdaType(t)!, in: scopeOfDefinition) - } else { + guard let k = program.ast.synthesizedKind(of: requirement, definedBy: concept) else { return nil } + + // Note: compiler-known requirement is assumed to be well-typed. + let scopeOfDefinition = AnyScopeID(source) ?? program[source].scope + return .init(k, typed: LambdaType(t)!, in: scopeOfDefinition) } /// Returns a concrete implementation of `requirement` in `concept`, which has type `t` and @@ -1064,7 +1061,7 @@ struct TypeChecker { ) -> AnyDeclID? { switch requirement.kind { case AssociatedTypeDecl.self: - UNIMPLEMENTED() + return implementation(of: AssociatedTypeDecl.ID(requirement)!) case AssociatedValueDecl.self: UNIMPLEMENTED() @@ -1094,6 +1091,31 @@ struct TypeChecker { } } + /// Returns the implementation of `requirement` in `model` or returns `nil` if no such + /// implementation exist. + /// + /// `requirement` is an associated type of `concept`. + func implementation(of requirement: AssociatedTypeDecl.ID) -> AnyDeclID? { + let n = program[requirement].baseName + let candidates = lookup(n, memberOf: m, exposedTo: scopeOfExposition) + let viable: [AnyDeclID] = candidates.reduce(into: []) { (s, c) in + // Candidate is viable iff it denotes a metatype. + if !(uncheckedType(of: c).base is MetatypeType) { return } + + // Ignore associated type declaration without a default value. + if let d = AssociatedTypeDecl.ID(c), program[d].defaultValue == nil { return } + + s.append(c) + } + + // Exclude associated types if they are other viable candidates. + if viable.count > 1 { + return viable.filter({ $0.kind != AssociatedTypeDecl.self }).uniqueElement + } else { + return viable.uniqueElement + } + } + /// Returns the implementation of `requirement` in `model` or returns `nil` if no such /// implementation exist. /// From f6571064277b85576ddd85aec4ccda600a2e1939 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 11:19:26 +0200 Subject: [PATCH 133/157] Fix the computation of the return types of FFI calls --- Sources/IR/Emitter.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 5484d7f0e..d7609f69a 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -272,8 +272,12 @@ struct Emitter { arguments.append(a) } + // Return type must be foreign convertible unless it is `Void` or `Never`. + let returnType = read(module.functions[f]!.output) { (t) in + t.isVoidOrNever ? t : program.foreignRepresentation(of: t, exposedTo: insertionScope!) + } + // Emit the call to the foreign function. - let returnType = module.functions[f]!.output let foreignResult = insert( module.makeCallFFI( returning: .object(returnType), applying: ast[d].foreignName!, to: arguments, at: site))! @@ -289,7 +293,7 @@ struct Emitter { insert(module.makeReturn(at: site)) default: - let v = emitConvert(foreign: foreignResult, to: returnType, at: site) + let v = emitConvert(foreign: foreignResult, to: module.functions[f]!.output, at: site) emitMove([.set], v, to: returnValue!, at: site) emitDeallocTopFrame(at: site) insert(module.makeReturn(at: site)) @@ -1984,6 +1988,8 @@ struct Emitter { let r = ast.requirements( Name(stem: "init", labels: ["foreign_value"]), in: foreignConvertible.decl)[0] + // TODO: Handle cases where the foreign representation of `t` is not built-in. + // Store the foreign representation in memory to call the converter. let source = emitAllocStack(for: module.type(of: foreign).ast, at: site) emitStore(value: foreign, to: source, at: site) @@ -2025,7 +2031,7 @@ struct Emitter { of: t.ast, to: foreignConvertible, exposedTo: insertionScope!)! let r = ast.requirements("foreign_value", in: foreignConvertible.decl)[0] - // TODO: Handle cases where the foreign representation of `t` is not built-in + // TODO: Handle cases where the foreign representation of `t` is not built-in. switch foreignConvertibleConformance.implementations[r]! { case .concrete(let m): From 18e91578e34e96cf9de19bff79507b39ee712078 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 11:20:30 +0200 Subject: [PATCH 134/157] Modify the signature of 'malloc' to accept 'Int' instead of 'Int32' --- Sources/CodeGen/LLVM/Transpilation.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index 8114bedac..cf7e18b33 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -122,7 +122,7 @@ extension LLVM.Module { let f = declareFunction( "malloc", - FunctionType(from: [i32], to: ptr, in: &self)) + FunctionType(from: [word()], to: ptr, in: &self)) addAttribute(named: .noundef, to: f.parameters[0]) addAttribute(named: .noalias, to: f.returnValue) From 9de5fb595be610f43cff63c5ab16f68759056222 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 11:54:38 +0200 Subject: [PATCH 135/157] Implement 'PointerToMutable.init(bit_pattern:)' --- Library/Hylo/Core/PointerToMutable.hylo | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Library/Hylo/Core/PointerToMutable.hylo b/Library/Hylo/Core/PointerToMutable.hylo index 1e9a7c5aa..18bc98c2c 100644 --- a/Library/Hylo/Core/PointerToMutable.hylo +++ b/Library/Hylo/Core/PointerToMutable.hylo @@ -33,6 +33,11 @@ public type PointerToMutable: Regular { &base = p.base } + /// Creates an instance with the same memory representation as `address`. + public init(bit_pattern address: Int) { + &self.base = Builtin.inttoptr_word(address.value) + } + /// Returns `self` offset forward by `n` array elements of `Pointee` type. public fun advance(by n: Int) -> Self { let offset_in_bytes = MemoryLayout.stride() * n From c3405d5a04fb65feffc5b75a07237b55792b68af Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 11:54:54 +0200 Subject: [PATCH 136/157] Implement 'PointerToMutable.infix!=' --- Library/Hylo/Core/PointerToMutable.hylo | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Library/Hylo/Core/PointerToMutable.hylo b/Library/Hylo/Core/PointerToMutable.hylo index 18bc98c2c..cca288971 100644 --- a/Library/Hylo/Core/PointerToMutable.hylo +++ b/Library/Hylo/Core/PointerToMutable.hylo @@ -86,6 +86,11 @@ public conformance PointerToMutable: Equatable { Bool(value: Builtin.icmp_eq_ptr(base, other.base)) } + /// Returns `false` iff `other` has an equivalent value. + public fun infix!= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ne_ptr(base, other.base)) + } + } public extension PointerToMutable where Pointee: Movable { From 0d3da3244a579c1558b7c6c72bb75f5d5c5d2b2c Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 11:55:31 +0200 Subject: [PATCH 137/157] Add an FFI for 'malloc' in the standard library --- Library/Hylo/LibC.hylo | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Library/Hylo/LibC.hylo b/Library/Hylo/LibC.hylo index d1ce5d176..55a582c4c 100644 --- a/Library/Hylo/LibC.hylo +++ b/Library/Hylo/LibC.hylo @@ -1,4 +1,4 @@ -/// Allocate `size` bytes of uninitialized storage whose alignment is specified by `alignment`. +/// Allocates `size` bytes of uninitialized storage whose alignment is specified by `alignment`. /// /// - Parameters: /// - alignment: The alignment of the allocated memory. Must be a valid alignment supported by @@ -7,6 +7,10 @@ @ffi("aligned_alloc") fun aligned_alloc(_ alignment: Int, _ size: Int) -> CVoidPointer +/// Allocates `size` bytes of uninitialized storage. +@ffi("malloc") +fun malloc(_ size: Int) -> CVoidPointer + /// Deallocates the block of memory pointed at by `pointer`. @ffi("free") fun free(_ pointer: CVoidPointer) From 001a14b0325c269740c4ffd076eeb4810df2c81e Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 11:55:56 +0200 Subject: [PATCH 138/157] Mark TODO --- Library/Hylo/Pointers+DynamicAllocation.hylo | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Library/Hylo/Pointers+DynamicAllocation.hylo b/Library/Hylo/Pointers+DynamicAllocation.hylo index a13332d39..841d3dc76 100644 --- a/Library/Hylo/Pointers+DynamicAllocation.hylo +++ b/Library/Hylo/Pointers+DynamicAllocation.hylo @@ -9,8 +9,7 @@ public extension PointerToMutable where Pointee == Never { public extension PointerToMutable where Pointee == Never { - // This function should be replaced with the normal advance function once #989 is fixed. - + // TODO: This function should be replaced with the normal advance function once #989 is fixed. /// Returns a pointer advanced by `offset_in_bytes`. public fun advance(by_bytes offset_in_bytes: Int) -> Self { let offset_in_bytes = 1 * offset_in_bytes From 1dd5bb10e62fa0c3e2a602aac9bc9bfbe33c3cba Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 11:56:32 +0200 Subject: [PATCH 139/157] Implement 'hylo_aligned_alloc' and 'hylo_aligned_free' --- Library/Hylo/Heap.hylo | 41 +++++++++++++++++++++ Tests/LibraryTests/TestCases/HeapTests.hylo | 12 ++++++ 2 files changed, 53 insertions(+) create mode 100644 Library/Hylo/Heap.hylo create mode 100644 Tests/LibraryTests/TestCases/HeapTests.hylo diff --git a/Library/Hylo/Heap.hylo b/Library/Hylo/Heap.hylo new file mode 100644 index 000000000..52aac31bf --- /dev/null +++ b/Library/Hylo/Heap.hylo @@ -0,0 +1,41 @@ +/// Allocates `size` bytes of uninitialized storage whose alignment is specified by `alignment`. +/// +/// This function is similar to `aligned_alloc` from libc, except that it accepts any non-negative +/// `size` whereas `aligned_alloc` requires `size` to be an integral multiple of `alignment`. Use +/// this function when you need memory aligned at a specific boundary. Othwewise, use `malloc`. +/// +/// Memory allocated with `hylo_aligned_alloc` must be deallocated with `hylo_aligned_free`. +/// +/// - Parameters: +/// - alignment: The alignment of the allocated memory. Must be a power of 2. +/// - size: The number of bytes to allocate. Must be greater than 0. +/// - Returns: a pointer to newly allocated memory. +public fun hylo_aligned_alloc(_ alignment: Int, _ size: Int) -> MemoryAddress { + + // We allocate `size` plus the storage for a pointer and some padding to satisfy the alignment + // requirement. The pointer to the allocated memory `p` is aligned to `alignment` and assigned + // to `r`. The value of `p` is stored "behind" `r` for use in `hylo_aligned_alloc`. + + let s = MemoryLayout.size() + let a = alignment - 1 + let p = MemoryAddress(base: malloc(s + a + size).base) + precondition(p != .null(), "out of memory") + + // precondition(((Int(bit_pattern: p) + s + a) & ~a) == (Int(bit_pattern: p) + 8)) + let r = MemoryAddress(bit_pattern: (Int(bit_pattern: p) + s + a) & ~a) + let h = r.advance(by_bytes: -s) + PointerToMutable(type_punning: h).unsafe_initialize_pointee(p.copy()) + return r +} + +/// Deallocates memory allocated by `hylo_aligned_alloc`. +/// +/// No operation is performed if `pointer == .null()`. +public fun hylo_aligned_free(_ pointer: MemoryAddress) { + if pointer == .null() { return } + + let s = MemoryLayout.size() + let h = pointer.advance(by_bytes: -s) + let p = PointerToMutable(type_punning: h).unsafe[].copy() + free(CVoidPointer(base: p.base)) +} diff --git a/Tests/LibraryTests/TestCases/HeapTests.hylo b/Tests/LibraryTests/TestCases/HeapTests.hylo new file mode 100644 index 000000000..0e2a658df --- /dev/null +++ b/Tests/LibraryTests/TestCases/HeapTests.hylo @@ -0,0 +1,12 @@ +//- compileAndRun expecting: success + +public fun main() { + let p = hylo_aligned_alloc(16, 8) + precondition(Int(bit_pattern: p) % 16 == 0, "pointer isn't aligned at 16") + + let q = PointerToMutable(type_punning: p) + q.unsafe_initialize_pointee(42) + precondition(q.unsafe[] == 42, "store/load failed") + + hylo_aligned_free(p) +} From d6c2033507566e8de6f51ca37253f415019f6a07 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 12:39:06 +0200 Subject: [PATCH 140/157] Ignore associated type declarations during conformance lowering --- Sources/IR/Module.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/IR/Module.swift b/Sources/IR/Module.swift index 5d7515dd3..563a9563c 100644 --- a/Sources/IR/Module.swift +++ b/Sources/IR/Module.swift @@ -531,7 +531,7 @@ public struct Module { /// Returns the lowered form of `c`. private mutating func loweredConformance(_ c: Core.Conformance) -> IR.Conformance { var implementations = IR.Conformance.ImplementationMap() - for (r, i) in c.implementations { + for (r, i) in c.implementations where (r.kind != AssociatedTypeDecl.self) { let f = demandDeclaration(lowering: i)! implementations[r] = .function(FunctionReference(to: f, in: self)) } From dd3c12cdcaf890f6e3d6ac98079afce7cef737ac Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 12:39:38 +0200 Subject: [PATCH 141/157] Apply swift-format --- Sources/CodeGen/LLVM/Transpilation.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index cf7e18b33..6ea2cb26c 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -303,7 +303,7 @@ extension LLVM.Module { aggregating: [ word().constant(layout.size), word().constant(layout.alignment), - ptr.null + ptr.null, ], in: &self) @@ -332,7 +332,7 @@ extension LLVM.Module { aggregating: [ word().constant(layout.size), word().constant(layout.alignment), - ptr.null + ptr.null, ], in: &self) From a1aa6bae562bf25ec12204614c7d58e9c731fb83 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 17:28:46 +0200 Subject: [PATCH 142/157] Use 'hylo_aligned_alloc' instead of 'aligned_alloc' --- Library/Hylo/Pointers+DynamicAllocation.hylo | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Library/Hylo/Pointers+DynamicAllocation.hylo b/Library/Hylo/Pointers+DynamicAllocation.hylo index 841d3dc76..29fe31d64 100644 --- a/Library/Hylo/Pointers+DynamicAllocation.hylo +++ b/Library/Hylo/Pointers+DynamicAllocation.hylo @@ -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) } } @@ -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.alignment(), MemoryLayout.stride() * count).base) + return Self.new(base: hylo_aligned_alloc( + MemoryLayout.alignment(), + MemoryLayout.stride() * count).base) } /// Deallocates the memory previously allocated at `self`. public fun deallocate() { - free(CVoidPointer(base: base)) + hylo_aligned_free(MemoryAddress(base: base)) } } From 44d1c69edae654c5947cc1179bb7bc942860f53d Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 17:29:10 +0200 Subject: [PATCH 143/157] Fix ASCII offset in 'print()' --- Library/Hylo/Print.hylo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Hylo/Print.hylo b/Library/Hylo/Print.hylo index fe89ebf83..5f1278fb2 100644 --- a/Library/Hylo/Print.hylo +++ b/Library/Hylo/Print.hylo @@ -19,7 +19,7 @@ public fun print(_ item: Int, radix: Int = 10, terminator: String = "\n") { let i = v % radix &v /= radix // Note: 48 = "0" and 97 = "a" - &a.append((Int8(truncating_or_extending: i + if i < 10 { 48 } else { 97 }))) + &a.append((Int8(truncating_or_extending: i + if i < 10 { 48 } else { 87 }))) } // Note: 45 = "-" From 389c05cc9c5177e7a2b69399037698d91cd4266a Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 22:20:13 +0200 Subject: [PATCH 144/157] Avoid needlessly re-evaluating type name expressions --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index d49970bdc..c9dfe0a78 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -2123,6 +2123,10 @@ struct TypeChecker { /// Evaluates and returns the value of `e`, which is a type annotation. private mutating func evalTypeAnnotation(_ e: NameExpr.ID) -> AnyType { + if let t = cache.local.exprType[e] { + return t + } + let resolution = resolve(e, withNonNominalPrefix: { (me, p) in me.evalQualification(of: p) }) switch resolution { case .done(let prefix, let suffix): From 3f6a24588d77ce9a3e1fb01805e20fb871b1b25c Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 22:21:13 +0200 Subject: [PATCH 145/157] Rename 'MemberLookupKey' to 'TypeLookupKey' --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index c9dfe0a78..a3f49c715 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -2488,7 +2488,7 @@ struct TypeChecker { break } - let key = Cache.MemberLookupKey(nominalScope, in: scopeOfUse) + let key = Cache.TypeLookupKey(nominalScope, in: scopeOfUse) if let m = cache.scopeToMembers[key]?[stem] { return m } @@ -4770,8 +4770,8 @@ struct TypeChecker { /// A lookup table. typealias LookupTable = [String: Set] - /// A key in a member lookup table. - typealias MemberLookupKey = ScopedValue + /// A key in a type lookup table. + typealias TypeLookupKey = ScopedValue /// The local instance being type checked. private(set) var local: TypedProgram @@ -4798,7 +4798,7 @@ struct TypeChecker { /// /// This map serves as cache for `lookup(_:memberOf:exposedTo)`. At no point is it guaranteed /// to be complete. - var scopeToMembers: [MemberLookupKey: LookupTable] = [:] + var scopeToMembers: [TypeLookupKey: LookupTable] = [:] /// A map from lexical scope to the names introduced in it. /// From 3cd60f03da46b67cd5066816953cfcd74e456609 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 22:21:42 +0200 Subject: [PATCH 146/157] Add a memo cache for 'conformedTraits(of:in:)' --- .../FrontEnd/TypeChecking/TypeChecker.swift | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index a3f49c715..54f764e97 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -123,24 +123,33 @@ struct TypeChecker { /// Returns the traits to which `t` is declared conforming in `scopeOfUse`. mutating func conformedTraits(of t: AnyType, in scopeOfUse: AnyScopeID) -> Set { + let key = Cache.TypeLookupKey(t, in: scopeOfUse) + if let r = cache.typeToConformedTraits[key] { + return r + } + + var result: Set switch t.base { case let u as BoundGenericType: - return conformedTraits(of: u.base, in: scopeOfUse) + result = conformedTraits(of: u.base, in: scopeOfUse) case let u as BuiltinType: - return conformedTraits(of: u, in: scopeOfUse) + result = conformedTraits(of: u, in: scopeOfUse) case let u as GenericTypeParameterType: - return conformedTraits(of: u, in: scopeOfUse) + result = conformedTraits(of: u, in: scopeOfUse) case let u as ProductType: - return conformedTraits(of: u, in: scopeOfUse) + result = conformedTraits(of: u, in: scopeOfUse) case let u as TraitType: - return conformedTraits(of: u, in: scopeOfUse) + result = conformedTraits(of: u, in: scopeOfUse) case let u as TypeAliasType: - return conformedTraits(of: u.resolved, in: scopeOfUse) + result = conformedTraits(of: u.resolved, in: scopeOfUse) case let u as WitnessType: - return conformedTraits(of: u, in: scopeOfUse) + result = conformedTraits(of: u, in: scopeOfUse) default: - return conformedTraits(declaredInExtensionsOf: t, exposedTo: scopeOfUse) + result = conformedTraits(declaredInExtensionsOf: t, exposedTo: scopeOfUse) } + + cache.typeToConformedTraits[key] = result + return result } /// Returns the traits to which `t` is declared conforming in `scopeOfUse`. @@ -4805,6 +4814,11 @@ struct TypeChecker { /// This map serves as cache for `names(introducedIn:)`. var scopeToNames: [AnyScopeID: LookupTable] = [:] + /// A map from type to the traits to which in conforms in a given scope. + /// + /// This map serves as cache for `conformedTraits(of:in:)`. + var typeToConformedTraits: [TypeLookupKey: Set] = [:] + /// Creates an instance for memoizing type checking results in `local` and comminicating them /// to concurrent type checkers using `shared`. init(local: TypedProgram, shared: SharedMutable? = nil) { From b2d35afb9c67e991192b402485a968fba36abcac Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 6 Oct 2023 23:04:48 +0200 Subject: [PATCH 147/157] Add a memo cache for 'extensions(of:exposedTo:)' --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 54f764e97..36772460b 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -2726,6 +2726,11 @@ struct TypeChecker { private mutating func extensions( of subject: AnyType, exposedTo scopeOfUse: AnyScopeID ) -> [AnyDeclID] { + let key = Cache.TypeLookupKey(subject, in: scopeOfUse) + if let r = cache.typeToExtensions[key] { + return r + } + let subject = canonical(subject, in: scopeOfUse) var matches: [AnyDeclID] = [] var root: ModuleDecl.ID? = nil @@ -2756,6 +2761,7 @@ struct TypeChecker { reduce(decls: symbols, extending: subject, in: scopeOfUse, into: &matches) } + cache.typeToExtensions[key] = matches return matches } @@ -4819,6 +4825,11 @@ struct TypeChecker { /// This map serves as cache for `conformedTraits(of:in:)`. var typeToConformedTraits: [TypeLookupKey: Set] = [:] + /// A map from type to its extensions in a given scope. + /// + /// This map serves as cache for `extensions(of:exposedTo:)`. + var typeToExtensions: [TypeLookupKey: [AnyDeclID]] = [:] + /// Creates an instance for memoizing type checking results in `local` and comminicating them /// to concurrent type checkers using `shared`. init(local: TypedProgram, shared: SharedMutable? = nil) { From 97b1ee93b4b8940021bc985fe8bf3456f2af7be5 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sat, 7 Oct 2023 12:07:54 +0200 Subject: [PATCH 148/157] Fix typo --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 36772460b..22100e3b0 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -3003,7 +3003,7 @@ struct TypeChecker { // Gather declarations qualified by `parent` if it isn't `nil` or unqualified otherwise. let matches = lookup(name, memberOf: context?.type, exposedTo: scopeOfUse) - // Resolve compilerKnown type aliases if no match was found. + // Resolve compiler-known type aliases if no match was found. if matches.isEmpty { if context == nil { return resolve(compilerKnownAlias: name, specializedBy: arguments, exposedTo: scopeOfUse) From f122cffae2a745fb3d5e493df01374443c434cf7 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sat, 7 Oct 2023 21:38:51 +0200 Subject: [PATCH 149/157] Fix name lookup in extensions --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 22100e3b0..c4db7b566 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -2518,7 +2518,10 @@ struct TypeChecker { matches = [] } - matches.formUnion(lookup(stem, inExtensionsOf: nominalScope, exposedTo: scopeOfUse)) + if matches.allSatisfy(\.isOverloadable) { + matches.formUnion(lookup(stem, inExtensionsOf: nominalScope, exposedTo: scopeOfUse)) + } + return matches } From 27669873c592363c8d096a26fd60f2985aac502a Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sun, 8 Oct 2023 12:58:00 +0200 Subject: [PATCH 150/157] Improve docs --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index c4db7b566..23aeaa3b1 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -354,8 +354,8 @@ struct TypeChecker { } } - /// Returns the type checking constraint anchored at `origin` that spedcializes `generic` by - /// applying `self.specialize` on its types for `specialization` in `scopeOfUse`. + /// Returns the type checking constraint that specializes `generic` for `specialization` in + /// `scopeOfUse`, anchoring it at `origin`. private mutating func specialize( _ generic: GenericConstraint, for specialization: GenericArguments, in scopeOfUse: AnyScopeID, origin: ConstraintOrigin @@ -1391,7 +1391,9 @@ struct TypeChecker { } } - /// Insert's `d`'s constraints in `e`. + /// Inserts `d`'s constraints in `e`. + /// + /// `e` is the environment in which `d` is introduced. private mutating func insertConstraints( of d: AssociatedTypeDecl.ID, in e: inout GenericEnvironment ) { @@ -1401,7 +1403,9 @@ struct TypeChecker { } } - /// Insert's `d`'s constraints in `e`. + /// Inserts `d`'s constraints in `e`. + /// + /// `e` is the environment in which `d` is introduced. private mutating func insertConstraints( of d: AssociatedValueDecl.ID, in e: inout GenericEnvironment ) { @@ -1411,6 +1415,9 @@ struct TypeChecker { } /// Inserts the constraints declared as `p`'s annotations in `e`. + /// + /// `p` is a generic parameter, associated type, or associated value declaration. `e` is the + /// environment in which `p` is introduced. private mutating func insertAnnotatedConstraints( on p: T.ID, in e: inout GenericEnvironment ) { From 00a5380096752125c66ad3d9886d4e4ec9811227 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sun, 8 Oct 2023 12:58:58 +0200 Subject: [PATCH 151/157] Fix the gathering of constraints on associated type declarations --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 23aeaa3b1..670b37f76 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -1421,11 +1421,9 @@ struct TypeChecker { private mutating func insertAnnotatedConstraints( on p: T.ID, in e: inout GenericEnvironment ) { + // TODO: Constraints on generic value parameters let t = uncheckedType(of: p) - - // TODO: Value constraints - guard let lhs = MetatypeType(t)?.instance, lhs.base is GenericTypeParameterType - else { return } + guard let lhs = MetatypeType(t)?.instance else { return } // Synthesize sugared conformance constraint, if any. for (n, t) in evalTraitComposition(program[p].conformances) { @@ -3401,8 +3399,9 @@ struct TypeChecker { for s in program.scopes(from: program[d].scope) { if s == lca { break } if let e = environment(of: s) { - for c in e.constraints { - result.insert(specialize(c, for: specialization, in: scopeOfUse, origin: origin)) + for g in e.constraints { + let c = specialize(g, for: specialization, in: scopeOfUse, origin: origin) + result.insert(c) } } } From 33bf7b58a3047381ff518c5e2a51192ed59f5a35 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sun, 8 Oct 2023 12:59:34 +0200 Subject: [PATCH 152/157] Enforce associated type constraints --- .../FrontEnd/TypeChecking/TypeChecker.swift | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 670b37f76..41218150a 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -994,11 +994,10 @@ struct TypeChecker { (s.kind == TranslationUnit.self) ? program[s].scope : s } - // TODO: Verify requirement constraints - // TODO: Use arguments to bound generic types as constraints - var m = canonical(model, in: scopeOfExposition) - let arguments: GenericArguments = [program[concept.decl].receiver: m] + let traitReceiverToModel: GenericArguments = [program[concept.decl].receiver: m] + + // TODO: Use arguments to bound generic types as constraints if let b = BoundGenericType(m) { m = b.base } @@ -1014,7 +1013,7 @@ struct TypeChecker { implementation(of: r) } - if !conformanceDiagnostics.isEmpty { + if !conformanceDiagnostics.isEmpty || !checkRequirementConstraints() { report(.error(model, doesNotConformTo: concept, at: site, because: conformanceDiagnostics)) return nil } @@ -1030,7 +1029,24 @@ struct TypeChecker { /// in `scopeOfUse`, or `nil` no such type can be constructed. func type(ofMember m: AnyDeclID) -> AnyType { let t = uncheckedType(of: m) - return specialize(t, for: arguments, in: scopeOfExposition) + return specialize(t, for: traitReceiverToModel, in: scopeOfExposition) + } + + /// Checks whether the constraints on the requirements of `concept` are satisfied by `model` in + /// `scopeOfuse`, reporting diagnostics in `conformanceDiagnostics`. + func checkRequirementConstraints() -> Bool { + var obligations = ProofObligations(scope: scopeOfExposition) + + let e = environment(of: concept.decl) + for g in e.constraints { + let c = specialize( + g, for: traitReceiverToModel, in: scopeOfExposition, + origin: .init(.structural, at: site)) + obligations.insert(c) + } + + let s = discharge(obligations, relatedTo: source) + return s.isSound } /// Returns a concrete or synthesized implementation of requirement `r` in `concept` for @@ -1116,22 +1132,16 @@ struct TypeChecker { func implementation(of requirement: AssociatedTypeDecl.ID) -> AnyDeclID? { let n = program[requirement].baseName let candidates = lookup(n, memberOf: m, exposedTo: scopeOfExposition) + + // Candidates are viable iff they declare a metatype and have a definition. let viable: [AnyDeclID] = candidates.reduce(into: []) { (s, c) in - // Candidate is viable iff it denotes a metatype. if !(uncheckedType(of: c).base is MetatypeType) { return } - - // Ignore associated type declaration without a default value. if let d = AssociatedTypeDecl.ID(c), program[d].defaultValue == nil { return } - s.append(c) } - // Exclude associated types if they are other viable candidates. - if viable.count > 1 { - return viable.filter({ $0.kind != AssociatedTypeDecl.self }).uniqueElement - } else { - return viable.uniqueElement - } + // Conformance is ambiguous if there are multiple viable candidates. + return viable.uniqueElement } /// Returns the implementation of `requirement` in `model` or returns `nil` if no such From b3d66bb31e226dfef2813d25a907c8fe51573762 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 10 Oct 2023 13:38:59 +0200 Subject: [PATCH 153/157] Simplify test --- Tests/HyloTests/TestCases/TypeChecking/Skolemization.hylo | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Tests/HyloTests/TestCases/TypeChecking/Skolemization.hylo b/Tests/HyloTests/TestCases/TypeChecking/Skolemization.hylo index 0b68a063b..0ff3aa5a9 100644 --- a/Tests/HyloTests/TestCases/TypeChecking/Skolemization.hylo +++ b/Tests/HyloTests/TestCases/TypeChecking/Skolemization.hylo @@ -17,9 +17,7 @@ extension A { typealias B = A -trait T {} - -conformance B: T { +extension B { public fun h() { let p = self check>(p) From acac6d748740c61b81674349f787a46e9b9ad84b Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 10 Oct 2023 13:39:36 +0200 Subject: [PATCH 154/157] Make 'UInt8' conform to 'Equatable' and 'Comparable' --- Library/Hylo/Core/Numbers/Integers/UInt8.hylo | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Library/Hylo/Core/Numbers/Integers/UInt8.hylo b/Library/Hylo/Core/Numbers/Integers/UInt8.hylo index cee1987a0..4d95b76a8 100644 --- a/Library/Hylo/Core/Numbers/Integers/UInt8.hylo +++ b/Library/Hylo/Core/Numbers/Integers/UInt8.hylo @@ -28,3 +28,35 @@ public conformance UInt8: Copyable { } } + +public conformance UInt8: Equatable { + + public fun infix== (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_eq_i8(value, other.value)) + } + + public fun infix!= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ne_i8(value, other.value)) + } + +} + +public conformance UInt8: Comparable { + + public fun infix< (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ult_i8(value, other.value)) + } + + public fun infix<= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ule_i8(value, other.value)) + } + + public fun infix> (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ugt_i8(value, other.value)) + } + + public fun infix>= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_uge_i8(value, other.value)) + } + +} From b7ce3a5598f2c5ddea5aa4215b5e5e03bc8ead07 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 10 Oct 2023 13:39:51 +0200 Subject: [PATCH 155/157] Fix typo --- Sources/Core/Diagnostic.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Core/Diagnostic.swift b/Sources/Core/Diagnostic.swift index 342fc0e3d..235c2a36e 100644 --- a/Sources/Core/Diagnostic.swift +++ b/Sources/Core/Diagnostic.swift @@ -56,7 +56,7 @@ public struct Diagnostic: Hashable { Diagnostic(level: .error, message: message, site: site, notes: notes) } - /// Returns a warning with the given `message` highlighting `range`.. + /// Returns a warning with the given `message` highlighting `range`. /// /// - Precondition: elements of `notes` have `self.level == .note` public static func warning( From b3a045aface734a1603f7d0b74dbfd84f70760b5 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Tue, 10 Oct 2023 16:14:05 -0700 Subject: [PATCH 156/157] Remove the use of github actions cache (#1075) Apparently it's a net loss: https://github.com/KyleMayes/install-llvm-action/commit/878985d084d4991346a5f6f26eae447ddc16457a --- .github/workflows/build-and-test.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 3c662a6e2..342d21a6c 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -54,13 +54,6 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: actions/cache@v3 - with: - path: .build - key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} - restore-keys: | - ${{ runner.os }}-spm- - - name: Build and Test uses: devcontainers/ci@v0.3 with: From 7b7365458d51e06b4b61f95d6de771d745821dfa Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Tue, 10 Oct 2023 16:14:18 -0700 Subject: [PATCH 157/157] Remove github action cache (#1076) Apparently it's a net loss: https://github.com/KyleMayes/install-llvm-action/commit/878985d084d4991346a5f6f26eae447ddc16457a --- .github/workflows/doc-extraction.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/doc-extraction.yml b/.github/workflows/doc-extraction.yml index b18183209..741a9af28 100644 --- a/.github/workflows/doc-extraction.yml +++ b/.github/workflows/doc-extraction.yml @@ -30,13 +30,6 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: actions/cache@v3 - with: - path: .build - key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} - restore-keys: | - ${{ runner.os }}-spm- - - name: Setup swift uses: swift-actions/setup-swift@v1 with: