Skip to content

SILGen: Implement if #_hasSymbol(...) conditions #61563

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/ABI/Mangling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,11 @@ Globals
global ::= protocol-conformance 'MA' // metadata for remote mirrors: associated type descriptor
global ::= nominal-type 'MC' // metadata for remote mirrors: superclass descriptor

// TODO check this::
global ::= mangled-name 'TA' // partial application forwarder
global ::= mangled-name 'Ta' // ObjC partial application forwarder
global ::= mangled-name 'TQ' index // Async await continuation partial function
global ::= mangled-name 'TY' index // Async suspend continuation partial function
global ::= mangled-name 'TwS' // #_hasSymbol query function

global ::= type 'w' VALUE-WITNESS-KIND // value witness

Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/ASTMangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ class ASTMangler : public Mangler {
AccessibleFunctionRecord,
BackDeploymentThunk,
BackDeploymentFallback,
HasSymbolQuery,
};

/// lldb overrides the defaulted argument to 'true'.
Expand Down Expand Up @@ -357,6 +358,8 @@ class ASTMangler : public Mangler {

std::string mangleGenericSignature(const GenericSignature sig);

std::string mangleHasSymbolQuery(const ValueDecl *decl);

enum SpecialContext {
ObjCContext,
ClangImporterContext,
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2733,6 +2733,11 @@ class ValueDecl : public Decl {
/// 'func foo(Int) -> () -> Self?'.
GenericParameterReferenceInfo findExistentialSelfReferences(
Type baseTy, bool treatNonResultCovariantSelfAsInvariant) const;

/// Returns a synthesized declaration for a query function that provides
/// the boolean value for a `if #_hasSymbol(...)` condition. The interface
/// type of the function is `() -> Builtin.Int1`.
FuncDecl *getHasSymbolQueryDecl() const;
};

/// This is a common base class for declarations which declare a type.
Expand Down
5 changes: 4 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5953,6 +5953,9 @@ ERROR(availability_macro_in_inlinable, none,
"availability macro cannot be used in " FRAGILE_FUNC_KIND "0",
(unsigned))

ERROR(has_symbol_condition_in_inlinable, none,
"'#_hasSymbol' cannot be used in " FRAGILE_FUNC_KIND "0", (unsigned))

#undef FRAGILE_FUNC_KIND

NOTE(resilience_decl_declared_here_public,
Expand Down Expand Up @@ -6619,7 +6622,7 @@ WARNING(has_symbol_decl_must_be_weak,none,
(DescriptiveDeclKind, DeclName))
ERROR(has_symbol_invalid_expr,none,
"'#_hasSymbol' condition must refer to a declaration", ())
ERROR(has_symbol_unsupported,none,
ERROR(has_symbol_unsupported_on_target,none,
"'#_hasSymbol' is unsupported on target '%0'", (StringRef))

//------------------------------------------------------------------------------
Expand Down
18 changes: 18 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -3736,6 +3736,24 @@ class GetTypeWrapperInitializer
bool isCached() const { return true; }
};

/// Synthesizes and returns a `#_hasSymbol` query function for the given
/// `ValueDecl`. The function has an interface type of `() -> Builtin.Int1`.
class SynthesizeHasSymbolQueryRequest
: public SimpleRequest<SynthesizeHasSymbolQueryRequest,
FuncDecl *(const ValueDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

FuncDecl *evaluate(Evaluator &evaluator, const ValueDecl *decl) const;

public:
bool isCached() const { return true; }
};

void simple_display(llvm::raw_ostream &out, ASTNode node);
void simple_display(llvm::raw_ostream &out, Type value);
void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR);
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -440,3 +440,6 @@ SWIFT_REQUEST(TypeChecker, SynthesizeLocalVariableForTypeWrapperStorage,
SWIFT_REQUEST(TypeChecker, GetTypeWrapperInitializer,
ConstructorDecl *(NominalTypeDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, SynthesizeHasSymbolQueryRequest,
FuncDecl *(ValueDecl *),
Cached, NoLocationInfo)
2 changes: 2 additions & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,9 @@ NODE(UniqueExtendedExistentialTypeShapeSymbolicReference)
NODE(NonUniqueExtendedExistentialTypeShapeSymbolicReference)
NODE(SymbolicExtendedExistentialType)

// Added in Swift 5.8
NODE(MetatypeParamsRemoved)
NODE(HasSymbolQuery)

#undef CONTEXT_NODE
#undef NODE
23 changes: 23 additions & 0 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,28 @@ std::string ASTMangler::mangleGenericSignature(const GenericSignature sig) {
return finalize();
}

std::string ASTMangler::mangleHasSymbolQuery(const ValueDecl *Decl) {
beginMangling();

if (auto Ctor = dyn_cast<ConstructorDecl>(Decl)) {
appendConstructorEntity(Ctor, /*isAllocating=*/false);
} else if (auto Dtor = dyn_cast<DestructorDecl>(Decl)) {
appendDestructorEntity(Dtor, /*isDeallocating=*/false);
} else if (auto GTD = dyn_cast<GenericTypeDecl>(Decl)) {
appendAnyGenericType(GTD);
} else if (isa<AssociatedTypeDecl>(Decl)) {
appendContextOf(Decl);
appendDeclName(Decl);
appendOperator("Qa");
} else {
appendEntity(Decl);
}

appendSymbolKind(ASTMangler::SymbolKind::HasSymbolQuery);

return finalize();
}

void ASTMangler::appendSymbolKind(SymbolKind SKind) {
switch (SKind) {
case SymbolKind::Default: return;
Expand All @@ -872,6 +894,7 @@ void ASTMangler::appendSymbolKind(SymbolKind SKind) {
case SymbolKind::AccessibleFunctionRecord: return appendOperator("HF");
case SymbolKind::BackDeploymentThunk: return appendOperator("Twb");
case SymbolKind::BackDeploymentFallback: return appendOperator("TwB");
case SymbolKind::HasSymbolQuery: return appendOperator("TwS");
}
}

Expand Down
2 changes: 2 additions & 0 deletions lib/Demangling/Demangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ bool swift::Demangle::isFunctionAttr(Node::Kind kind) {
case Node::Kind::AccessibleFunctionRecord:
case Node::Kind::BackDeploymentThunk:
case Node::Kind::BackDeploymentFallback:
case Node::Kind::HasSymbolQuery:
return true;
default:
return false;
Expand Down Expand Up @@ -2675,6 +2676,7 @@ NodePointer Demangler::demangleThunkOrSpecialization() {
switch (nextChar()) {
case 'b': return createNode(Node::Kind::BackDeploymentThunk);
case 'B': return createNode(Node::Kind::BackDeploymentFallback);
case 'S': return createNode(Node::Kind::HasSymbolQuery);
default:
return nullptr;
}
Expand Down
4 changes: 4 additions & 0 deletions lib/Demangling/NodePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@ class NodePrinter {
case Node::Kind::UniqueExtendedExistentialTypeShapeSymbolicReference:
case Node::Kind::NonUniqueExtendedExistentialTypeShapeSymbolicReference:
case Node::Kind::SymbolicExtendedExistentialType:
case Node::Kind::HasSymbolQuery:
return false;
}
printer_unreachable("bad node kind");
Expand Down Expand Up @@ -3046,6 +3047,9 @@ NodePointer NodePrinter::print(NodePointer Node, unsigned depth,

return nullptr;
}
case Node::Kind::HasSymbolQuery:
Printer << "#_hasSymbol query for ";
return nullptr;
}

printer_unreachable("bad node kind!");
Expand Down
5 changes: 5 additions & 0 deletions lib/Demangling/OldRemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2854,3 +2854,8 @@ ManglingError Remangler::mangleBackDeploymentFallback(Node *node,
Buffer << "TwB";
return ManglingError::Success;
}

ManglingError Remangler::mangleHasSymbolQuery(Node *node, unsigned depth) {
Buffer << "TwS";
return ManglingError::Success;
}
6 changes: 6 additions & 0 deletions lib/Demangling/Remangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1669,6 +1669,7 @@ ManglingError Remangler::mangleGlobal(Node *node, unsigned depth) {
case Node::Kind::AccessibleFunctionRecord:
case Node::Kind::BackDeploymentThunk:
case Node::Kind::BackDeploymentFallback:
case Node::Kind::HasSymbolQuery:
mangleInReverseOrder = true;
break;
default:
Expand Down Expand Up @@ -3503,6 +3504,11 @@ ManglingError Remangler::mangleExtendedExistentialTypeShape(Node *node,
return ManglingError::Success;
}

ManglingError Remangler::mangleHasSymbolQuery(Node *node, unsigned depth) {
Buffer << "TwS";
return ManglingError::Success;
}

ManglingError Remangler::mangleSymbolicExtendedExistentialType(Node *node,
unsigned int depth) {
RETURN_IF_ERROR(mangle(node->getChild(0), depth+1));
Expand Down
17 changes: 16 additions & 1 deletion lib/SILGen/SILGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1522,7 +1522,22 @@ void SILGenFunction::emitStmtCondition(StmtCondition Cond, JumpDest FalseDest,
}

case StmtConditionElement::CK_HasSymbol: {
llvm::report_fatal_error("Can't SILGen #_hasSymbol yet!");
auto info = elt.getHasSymbolInfo();
assert(!info->isInvalid());
auto expr = info->getSymbolExpr();
auto declRef = info->getReferencedDecl();
assert(declRef);

auto queryFunc = declRef.getDecl()->getHasSymbolQueryDecl();
SILFunction *silFn = SGM.getFunction(
SILDeclRef(queryFunc, SILDeclRef::Kind::Func), NotForDefinition);
SILValue fnRef = B.createFunctionRefFor(loc, silFn);
booleanTestValue = B.createApply(loc, fnRef, {}, {});
booleanTestValue = emitUnwrapIntegerResult(expr, booleanTestValue);
booleanTestLoc = expr;

// FIXME: Add decl to list of decls that need a has symbol query
// function to be emitted during IRGen.
break;
}
}
Expand Down
36 changes: 33 additions & 3 deletions lib/Sema/CodeSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

#include "CodeSynthesis.h"

#include "TypeChecker.h"
#include "TypeCheckDecl.h"
#include "TypeCheckDistributed.h"
#include "TypeCheckObjC.h"
#include "TypeCheckType.h"
#include "TypeCheckDistributed.h"
#include "TypeChecker.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/Availability.h"
#include "swift/AST/DistributedDecl.h"
#include "swift/AST/Expr.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/Initializer.h"
Expand All @@ -31,7 +33,6 @@
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/DistributedDecl.h"
#include "swift/Basic/Defer.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/Sema/ConstraintSystem.h"
Expand Down Expand Up @@ -1608,3 +1609,32 @@ ConstructorDecl *SynthesizeTypeWrappedTypeMemberwiseInitializer::evaluate(
ctor->setBody(body, AbstractFunctionDecl::BodyKind::Parsed);
return ctor;
}

FuncDecl *ValueDecl::getHasSymbolQueryDecl() const {
return evaluateOrDefault(getASTContext().evaluator,
SynthesizeHasSymbolQueryRequest{this}, nullptr);
}

FuncDecl *
SynthesizeHasSymbolQueryRequest::evaluate(Evaluator &evaluator,
const ValueDecl *decl) const {
auto &ctx = decl->getASTContext();
auto dc = decl->getModuleContext();

Mangle::ASTMangler mangler;
auto mangledName = ctx.AllocateCopy(mangler.mangleHasSymbolQuery(decl));

ParameterList *params = ParameterList::createEmpty(ctx);

DeclName funcName =
DeclName(ctx, DeclBaseName(ctx.getIdentifier(mangledName)),
/*argumentNames=*/ArrayRef<Identifier>());

auto i1 = BuiltinIntegerType::get(1, ctx);
FuncDecl *func = FuncDecl::createImplicit(
ctx, swift::StaticSpellingKind::None, funcName, SourceLoc(),
/*async=*/false, /*throws=*/false, nullptr, params, i1, dc);

func->getAttrs().add(new (ctx) SILGenNameAttr(mangledName, IsImplicit));
return func;
}
Copy link
Contributor

@xymus xymus Oct 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it help the inlinable case to write that synthesized function in the swiftinterface (and swiftmodule) as usable from inline?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't help directly. The function body cannot be spelled in Swift (or even SIL) today so we can't emit the implementation of the helper function. Emitting just the function declaration with some kind of indication that it needs special treatment is doable but it also doesn't help with the crux of the problem, which is reconstituting the VarDecl * that we need to pass down to IRGen.

These are my ideas for lifting the restriction in the future:

  1. Invent a new SIL instruction for the #_hasSymbol() condition that, when serialized, serializes the VarDecl * so that we can re-derive the helper function.
  2. Make SIL capable of representing the logic of the helper function, in which case we generate the helper function in SILGen instead of IRGen.

20 changes: 19 additions & 1 deletion lib/Sema/MiscDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4410,11 +4410,29 @@ static bool diagnoseHasSymbolCondition(PoundHasSymbolInfo *info,
if (!ctx.LangOpts.Target.isOSDarwin()) {
// SILGen for #_hasSymbol is currently implemented assuming the target OS
// is a Darwin platform.
ctx.Diags.diagnose(info->getStartLoc(), diag::has_symbol_unsupported,
ctx.Diags.diagnose(info->getStartLoc(),
diag::has_symbol_unsupported_on_target,
ctx.LangOpts.Target.str());
return true;
}

if (DC->getAsDecl()) {
auto fragileKind = DC->getFragileFunctionKind();
if (fragileKind.kind != FragileFunctionKind::None) {
// #_hasSymbol cannot be used in inlinable code because of limitations of
// the current implementation strategy. It relies on recording the
// referenced ValueDecl, mangling a helper function name using that
// ValueDecl, and then passing the responsibility of generating the
// definition for that helper function to IRGen. In order to lift this
// restriction, we will need teach SIL to encode the ValueDecl, or take
// another approach entirely.
ctx.Diags.diagnose(info->getStartLoc(),
diag::has_symbol_condition_in_inlinable,
fragileKind.getSelector());
return true;
}
}

auto decl = info->getReferencedDecl().getDecl();
if (!decl) {
// Diagnose because we weren't able to interpret the expression as one
Expand Down
2 changes: 2 additions & 0 deletions test/Demangle/Inputs/manglings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -439,3 +439,5 @@ $sxIeghHr_xs5Error_pIegHrzo_s8SendableRzs5NeverORs_r0_lTRTQ0_ ---> {T:} (1) awai
$sxIeghHr_xs5Error_pIegHrzo_s8SendableRzs5NeverORs_r0_lTRTY0_ ---> {T:} (1) suspend resume partial function for reabstraction thunk helper <A, B where A: Swift.Sendable, B == Swift.Never> from @escaping @callee_guaranteed @Sendable @async () -> (@out A) to @escaping @callee_guaranteed @async () -> (@out A, @error @owned Swift.Error)
$sxIeghHr_xs5Error_pIegHrzo_s8SendableRzs5NeverORs_r0_lTRTY_ ---> {T:} (0) suspend resume partial function for reabstraction thunk helper <A, B where A: Swift.Sendable, B == Swift.Never> from @escaping @callee_guaranteed @Sendable @async () -> (@out A) to @escaping @callee_guaranteed @async () -> (@out A, @error @owned Swift.Error)
$sxIeghHr_xs5Error_pIegHrzo_s8SendableRzs5NeverORs_r0_lTRTQ12_ ---> {T:} (13) await resume partial function for reabstraction thunk helper <A, B where A: Swift.Sendable, B == Swift.Never> from @escaping @callee_guaranteed @Sendable @async () -> (@out A) to @escaping @callee_guaranteed @async () -> (@out A, @error @owned Swift.Error)
$s7Library3fooyyFTwS ---> #_hasSymbol query for Library.foo() -> ()
$s7Library5KlassCTwS ---> #_hasSymbol query for Library.Klass
56 changes: 56 additions & 0 deletions test/SILGen/Inputs/has_symbol_helper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@

public let global: Int = 0

public func function(with argument: Int) {}
public func genericFunc<T: P>(_ t: T) {}
@_cdecl("cdecl_func") public func cdeclFunc() {}
@_silgen_name("forward_declared_func") public func forwardDeclaredFunc()

public protocol P {
func requirement()
}

public struct S {
public static var staticMember: Int = 0
public static func staticFunc() {}

public var member: Int

public init(member: Int) {
self.member = member
}
public func method(with argument: Int) {}
public func genericFunc<T: P>(_ t: T) {}
}

extension S: P {
public func requirement() {}
}

public struct GenericS<T: P> {
public var member: T

public init(member: T) {
self.member = member
}
public func method(with argument: T) {}
}

public class C {
public static var staticMember: Int = 0
public class func classFunc() {}

public var member: Int

public init(member: Int) {
self.member = member
}
public func method(with argument: Int) {}
}

public enum E {
case basicCase
case payloadCase(_: S)

public func method() {}
}
Loading