【问题标题】:Weird value of #function literal in Swift 3.1Swift 3.1 中#function 文字的奇怪值
【发布时间】:2018-09-07 01:40:12
【问题描述】:

我发现 Swift 3.1 中 #function 文字返回的 String 是……很奇怪。这里:

class FunctionLiteralTest {
    func weirdo() -> String {
        return #function
    }

    func weirdo(parameter: Int) -> String {
        return #function
    }

    func weirdo(_ parameter: Int) -> String {
        return #function
    }

    func weirdo(_ parameter: Int, _ anotherParameter: Int) -> String {
        return #function
    }
}

let functionLiteralTest = FunctionLiteralTest()

functionLiteralTest.weirdo()             // returns "weirdo()"
functionLiteralTest.weirdo(parameter: 1) // returns "weirdo(parameter:)"
functionLiteralTest.weirdo(1)            // returns "weirdo"
functionLiteralTest.weirdo(1, 2)         // returns "weirdo"

当所有参数都未标记时,括号将被完全跳过。如果#function 为没有任何参数的函数返回不带括号的函数名,我会非常理解。

这是正当的行为还是错误?

【问题讨论】:

  • 奇怪,我本以为 weirdo()weirdo(parameter:)weirdo(_:)weirdo(_:_:)
  • 没错!或者至少只是weirdo,完全跳过参数。

标签: swift function


【解决方案1】:

Swift 5 更新

在 Swift 5(尚未正式发布,但您可以获取 master snapshot)中,由于 #19062,此不一致已得到修复。您的代码现在输出以下内容:

functionLiteralTest.weirdo()             // returns "weirdo()"
functionLiteralTest.weirdo(parameter: 1) // returns "weirdo(parameter:)"
functionLiteralTest.weirdo(1)            // returns "weirdo(_:)"
functionLiteralTest.weirdo(1, 2)         // returns "weirdo(_:_:)"

前 Swift 5

我同意这完全是令人费解的行为,但它确实似乎是故意的。

函数字面量在 SILGen 的过程中被“填充”;这是通过SILGenApply.cpp 中的SILGenFunction::emitLiteral 函数完成的。

然后调用getMagicFunctionString 获取函数字面量:

static StringRef
getMagicFunctionString(SILGenFunction &SGF) {
  assert(SGF.MagicFunctionName
         && "asking for #function but we don't have a function name?!");
  if (SGF.MagicFunctionString.empty()) {
    llvm::raw_string_ostream os(SGF.MagicFunctionString);
    SGF.MagicFunctionName.printPretty(os);
  }
  return SGF.MagicFunctionString;
}

如果尚未生成,则创建一个新流以输出到MagicFunctionString,并使用此流在MagicFunctionName 上调用DeclName::printPretty

llvm::raw_ostream &DeclName::printPretty(llvm::raw_ostream &os) const {
  return print(os, /*skipEmptyArgumentNames=*/true);
}

MagicFunctionName被赋值为when the function is emitted;和is given the value ofgetFullName(),其中is just the Name的声明)

然后调用DeclName::print,它的第二个参数采用布尔参数来确定是否跳过列出的参数名称,如果它们都是空的:

llvm::raw_ostream &DeclName::print(llvm::raw_ostream &os,
                                   bool skipEmptyArgumentNames) const {
  // Print the base name.
  os << getBaseName();

  // If this is a simple name, we're done.
  if (isSimpleName())
    return os;

  if (skipEmptyArgumentNames) {
    // If there is more than one argument yet none of them have names,
    // we're done.
    if (getArgumentNames().size() > 0) {
      bool anyNonEmptyNames = false;
      for (auto c : getArgumentNames()) {
        if (!c.empty()) {
          anyNonEmptyNames = true;
          break;
        }
      }

      if (!anyNonEmptyNames)
        return os;
    }
  }

  // Print the argument names.
  os << "(";
  for (auto c : getArgumentNames()) {
    os << c << ':';
  }
  os << ")";
  return os;

}

您可以看到,由于 if 条件 if (getArgumentNames().size() &gt; 0),没有参数的函数将跳过对所有空参数名称的检查,导致它们被发出 括号,例如 weirdo() .但是带有一个或多个参数的函数,所有参数名称都为空,会在没有括号的情况下发出,例如weirdo

因此,鉴于 DeclName::printPretty 专门为 skipEmptyArgumentNames 参数传递了 true,看来 Swift 团队确实特别希望 #function 有这种行为。

此外,如果我们看一下blame,我们可以看到DeclName::printPretty被添加到this commit中,并带有提交消息:

通过删除没有关键字参数的漂亮打印 DeclNames 带括号的位。

不打印“f(_:_:)”,只打印“f”。

话虽如此,我仍然会file a bug report 处理它,因为它对于函数字面量来说似乎并不那么直观。

【讨论】:

猜你喜欢
  • 2019-02-14
  • 2014-07-26
  • 1970-01-01
  • 1970-01-01
  • 2021-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多