Skip to content

[DO NOT MERGE] Inline data and dataprotocol #21165

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

Closed
wants to merge 4 commits into from
Closed
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
6 changes: 6 additions & 0 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,12 @@ class ModuleDecl : public DeclContext, public TypeDecl {
Optional<ProtocolConformanceRef>
lookupExistentialConformance(Type type, ProtocolDecl *protocol);

/// Expose TypeChecker functionality for querying protocol conformance.
/// Returns a valid ProtocolConformanceRef only if all conformance
/// requirements are succesfully resolved.
Optional<ProtocolConformanceRef>
conformsToProtocol(Type sourceTy, ProtocolDecl *targetProtocol);

/// Find a member named \p name in \p container that was declared in this
/// module.
///
Expand Down
1 change: 1 addition & 0 deletions include/swift/SIL/DynamicCasts.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define SWIFT_SIL_DYNAMICCASTS_H

#include "swift/Basic/ProfileCounter.h"
#include "swift/SIL/SILValue.h"

namespace swift {

Expand Down
118 changes: 63 additions & 55 deletions include/swift/SILOptimizer/Utils/Existential.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,35 @@

namespace swift {

/// Find InitExistential from global_addr and copy_addr.
SILValue findInitExistentialFromGlobalAddrAndCopyAddr(GlobalAddrInst *GAI,
CopyAddrInst *CAI);

/// Find InitExistential from global_addr and an apply argument.
SILValue findInitExistentialFromGlobalAddrAndApply(GlobalAddrInst *GAI,
ApplySite Apply, int ArgIdx);

/// Returns the address of an object with which the stack location \p ASI is
/// initialized. This is either a init_existential_addr or the destination of a
/// copy_addr. Returns a null value if the address does not dominate the
/// alloc_stack user \p ASIUser.
/// If the value is copied from another stack location, \p isCopied is set to
/// true.
SILValue getAddressOfStackInit(SILValue allocStackAddr, SILInstruction *ASIUser,
bool &isCopied);

/// Find the init_existential, which could be used to determine a concrete
/// type of the value used by \p openedUse.
/// If the value is copied from another stack location, \p isCopied is set to
/// true.
/// Record information about an opened archetype.
///
/// FIXME: replace all uses of this with ConcreteExistentialInfo.
SILInstruction *findInitExistential(Operand &openedUse,
ArchetypeType *&OpenedArchetype,
SILValue &OpenedArchetypeDef,
bool &isCopied);
/// This is used to determine whether a generic call argument originates from
/// an opened existential. For example:
/// %o = open_existential_ref %e : $P & Q to $@opened("PQ") P & Q
/// %r = apply %f<@opened("PQ") P & Q>(%o)
/// : $@convention(method) <τ_0_0 where τ_0_0 : P, τ_0_0 : Q>
/// (@guaranteed τ_0_0) -> @owned τ_0_0
///
/// When successfull, ConcreteExistentialInfo can be used to determinen the
/// concrete type of the opened existential.
struct OpenedArchetypeInfo {
ArchetypeType *OpenedArchetype = nullptr;
// The opened value.
SingleValueInstruction *OpenedArchetypeValue;
// The existential value.
SILValue ExistentialValue;
// True if the openedValue is copied from another stack location
bool isOpenedValueCopied = false;

// Construct a valid instance if the given use originates from a recognizable
// OpenedArchetype instruction.
OpenedArchetypeInfo(Operand &use);

bool isValid() const {
assert(!OpenedArchetype || (OpenedArchetypeValue && ExistentialValue));
return OpenedArchetype;
}
};

/// Record conformance and concrete type info derived from an init_existential
/// value that is reopened before it's use. This is useful for finding the
Expand All @@ -60,20 +62,16 @@ SILInstruction *findInitExistential(Operand &openedUse,
/// : $@convention(method) <τ_0_0 where τ_0_0 : P, τ_0_0 : Q>
/// (@guaranteed τ_0_0) -> @owned τ_0_0
struct ConcreteExistentialInfo {
// The opened type passed as self. `$@opened("PQ")` above.
// This is also the replacement type of the method's Self type.
ArchetypeType *OpenedArchetype = nullptr;
// The definition of the OpenedArchetype.
SILValue OpenedArchetypeDef;
// True if the openedValue is copied from another stack location
bool isCopied;
// The init_existential instruction that produces the opened existential.
SILInstruction *InitExistential = nullptr;
// The existential type of the self argument before it is opened,
// produced by an init_existential.
CanType ExistentialType;
// The concrete type of self from the init_existential. `$C` above.
CanType ConcreteType;
// The concrete value used to initialize the opened existential.
// `%c` in the above comment.
SILValue ConcreteValue;
// True if the ConcreteValue is copied from another stack location
bool isConcreteValueCopied = false;
// When ConcreteType is itself an opened existential, record the type
// definition. May be nullptr for a valid AppliedConcreteType.
SingleValueInstruction *ConcreteTypeDef = nullptr;
Expand All @@ -82,31 +80,20 @@ struct ConcreteExistentialInfo {
// and includes the full list of existential conformances.
// signature: <P & Q>, replacement: $C : conformances: [$P, $Q]
SubstitutionMap ExistentialSubs;
// The value of concrete type used to initialize the existential. `%c` above.
SILValue ConcreteValue;

// Search for a recognized pattern in which the given value is an opened
// existential that was previously initialized to a concrete type.
// Constructs a valid ConcreteExistentialInfo object if successfull.
ConcreteExistentialInfo(Operand &openedUse);
// Search for a recognized pattern in which the given existential value is
// initialized to a concrete type. Constructs a valid ConcreteExistentialInfo
// object if successfull.
ConcreteExistentialInfo(SILValue existential, SILInstruction *user);

// This constructor initializes a ConcreteExistentialInfo based on already
// known ConcreteType and ProtocolDecl pair. It determines the
// OpenedArchetypeDef for the ArgOperand that will be used by unchecked_cast
// instructions to cast OpenedArchetypeDef to ConcreteType.
ConcreteExistentialInfo(Operand &ArgOperand, CanType ConcreteType,
ProtocolDecl *Protocol);
// known ConcreteType and ProtocolDecl pair.
ConcreteExistentialInfo(SILValue existential, SILInstruction *user,
CanType ConcreteType, ProtocolDecl *Protocol);

/// For scenerios where ConcreteExistentialInfo is created using a known
/// ConcreteType and ProtocolDecl, both of InitExistential
/// and ConcreteValue can be null. So there is no need for explicit check for
/// not null for them instead we assert on (!InitExistential ||
/// ConcreteValue).
bool isValid() const {
assert(!InitExistential || ConcreteValue);
return OpenedArchetype && OpenedArchetypeDef && ConcreteType &&
!ExistentialSubs.empty();
}
/// ConcreteType and ProtocolDecl, ConcreteValue can be null.
bool isValid() const { return ConcreteType && !ExistentialSubs.empty(); }

// Do a conformance lookup on ConcreteType with the given requirement, P. If P
// is satisfiable based on the existential's conformance, return the new
Expand All @@ -116,6 +103,27 @@ struct ConcreteExistentialInfo {
CanType selfTy = P->getSelfInterfaceType()->getCanonicalType();
return ExistentialSubs.lookupConformance(selfTy, P);
}

private:
void initializeSubstitutionMap(
ArrayRef<ProtocolConformanceRef> ExistentialConformances, SILModule *M);

void initializeConcreteTypeDef(SILInstruction *typeConversionInst);
};

// Convenience for tracking both the OpenedArchetypeInfo and
// ConcreteExistentialInfo from the same SILValue.
struct ConcreteOpenedArchetypeInfo {
OpenedArchetypeInfo OAI;
// If CEI has a value, it must be valid.
Optional<ConcreteExistentialInfo> CEI;

ConcreteOpenedArchetypeInfo(Operand &use);

// Provide a whole module type-inferred ConcreteType to fall back on if the
// concrete type cannot be determined from data flow.
ConcreteOpenedArchetypeInfo(Operand &use, CanType concreteType,
ProtocolDecl *protocol);
};

} // end namespace swift
Expand Down
32 changes: 22 additions & 10 deletions lib/SIL/DynamicCasts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
//
//===----------------------------------------------------------------------===//

#include "swift/SIL/DynamicCasts.h"
#include "swift/AST/Module.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/Types.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/DynamicCasts.h"
#include "swift/SIL/TypeLowering.h"

using namespace swift;
Expand Down Expand Up @@ -93,15 +94,10 @@ classifyDynamicCastToProtocol(ModuleDecl *M, CanType source, CanType target,
if (!TargetProtocol)
return DynamicCastFeasibility::MaySucceed;

auto conformance = M->lookupConformance(source, TargetProtocol);
if (conformance) {
// A conditional conformance can have things that need to be evaluated
// dynamically.
if (conformance->getConditionalRequirements().empty())
return DynamicCastFeasibility::WillSucceed;

return DynamicCastFeasibility::MaySucceed;
}
// If conformsToProtocol returns a valid conformance, then all requirements
// were proven by the type checker.
if (M->conformsToProtocol(source, TargetProtocol))
return DynamicCastFeasibility::WillSucceed;

auto *SourceNominalTy = source.getAnyNominal();
if (!SourceNominalTy)
Expand Down Expand Up @@ -129,6 +125,22 @@ classifyDynamicCastToProtocol(ModuleDecl *M, CanType source, CanType target,
}
}

// The WillFail conditions below assume any possible conformance on the
// nominal source type has been ruled out. The prior conformsToProtocol query
// identified any definite conformance. Now check if there is already a known
// conditional conformance on the nominal type with requirements that were
// not proven.
//
// TODO: The TypeChecker can easily prove that some requirements cannot be
// met. Returning WillFail in those cases would be more optimal. To do that,
// the conformsToProtocol interface needs to be reformulated as a query, and
// the implementation, including checkGenericArguments, needs to be taught to
// recognize that types with archetypes may potentially succeed.
if (auto conformance = M->lookupConformance(source, TargetProtocol)) {
assert(!conformance->getConditionalRequirements().empty());
return DynamicCastFeasibility::MaySucceed;
}

// If the source type is file-private or target protocol is file-private,
// then conformances cannot be changed at run-time, because only this
// file could have implemented them, but no conformances were found.
Expand Down
Loading