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?
source to share
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.
source to share