Skip to content

Commit

Permalink
Merge pull request #1357 from hylo-lang/fix-capture-detection
Browse files Browse the repository at this point in the history
Fix capture detection
  • Loading branch information
kyouko-taiga authored Feb 13, 2024
2 parents c54a236 + f1da982 commit 3b5ebb4
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 18 deletions.
16 changes: 3 additions & 13 deletions Sources/Core/AST/AST+Walk.swift
Original file line number Diff line number Diff line change
Expand Up @@ -236,16 +236,6 @@ extension AST {
}
}

/// Visits `b` and its children in pre-order, notifying `o` when a node is entered or left.
public func walk<O: ASTWalkObserver>(functionBody b: FunctionBody, notifying o: inout O) {
switch b {
case .expr(let e):
walk(e, notifying: &o)
case .block(let s):
walk(s, notifying: &o)
}
}

// MARK: Declarations

/// Visits the children of `n` in pre-order, notifying `o` when a node is entered or left.
Expand Down Expand Up @@ -301,7 +291,7 @@ extension AST {
walk(roots: n.parameters, notifying: &o)
walk(n.receiver, notifying: &o)
walk(n.output, notifying: &o)
n.body.map({ walk(functionBody: $0, notifying: &o) })
n.body.map({ (b) in walk(b.base, notifying: &o) })
}

/// Visits the children of `n` in pre-order, notifying `o` when a node is entered or left.
Expand Down Expand Up @@ -342,7 +332,7 @@ extension AST {
_ n: MethodImpl, notifying o: inout O
) {
walk(n.receiver, notifying: &o)
n.body.map({ walk(functionBody: $0, notifying: &o) })
n.body.map({ (b) in walk(b.base, notifying: &o) })
}

/// Visits the children of `n` in pre-order, notifying `o` when a node is entered or left.
Expand Down Expand Up @@ -397,7 +387,7 @@ extension AST {
_ n: SubscriptImpl, notifying o: inout O
) {
walk(n.receiver, notifying: &o)
n.body.map({ walk(functionBody: $0, notifying: &o) })
n.body.map({ (b) in walk(b.base, notifying: &o) })
}

/// Visits the children of `n` in pre-order, notifying `o` when a node is entered or left.
Expand Down
3 changes: 3 additions & 0 deletions Sources/Core/AST/Decl/CapturingDecl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ public protocol CapturingDecl: Decl, LexicalScope {
/// The explicit capture declarations of the entity.
var explicitCaptures: [BindingDecl.ID] { get }

/// The part of the declaration that may have implicit captures.
var sourcesOfImplicitCaptures: [AnyNodeID] { get }

}
3 changes: 3 additions & 0 deletions Sources/Core/AST/Decl/FunctionDecl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ public struct FunctionDecl: CapturingDecl, ExposableDecl, GenericDecl, GenericSc
}
}

/// The part of the declaration that may have implicit captures.
public var sourcesOfImplicitCaptures: [AnyNodeID] { body.map({ (b) in [b.base] }) ?? [] }

public func validateForm(in ast: AST, reportingDiagnosticsTo log: inout DiagnosticSet) {
if !isInExprContext {
// Parameter declarations must have a type annotation.
Expand Down
3 changes: 3 additions & 0 deletions Sources/Core/AST/Decl/SubscriptDecl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ public struct SubscriptDecl: BundleDecl, CapturingDecl, GenericDecl, GenericScop
/// Returns whether the declaration denotes a static subscript.
public var isStatic: Bool { memberModifier?.value == .static }

/// The part of the declaration that may have implicit captures.
public var sourcesOfImplicitCaptures: [AnyNodeID] { impls.map(AnyNodeID.init) }

public func validateForm(in ast: AST, reportingDiagnosticsTo log: inout DiagnosticSet) {
// Parameter declarations must have a type annotation.
for p in parameters {
Expand Down
6 changes: 4 additions & 2 deletions Sources/FrontEnd/AST+UseCollection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ extension AST {
///
/// This method collects all name expressions that occurs in `d`, visiting its children in
/// pre-order. Nested type and extension declarations are not visited.
func uses(in d: AnyDeclID) -> [(NameExpr.ID, AccessEffect)] {
func uses<T: CapturingDecl>(in d: T.ID) -> [(NameExpr.ID, AccessEffect)] {
var v = UseVisitor()
walk(d, notifying: &v)
for s in self[d].sourcesOfImplicitCaptures {
walk(s, notifying: &v)
}
return v.uses
}

Expand Down
6 changes: 3 additions & 3 deletions Sources/FrontEnd/TypeChecking/TypeChecker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2602,7 +2602,7 @@ struct TypeChecker {
}

// Look at uses to update conventions where we could only guess `let` from the context.
for (n, m) in program.ast.uses(in: AnyDeclID(program[e].decl)) {
for (n, m) in program.ast.uses(in: program[e].decl) {
let candidates = lookup(unqualified: program[n].name.value.stem, in: program[n].scope)
guard
let pick = candidates.unique(ParameterDecl.self),
Expand Down Expand Up @@ -2680,7 +2680,7 @@ struct TypeChecker {
}

/// Returns the implicit captures found in the body of `d`.
private mutating func implicitCaptures<T: Decl & LexicalScope>(
private mutating func implicitCaptures<T: CapturingDecl>(
of d: T.ID, ignoring explicitCaptures: Set<String>
) -> [TupleType.Element] {
// Only local declarations have captures.
Expand All @@ -2690,7 +2690,7 @@ struct TypeChecker {
}

var captureToStemAndEffect: [AnyDeclID: (stem: String, effect: AccessEffect)] = [:]
for (name, mutability) in program.ast.uses(in: AnyDeclID(d)) {
for (name, mutability) in program.ast.uses(in: d) {
guard
let (stem, pick) = lookupImplicitCapture(name, occurringIn: d),
!explicitCaptures.contains(stem)
Expand Down
15 changes: 15 additions & 0 deletions Tests/EndToEndTests/TestCases/ExplicitCaptures.hylo
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//- compileAndRun expecting: success

fun apply<E>(_ f: [E]() -> Int) -> Int {
f()
}

public fun main() {
var local_variable = 0
let p = mutable_pointer[to: &local_variable]
let n = apply(fun[sink let q = p.copy()]() -> Int {
&(q.unsafe[]) = 19
return 19
})
precondition(n == local_variable)
}

0 comments on commit 3b5ebb4

Please sign in to comment.