Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
38765fb
[AST] Add a new `@typeWrapper` attribute
xedin Jul 26, 2022
69d80dc
[AST] Add a way to check whether type has a type wrapper
xedin Jul 26, 2022
eb51a11
[AST] Add a way to obtain a type of type wrapper
xedin Aug 1, 2022
029d6d7
[AST] TypeWrappers: Add a way to inject `$Storage` into a type
xedin Jul 26, 2022
ccd7e4f
[AST] TypeWrappers: Add a request to inject or get `$_storage` property
xedin Jul 26, 2022
43feefd
[AST] TypeWrappers: Add a request to create and get "mirror" type wra…
xedin Jul 27, 2022
3dc441b
[TypeChecker] Synthesize getters for stored properties of a type wrap…
xedin Jul 27, 2022
1e0976b
[TypeChecker] Synthesize setters for stored properties of a type wrap…
xedin Jul 28, 2022
0711d77
[Sema] Don't attempt init synthesis for type wrapped types
xedin Jul 28, 2022
d50dec4
[AST] TypeWrapper: Turn `VarDecl::isAccessibleViaTypeWrapper()` into …
xedin Jul 29, 2022
39f5b69
[TypeChecker] Synthesize initializer for a type wrapped declaration
xedin Jul 29, 2022
a672db9
[Sema] TypeWrappers: Make sure that type wrapped properties are alway…
xedin Jul 29, 2022
b3ed4d3
[AST] Make it possible to access type wrapper storage of a var
xedin Jul 29, 2022
d341c41
[AST] Make it possible to access type wrapper property (storage)
xedin Jul 29, 2022
047f51f
[Sema] TypeWrappers: Make sure that synthesized accessors for wrapped…
xedin Jul 29, 2022
7d28a4f
[TypeChecker] NFC: Add more property wrapper tests
xedin Aug 1, 2022
a3b5430
[Frontend] Mark 'Type Wrappers' as experimental feature that has to b…
xedin Aug 1, 2022
39b1566
[Sema] TypeWrappers: convert variable init expr into initializer default
xedin Aug 1, 2022
ecc363d
[Sema] TypeWrappers: Allow wrapping stored properties with attached p…
xedin Aug 10, 2022
20a52d2
[Sema] TypeWrappers/NFC: Move initializer synthesis into a request
xedin Aug 10, 2022
bf2519a
[Sema] Allow implicit parameter decls to have property wrapper attrib…
xedin Aug 11, 2022
0500f35
[Sema] TypeWrappers: Synthesize init parameters for property wrapped …
xedin Aug 12, 2022
8c0f6e1
[TypeWrappers] NFC: Fix type wrapper executable test on Linux
xedin Aug 18, 2022
5a73b48
[Sema] CodeSynthesis: Extract logic to synthesize a parameter for mem…
xedin Aug 24, 2022
347e85d
[Sema] TypeWrappers: Add unamanged stored properties to the synthesiz…
xedin Aug 24, 2022
3aa2bb9
[Sema] TypeWrappers: Mark `$Storage` struct as `internal`
xedin Aug 26, 2022
491defb
[Sema] TypeWrappers: Mark `$_storage` as `private` and all `$Storage`…
xedin Aug 26, 2022
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
5 changes: 5 additions & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,11 @@ DECL_ATTR(_expose, Expose,
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
133)

SIMPLE_DECL_ATTR(typeWrapper, TypeWrapper,
OnStruct | OnClass | OnEnum |
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
134)

// If you're adding a new underscored attribute here, please document it in
// docs/ReferenceGuides/UnderscoredAttributes.md.

Expand Down
28 changes: 27 additions & 1 deletion include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3754,6 +3754,21 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
return getGlobalActorInstance() != nullptr;
}

/// Returns true if this type has a type wrapper custom attribute.
bool hasTypeWrapper() const { return bool(getTypeWrapper()); }

/// Return a type wrapper (if any) associated with this type.
NominalTypeDecl *getTypeWrapper() const;

/// If this declaration has a type wrapper return a property that
/// is used for all type wrapper related operations (mainly for
/// applicable property access routing).
VarDecl *getTypeWrapperProperty() const;

/// Get an initializer that could be used to instantiate a
/// type wrapped type.
ConstructorDecl *getTypeWrapperInitializer() const;

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
return D->getKind() >= DeclKind::First_NominalTypeDecl &&
Expand Down Expand Up @@ -5549,7 +5564,18 @@ class VarDecl : public AbstractStorageDecl {
/// Return true if this property either has storage or has an attached property
/// wrapper that has storage.
bool hasStorageOrWrapsStorage() const;


/// Whether this property belongs to a type wrapped type and has
/// all access to it routed through a type wrapper.
bool isAccessedViaTypeWrapper() const;

/// For type wrapped properties (see \c isAccessedViaTypeWrapper)
/// all access is routed through a type wrapper.
///
/// \returns an underlying type wrapper property which is a
/// storage endpoint for all access to this property.
VarDecl *getUnderlyingTypeWrapperStorage() const;

/// Visit all auxiliary declarations to this VarDecl.
///
/// An auxiliary declaration is a declaration synthesized by the compiler to support
Expand Down
41 changes: 41 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -6476,5 +6476,46 @@ ERROR(moveOnly_not_allowed_here,none,
ERROR(move_expression_not_passed_lvalue,none,
"'move' can only be applied to lvalues", ())

//------------------------------------------------------------------------------
// MARK: Type Wrappers
//------------------------------------------------------------------------------

ERROR(type_wrappers_are_experimental,none,
"type wrappers are an experimental feature", ())

ERROR(type_wrapper_attribute_not_allowed_here,none,
"type wrapper attribute %0 can only be applied to a class, struct",
(Identifier))

ERROR(type_wrapper_requires_a_single_generic_param,none,
"type wrapper has to declare a single generic parameter "
"for underlying storage type", ())

ERROR(cannot_use_multiple_type_wrappers,none,
"type %0 cannot use more than one type wrapper", ())

ERROR(type_wrapper_requires_memberwise_init,none,
"type wrapper type %0 does not contain a required initializer"
" - init(memberwise:)",
(DeclName))

ERROR(type_wrapper_requires_subscript,none,
"type wrapper type %0 does not contain a required subscript"
" - subscript(storedKeyPath:)",
(DeclName))

ERROR(type_wrapper_failable_init,none,
"type wrapper initializer %0 cannot be failable", (DeclName))

ERROR(type_wrapper_invalid_subscript_param_type, none,
"type wrapper subscript expects a key path parameter type (got: %0)",
(Type))

ERROR(type_wrapper_type_requirement_not_accessible,none,
"%select{private|fileprivate|internal|public|open}0 %1 %2 cannot have "
"more restrictive access than its enclosing type wrapper type %3 "
"(which is %select{private|fileprivate|internal|public|open}4)",
(AccessLevel, DescriptiveDeclKind, DeclName, Type, AccessLevel))

#define UNDEFINE_DIAGNOSTIC_MACROS
#include "DefineDiagnosticMacros.h"
6 changes: 6 additions & 0 deletions include/swift/AST/KnownIdentifiers.def
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,12 @@ IDENTIFIER(decodeNextArgument)
IDENTIFIER(SerializationRequirement)
IDENTIFIER_WITH_NAME(builderSelf, "$builderSelf")

// Type wrappers
IDENTIFIER_WITH_NAME(TypeWrapperStorage, "$Storage")
IDENTIFIER_WITH_NAME(TypeWrapperProperty, "$_storage")
IDENTIFIER(storageKeyPath)
IDENTIFIER(memberwise)

#undef IDENTIFIER
#undef IDENTIFIER_
#undef IDENTIFIER_WITH_NAME
172 changes: 172 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -3364,6 +3364,10 @@ enum class CustomAttrTypeKind {
/// unbound generic types.
PropertyWrapper,

/// Just like property wrappers, type wrappers are represented
/// as custom type attributes and allow unbound generic types.
TypeWrapper,

/// Global actors are represented as custom type attributes. They don't
/// have any particularly interesting semantics.
GlobalActor,
Expand Down Expand Up @@ -3499,6 +3503,174 @@ class GetSourceFileAsyncNode
bool isCached() const { return true; }
};

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

private:
friend SimpleRequest;

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

public:
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
: public SimpleRequest<GetTypeWrapperStorage,
NominalTypeDecl *(NominalTypeDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

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

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

/// Inject or get `$_storage` property which is used to route accesses through
/// to all stored properties of a type that has a type wrapper.
class GetTypeWrapperProperty
: public SimpleRequest<GetTypeWrapperProperty, VarDecl *(NominalTypeDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

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

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

/// Given a stored property associated with a type wrapped type,
/// produce a property that mirrors it in the type wrapper context.
class GetTypeWrapperStorageForProperty
: public SimpleRequest<GetTypeWrapperStorageForProperty,
VarDecl *(VarDecl *), RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

VarDecl *evaluate(Evaluator &evaluator, VarDecl *) const;

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

/// Synthesize the body of a getter for a stored property that belongs to
/// a type wrapped type.
class SynthesizeTypeWrappedPropertyGetterBody
: public SimpleRequest<SynthesizeTypeWrappedPropertyGetterBody,
BraceStmt *(AccessorDecl *), RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

BraceStmt *evaluate(Evaluator &evaluator, AccessorDecl *) const;

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

/// Synthesize the body of a setter for a stored property that belongs to
/// a type wrapped type.
class SynthesizeTypeWrappedPropertySetterBody
: public SimpleRequest<SynthesizeTypeWrappedPropertySetterBody,
BraceStmt *(AccessorDecl *), RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

BraceStmt *evaluate(Evaluator &evaluator, AccessorDecl *) 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 IsPropertyAccessedViaTypeWrapper
: public SimpleRequest<IsPropertyAccessedViaTypeWrapper, bool(VarDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

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

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

class SynthesizeTypeWrapperInitializer
: public SimpleRequest<SynthesizeTypeWrapperInitializer,
ConstructorDecl *(NominalTypeDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

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

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

class SynthesizeTypeWrapperInitializerBody
: public SimpleRequest<SynthesizeTypeWrapperInitializerBody,
BraceStmt *(ConstructorDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

BraceStmt *evaluate(Evaluator &evaluator, ConstructorDecl *) 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
30 changes: 30 additions & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -399,3 +399,33 @@ SWIFT_REQUEST(TypeChecker, ClosureEffectsRequest,
SWIFT_REQUEST(TypeChecker, GetSourceFileAsyncNode,
AwaitExpr *(const SourceFile *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, GetTypeWrapper,
NominalTypeDecl *(NominalTypeDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, GetTypeWrapperType,
Type(NominalTypeDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, GetTypeWrapperStorage,
NominalTypeDecl *(NominalTypeDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, GetTypeWrapperProperty,
VarDecl *(NominalTypeDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, GetTypeWrapperStorageForProperty,
VarDecl *(VarDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, SynthesizeTypeWrappedPropertyGetterBody,
BraceStmt *(AccessorDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, SynthesizeTypeWrappedPropertySetterBody,
BraceStmt *(AccessorDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, IsPropertyAccessedViaTypeWrapper,
bool(VarDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, SynthesizeTypeWrapperInitializer,
ConstructorDecl *(NominalTypeDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, SynthesizeTypeWrapperInitializerBody,
BraceStmt *(ConstructorDecl *),
Cached, NoLocationInfo)
5 changes: 5 additions & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ EXPERIMENTAL_FEATURE(SendableCompletionHandlers)
/// Enables opaque type erasure without also enabling implict dynamic
EXPERIMENTAL_FEATURE(OpaqueTypeErasure)

/// Whether to enable experimental @typeWrapper feature which allows to
/// declare a type that controls access to all stored properties of the
/// wrapped type.
EXPERIMENTAL_FEATURE(TypeWrappers)

#undef EXPERIMENTAL_FEATURE
#undef UPCOMING_FEATURE
#undef SUPPRESSIBLE_LANGUAGE_FEATURE
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2935,6 +2935,10 @@ static bool usesFeatureSpecializeAttributeWithAvailability(Decl *decl) {
return false;
}

static bool usesFeatureTypeWrappers(Decl *decl) {
return decl->getAttrs().hasAttribute<TypeWrapperAttr>();
}

static void suppressingFeatureSpecializeAttributeWithAvailability(
PrintOptions &options,
llvm::function_ref<void()> action) {
Expand Down
3 changes: 2 additions & 1 deletion lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6733,7 +6733,8 @@ bool VarDecl::hasStorageOrWrapsStorage() const {
}

void VarDecl::visitAuxiliaryDecls(llvm::function_ref<void(VarDecl *)> visit) const {
if (getDeclContext()->isTypeContext() || isImplicit())
if (getDeclContext()->isTypeContext() ||
(isImplicit() && !isa<ParamDecl>(this)))
return;

if (getAttrs().hasAttribute<LazyAttr>()) {
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/TypeCheckRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1531,6 +1531,10 @@ void swift::simple_display(llvm::raw_ostream &out, CustomAttrTypeKind value) {
out << "property-wrapper";
return;

case CustomAttrTypeKind::TypeWrapper:
out << "type-wrapper";
return;

case CustomAttrTypeKind::GlobalActor:
out << "global-actor";
return;
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ add_swift_host_library(swiftSema STATIC
TypeCheckNameLookup.cpp
TypeCheckPattern.cpp
TypeCheckPropertyWrapper.cpp
TypeCheckTypeWrapper.cpp
TypeCheckProtocol.cpp
TypeCheckProtocolInference.cpp
TypeCheckRegex.cpp
Expand Down
Loading