Skip to content

[Sema] TypeWrappers: If type wrapper comes from .swiftinterface use synthesized decls #62838

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 5 commits into from
Jan 5, 2023
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
34 changes: 0 additions & 34 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -3546,23 +3546,6 @@ class GetTypeWrapper
bool isCached() const { return true; }
};

/// Return a type of the type wrapper (if any) associated with the given
/// declaration.
class GetTypeWrapperType
: public SimpleRequest<GetTypeWrapperType, Type(NominalTypeDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

Type evaluate(Evaluator &evaluator, NominalTypeDecl *) const;

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

/// Inject or get `$Storage` type which has all of the stored properties
/// of the given type with a type wrapper.
class GetTypeWrapperStorage
Expand Down Expand Up @@ -3762,23 +3745,6 @@ class GetTypeWrapperInitializer
bool isCached() const { return true; }
};

/// Check whether this is a protocol that has a type wrapper attribute
/// or one of its dependencies does.
class UsesTypeWrapperFeature
: public SimpleRequest<UsesTypeWrapperFeature, bool(NominalTypeDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

bool evaluate(Evaluator &evaluator, NominalTypeDecl *) const;

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

/// Find the definition of a given macro.
class MacroDefinitionRequest
: public SimpleRequest<MacroDefinitionRequest,
Expand Down
6 changes: 0 additions & 6 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,6 @@ SWIFT_REQUEST(TypeChecker, GetSourceFileAsyncNode,
SWIFT_REQUEST(TypeChecker, GetTypeWrapper,
Optional<TypeWrapperInfo>(NominalTypeDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, GetTypeWrapperType,
Type(NominalTypeDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, GetTypeWrapperStorage,
TypeDecl *(NominalTypeDecl *),
Cached, NoLocationInfo)
Expand Down Expand Up @@ -443,9 +440,6 @@ SWIFT_REQUEST(TypeChecker, ContinueTargetRequest,
SWIFT_REQUEST(TypeChecker, GetTypeWrapperInitializer,
ConstructorDecl *(NominalTypeDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, UsesTypeWrapperFeature,
bool(NominalTypeDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, MacroDefinitionRequest,
MacroDefinition(MacroDecl *),
Cached, NoLocationInfo)
Expand Down
9 changes: 5 additions & 4 deletions include/swift/AST/TypeWrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,18 @@ namespace swift {

struct TypeWrapperInfo {
CustomAttr *Attr;
Type AttrType;
NominalTypeDecl *Wrapper;
NominalTypeDecl *AttachedTo;
bool IsInferred;

TypeWrapperInfo(CustomAttr *attr, NominalTypeDecl *wrapperDecl,
TypeWrapperInfo(CustomAttr *attr, Type attrType, NominalTypeDecl *wrapperDecl,
NominalTypeDecl *attachedTo, bool isInferred)
: Attr(attr), Wrapper(wrapperDecl), AttachedTo(attachedTo),
IsInferred(isInferred) {}
: Attr(attr), AttrType(attrType), Wrapper(wrapperDecl),
AttachedTo(attachedTo), IsInferred(isInferred) {}

TypeWrapperInfo asInferred() const {
return {Attr, Wrapper, AttachedTo, true};
return {Attr, AttrType, Wrapper, AttachedTo, true};
}
};

Expand Down
14 changes: 1 addition & 13 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2949,19 +2949,7 @@ static bool usesFeatureSpecializeAttributeWithAvailability(Decl *decl) {
}

static bool usesFeatureTypeWrappers(Decl *decl) {
NullablePtr<NominalTypeDecl> typeDecl;

if (auto *extension = dyn_cast<ExtensionDecl>(decl)) {
typeDecl = extension->getExtendedNominal();
} else {
typeDecl = dyn_cast<NominalTypeDecl>(decl);
}

if (!typeDecl)
return false;

return evaluateOrDefault(decl->getASTContext().evaluator,
UsesTypeWrapperFeature{typeDecl.get()}, false);
return false;
}

static bool usesFeatureRuntimeDiscoverableAttrs(Decl *decl) {
Expand Down
50 changes: 0 additions & 50 deletions lib/AST/TypeWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,53 +44,3 @@ bool VarDecl::isTypeWrapperLocalStorageForInitializer() const {
}
return false;
}

bool UsesTypeWrapperFeature::evaluate(Evaluator &evaluator,
NominalTypeDecl *decl) const {
// This is a type wrapper type.
if (decl->getAttrs().hasAttribute<TypeWrapperAttr>())
return true;

// This is a type wrapped type.
if (decl->hasTypeWrapper())
return true;

// This type could be depending on a type wrapper feature
// indirectly by conforming to a protocol with a type
// wrapper attribute. To determine that we need to walk
// protocol dependency chains and check each one.

auto &ctx = decl->getASTContext();

auto usesTypeWrapperFeature = [&](ProtocolDecl *protocol) {
return evaluateOrDefault(ctx.evaluator, UsesTypeWrapperFeature{protocol},
false);
};

for (unsigned i : indices(decl->getInherited())) {
auto inheritedType = evaluateOrDefault(
ctx.evaluator,
InheritedTypeRequest{decl, i, TypeResolutionStage::Interface}, Type());

if (!(inheritedType && inheritedType->isConstraintType()))
continue;

if (auto *protocol =
dyn_cast_or_null<ProtocolDecl>(inheritedType->getAnyNominal())) {
if (usesTypeWrapperFeature(protocol))
return true;
}

if (auto composition = inheritedType->getAs<ProtocolCompositionType>()) {
for (auto member : composition->getMembers()) {
if (auto *protocol =
dyn_cast_or_null<ProtocolDecl>(member->getAnyNominal())) {
if (usesTypeWrapperFeature(protocol))
return true;
}
}
}
}

return false;
}
19 changes: 17 additions & 2 deletions lib/Sema/CodeSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
params.push_back(arg);
}
} else if (ICK == ImplicitConstructorKind::TypeWrapperStorage) {
accessLevel = AccessLevel::Public;
accessLevel = decl->getTypeWrapperStorageDecl()->getFormalAccess();

auto typeWrapperInfo = decl->getTypeWrapper();
assert(typeWrapperInfo);
Expand Down Expand Up @@ -1657,14 +1657,29 @@ ConstructorDecl *SynthesizeTypeWrappedTypeStorageWrapperInitializer::evaluate(
if (!wrappedType->hasTypeWrapper())
return nullptr;

auto &ctx = wrappedType->getASTContext();

// .swiftinterfaces have both attribute and a synthesized member
// (if it's public), so in this case we need use existing declaration
// if available.
{
auto parentSF = wrappedType->getDeclContext()->getParentSourceFile();
if (parentSF && parentSF->Kind == SourceFileKind::Interface) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering if it's worth restricting this logic to swiftinterfaces. Could it be useful for the user to define custom implementations for the type wrapper's backing logic? Keeping a similar behavior between sources and swiftinterface would be nice too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is something we intentionally don't want users to mess with.

DeclName initName(ctx, DeclBaseName::createConstructor(),
/*labels=*/{ctx.Id_storageWrapper});
auto results = wrappedType->lookupDirect(initName);
if (results.size() == 1)
return cast<ConstructorDecl>(results.front());
}
}

// `@typeWrapperIgnored` properties suppress this initializer.
if (llvm::any_of(wrappedType->getMembers(), [&](Decl *member) {
return member->getAttrs().hasAttribute<TypeWrapperIgnoredAttr>();
}))
return nullptr;

// Create the implicit type wrapper storage constructor.
auto &ctx = wrappedType->getASTContext();
auto ctor = createImplicitConstructor(
wrappedType, ImplicitConstructorKind::TypeWrapperStorage, ctx);
wrappedType->addMember(ctor);
Expand Down
11 changes: 10 additions & 1 deletion lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3826,7 +3826,16 @@ void AttributeChecker::visitPropertyWrapperAttr(PropertyWrapperAttr *attr) {
}

void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
if (!Ctx.LangOpts.hasFeature(Feature::TypeWrappers)) {
auto isEnabled = [&]() {
if (Ctx.LangOpts.hasFeature(Feature::TypeWrappers))
return true;

// Accept attributes that come from swiftinterface files.
auto *parentSF = D->getDeclContext()->getParentSourceFile();
return parentSF && parentSF->Kind == SourceFileKind::Interface;
};

if (!isEnabled()) {
diagnose(attr->getLocation(), diag::type_wrappers_are_experimental);
attr->setInvalid();
return;
Expand Down
66 changes: 42 additions & 24 deletions lib/Sema/TypeCheckTypeWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@

using namespace swift;

/// Check whether given declaration comes from the .swiftinterface file.
static bool inSwiftInterfaceContext(NominalTypeDecl *typeDecl) {
auto *SF = typeDecl->getDeclContext()->getParentSourceFile();
return SF && SF->Kind == SourceFileKind::Interface;
}

static ValueDecl *findMember(NominalTypeDecl *typeDecl, Identifier memberName) {
auto members = typeDecl->lookupDirect(memberName);
return members.size() == 1 ? members.front() : nullptr;
}

static PatternBindingDecl *injectVariable(DeclContext *DC, Identifier name,
Type type,
VarDecl::Introducer introducer,
Expand Down Expand Up @@ -119,9 +130,19 @@ static void getTypeWrappers(NominalTypeDecl *decl,
continue;

auto *typeWrapper = nominal->getAttrs().getAttribute<TypeWrapperAttr>();
if (typeWrapper && typeWrapper->isValid())
if (typeWrapper && typeWrapper->isValid()) {
auto attrType = evaluateOrDefault(
ctx.evaluator,
CustomAttrTypeRequest{mutableAttr, decl,
CustomAttrTypeKind::TypeWrapper},
Type());

if (!attrType || attrType->hasError())
continue;

typeWrappers.push_back(
{mutableAttr, nominal, decl, /*isInferred=*/false});
{mutableAttr, attrType, nominal, decl, /*isInferred=*/false});
}
}

// Do not allow transitive protocol inference between protocols.
Expand Down Expand Up @@ -184,24 +205,6 @@ GetTypeWrapper::evaluate(Evaluator &evaluator, NominalTypeDecl *decl) const {
return typeWrappers.front();
}

Type GetTypeWrapperType::evaluate(Evaluator &evaluator,
NominalTypeDecl *decl) const {
auto typeWrapperInfo = decl->getTypeWrapper();
if (!typeWrapperInfo)
return Type();

auto type = evaluateOrDefault(
evaluator,
CustomAttrTypeRequest{typeWrapperInfo->Attr, decl->getDeclContext(),
CustomAttrTypeKind::TypeWrapper},
Type());

if (!type || type->hasError()) {
return ErrorType::get(decl->getASTContext());
}
return type;
}

VarDecl *NominalTypeDecl::getTypeWrapperProperty() const {
auto *mutableSelf = const_cast<NominalTypeDecl *>(this);
return evaluateOrDefault(getASTContext().evaluator,
Expand Down Expand Up @@ -284,6 +287,15 @@ TypeDecl *GetTypeWrapperStorage::evaluate(Evaluator &evaluator,

auto &ctx = parent->getASTContext();

// .swiftinterfaces have both attribute and a synthesized member
// (if it's public), so in this case we need use existing declaration
// if available.
if (inSwiftInterfaceContext(parent)) {
if (auto *storage = dyn_cast_or_null<TypeDecl>(
findMember(parent, ctx.Id_TypeWrapperStorage)))
return storage;
}

TypeDecl *storage = nullptr;
if (isa<ProtocolDecl>(parent)) {
// If type wrapper is associated with a protocol, we need to
Expand Down Expand Up @@ -320,13 +332,19 @@ GetTypeWrapperProperty::evaluate(Evaluator &evaluator,
if (!typeWrapper)
return nullptr;

// .swiftinterfaces have both attribute and a synthesized member
// (if it's public), so in this case we need use existing declaration
// if available.
if (inSwiftInterfaceContext(parent)) {
if (auto *storage = dyn_cast_or_null<VarDecl>(
findMember(parent, ctx.Id_TypeWrapperProperty)))
return storage;
}

auto *storage = parent->getTypeWrapperStorageDecl();
assert(storage);

auto *typeWrapperType =
evaluateOrDefault(ctx.evaluator, GetTypeWrapperType{parent}, Type())
->castTo<AnyGenericType>();
assert(typeWrapperType);
auto *typeWrapperType = typeWrapper->AttrType->castTo<AnyGenericType>();

// $storage: Wrapper<<ParentType>, <ParentType>.$Storage>
auto propertyTy = BoundGenericType::get(
Expand Down
1 change: 0 additions & 1 deletion test/ModuleInterface/type_wrappers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public struct Wrapper<W, S> {

// CHECK: @TypeWrappers.Wrapper public class Test<T> where T : Swift.StringProtocol {
// CHECK: public init(a: Swift.Int, b: [T])
// CHECK: public init(storageWrapper: TypeWrappers.Wrapper<TypeWrappers.Test<T>, TypeWrappers.Test<T>.$Storage>)
// CHECK: }

@Wrapper
Expand Down
Loading