为什么 Swift 无法推断结果生成器中的上下文基类型?

Swe*_*per 6 dsl swift

背景:我正在用 Swift 为一种玩具语言编写一个编译器,并且我想要一种优雅的方式来为我的语言创建 AST。我的玩具语言中的语句的 AST 类型如下所示:

indirect enum Statement {
    case assignment(variable: String, expression: Expression)
    case conditional(`if`: Expression, then: Statement, `else`: Statement)
    case loop(`while`: Expression, `do`: Statement)
    case sequence(Statement, Statement)
    case noop
    case halt
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我想为一系列语句编写 AST,我必须编写:

// let's say I want to represent a series of 4 no-ops:
.sequence(.noop, 
    .sequence(.noop, 
        .sequence(.noop, 
                      .noop)))
Run Code Online (Sandbox Code Playgroud)

这看起来非常冗长。我想如果我能使用这个功能就好了@resultBuilder,这样我就可以写:

Statement {
    .noop
    .noop
    .noop
    .noop
}
Run Code Online (Sandbox Code Playgroud)

这是我的尝试:

@resultBuilder
struct StatementBuilder {
    static func buildBlock(_ components: Statement...) -> Statement {
        if components.isEmpty {
            return .noop
        } else {
            return components.dropFirst().reduce(components.first!) { x, y in .sequence(x, y) }
        }
    }
}

extension Statement {
    init(@StatementBuilder block: () -> Statement) {
        self = block()
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,这给了我错误:

无法推断引用成员“noop”的上下文基础

Statement { ... }街区里。

上下文基础有哪些不清楚的地方?除了 之外,它还可以是什么类型Statement?我可以通过在所有内容前面加上 前缀来解决这个问题Statement.,但这太冗长了。我还可以做些什么?


请注意,我还计划重载运算符,以便可以轻松创建赋值和表达式,使语法树类型符合ExpressibleXXXLiteral,并添加IfWhile函数采用StatementBuilders,从而创建.conditional.loop语句。因此,结果生成器将比仅仅用于创建无操作和停止有用得多。

Ada*_*kis 5

ResultBuilder 表达式似乎使用了一种称为单向约束的东西

霍莉·博拉 (Holly Borla)在这里解释道:

是的,这是预期的行为。函数生成器中表达式的推断类型不会影响其他表达式的推断类型。

与 Swift 中的大多数表达式不同,函数构建器使用一种称为单向约束的东西来进行类型推断(这就是为什么对 buildBlock 的常规函数​​调用与类型推断的行为不同)。单向约束意味着类型信息只能在一个方向上流动,而不是通常的双向类型推断。因此,在这种情况下,类型信息 Conv2D 将影响模型推断的整体类型,但不会影响 Sequential { .... 内的其他表达式。

@Douglas_Gregor 写了一篇关于单向约束的解释以及为什么在他的 PR 中使用它们并添加了它们: https ://github.com/apple/swift/pull/25983