Strange meaning of literal #function in Swift 3.1

I found out that what a String

literal returns #function

in Swift 3.1 is ... weird. Here:

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"

      

All parentheses are omitted when all parameters are unmarked. I would quite understand that if it #function

returns the function name without parentheses for functions without any parameters.

Is this justified behavior or a mistake?

+3


source to share


1 answer


I agree that this is completely incomprehensible behavior, but it seems to be intentional.

Function literals get "padded" in the SILGen process; and this is done with the help SILGenFunction::emitLiteral

in SILGenApply.cpp .

Then it calls getMagicFunctionString

functions for the literal:

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;
}

      

Which, if not already generated, creates a new stream to output to MagicFunctionString

and calls DeclName::printPretty

in MagicFunctionName

with that stream:

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

      

( MagicFunctionName

assigned when the function is emitted ; given a value getFullName()

that is just aName

declaration)



It then calls DeclName::print

, which takes a boolean argument as its second parameter, to determine whether to omit the list argument names if they are 'all empty:

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;

}

      

And you can see that due to the if condition, if (getArgumentNames().size() > 0)

functions with no parameters skip checking for all empty argument names, causing them to be issued with parentheses, for example weirdo()

. But functions with one or more parameters, all with empty argument names, are emitted without parentheses, for example weirdo

.

So, given DeclName::printPretty

specifically passing true

for an argument skipEmptyArgumentNames

, it seems like the Swift team specifically wants this behavior for #function

.

Also, if we look at the blame, we can see what DeclName::printPretty

was added to this commit with the commit message:

Pretty-print DeclNames with no keyword arguments, discarding the parting bit.

Instead of printing " f(_:_:)

", just type " f

".

That being said, I 'll write a bug report anyway , since it doesn't seem intuitive for function literals.

+2


source







All Articles