Skip to content

Synthesize accessors for dynamic global variables #20570

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
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
11 changes: 9 additions & 2 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ class alignas(1 << DeclAlignInBits) Decl {
HasAnyUnavailableValues : 1
);

SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1,
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1,
/// If the module was or is being compiled with `-enable-testing`.
TestingEnabled : 1,

Expand All @@ -586,7 +586,10 @@ class alignas(1 << DeclAlignInBits) Decl {
HasResolvedImports : 1,

// If the module was or is being compiled with `-enable-private-imports`.
PrivateImportsEnabled : 1
PrivateImportsEnabled : 1,

// If the module is compiled with `-enable-implicit-dynamic`.
ImplicitDynamicEnabled : 1
);

SWIFT_INLINE_BITFIELD(PrecedenceGroupDecl, Decl, 1+2,
Expand Down Expand Up @@ -2594,6 +2597,10 @@ class ValueDecl : public Decl {
return isObjC() && isDynamic();
}

bool isNativeDynamic() const {
return !isObjC() && isDynamic();
}

/// Set whether this type is 'dynamic' or not.
void setIsDynamic(bool value);

Expand Down
8 changes: 8 additions & 0 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,14 @@ class ModuleDecl : public DeclContext, public TypeDecl {
Bits.ModuleDecl.TestingEnabled = enabled;
}

// Returns true if this module is compiled with implicit dynamic.
bool isImplicitDynamicEnabled() const {
return Bits.ModuleDecl.ImplicitDynamicEnabled;
}
void setImplicitDynamicEnabled(bool enabled = true) {
Bits.ModuleDecl.ImplicitDynamicEnabled = enabled;
}

/// Returns true if this module was or is begin compile with
/// `-enable-private-imports`.
bool arePrivateImportsEnabled() const {
Expand Down
4 changes: 0 additions & 4 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,6 @@ namespace swift {
/// was not compiled with -enable-testing.
bool EnableTestableAttrRequiresTestableModule = true;

/// If true, the 'dynamic' attribute is added to all applicable
/// declarations.
bool EnableImplicitDynamic = false;

///
/// Flags for developers
///
Expand Down
6 changes: 6 additions & 0 deletions include/swift/Frontend/FrontendOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ class FrontendOptions {
/// \see ModuleDecl::arePrivateImportsEnabled
bool EnablePrivateImports = false;


/// Indicates whether we add implicit dynamic.
///
/// \see ModuleDecl::isImplicitDynamicEnabled
bool EnableImplicitDynamic = false;

/// Enables the "fully resilient" resilience strategy.
///
/// \see ResilienceStrategy::Resilient
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/ASTVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3149,12 +3149,12 @@ class Verifier : public ASTWalker {
storageDecl->getWriteImpl() ==
WriteImplKind::StoredWithObservers ||
storageDecl->getWriteImpl() == WriteImplKind::MutableAddress) &&
storageDecl->isDynamic() && !storageDecl->isObjC()) &&
storageDecl->isNativeDynamic()) &&
// We allow a non dynamic getter if there is a dynamic read.
!(FD->isGetter() &&
(storageDecl->getReadImpl() == ReadImplKind::Read ||
storageDecl->getReadImpl() == ReadImplKind::Address) &&
storageDecl->isDynamic() && !storageDecl->isObjC())) {
storageDecl->isNativeDynamic())) {
Out << "Property and accessor do not match for 'dynamic'\n";
abort();
}
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1549,7 +1549,7 @@ getDirectReadWriteAccessStrategy(const AbstractStorageDecl *storage) {
return AccessStrategy::getStorage();
case ReadWriteImplKind::Stored: {
// If the storage isDynamic (and not @objc) use the accessors.
if (storage->isDynamic() && !storage->isObjC())
if (storage->isNativeDynamic())
return AccessStrategy::getMaterializeToTemporary(
getOpaqueReadAccessStrategy(storage, false),
getOpaqueWriteAccessStrategy(storage, false));
Expand Down Expand Up @@ -1623,7 +1623,7 @@ AbstractStorageDecl::getAccessStrategy(AccessSemantics semantics,
if (isPolymorphic(this))
return getOpaqueAccessStrategy(this, accessKind, /*dispatch*/ true);

if (isDynamic())
if (isNativeDynamic())
return getOpaqueAccessStrategy(this, accessKind, /*dispatch*/ false);

// If the storage is resilient to the given use DC (perhaps because
Expand Down
1 change: 1 addition & 0 deletions lib/Frontend/ArgsToFrontendOptionsConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ bool ArgsToFrontendOptionsConverter::convert(
Opts.EnableTesting |= Args.hasArg(OPT_enable_testing);
Opts.EnablePrivateImports |= Args.hasArg(OPT_enable_private_imports);
Opts.EnableResilience |= Args.hasArg(OPT_enable_resilience);
Opts.EnableImplicitDynamic |= Args.hasArg(OPT_enable_implicit_dynamic);

Opts.TrackSystemDeps |= Args.hasArg(OPT_track_system_dependencies);

Expand Down
1 change: 0 additions & 1 deletion lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,6 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
Opts.DebugConstraintSolver |= Args.hasArg(OPT_debug_constraints);
Opts.NamedLazyMemberLoading &= !Args.hasArg(OPT_disable_named_lazy_member_loading);
Opts.DebugGenericSignatures |= Args.hasArg(OPT_debug_generic_signatures);
Opts.EnableImplicitDynamic |= Args.hasArg(OPT_enable_implicit_dynamic);

if (Args.hasArg(OPT_verify_syntax_tree)) {
Opts.BuildSyntaxTree = true;
Expand Down
2 changes: 2 additions & 0 deletions lib/Frontend/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,8 @@ ModuleDecl *CompilerInstance::getMainModule() {
MainModule->setTestingEnabled();
if (Invocation.getFrontendOptions().EnablePrivateImports)
MainModule->setPrivateImportsEnabled();
if (Invocation.getFrontendOptions().EnableImplicitDynamic)
MainModule->setImplicitDynamicEnabled();

if (Invocation.getFrontendOptions().EnableResilience)
MainModule->setResilienceStrategy(ResilienceStrategy::Resilient);
Expand Down
5 changes: 3 additions & 2 deletions lib/SIL/SILDeclRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -964,13 +964,14 @@ bool SILDeclRef::isDynamicallyReplaceable() const {
return false;

if (kind == SILDeclRef::Kind::Destroyer ||
kind == SILDeclRef::Kind::Initializer) {
kind == SILDeclRef::Kind::Initializer ||
kind == SILDeclRef::Kind::GlobalAccessor) {
return false;
}

if (!hasDecl())
return false;

auto decl = getDecl();
return decl->isDynamic() && !decl->isObjC();
return decl->isNativeDynamic();
}
5 changes: 5 additions & 0 deletions lib/Sema/CodeSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2042,6 +2042,11 @@ void swift::maybeAddAccessorsToStorage(TypeChecker &TC,
return;

if (!dc->isTypeContext()) {
// dynamic globals need accessors.
if (dc->isModuleScopeContext() && storage->isNativeDynamic()) {
addTrivialAccessorsToStorage(storage, TC);
return;
}
// Fixed-layout global variables don't get accessors.
if (!storage->isResilient())
return;
Expand Down
5 changes: 1 addition & 4 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2175,7 +2175,7 @@ void TypeChecker::checkDynamicReplacementAttribute(ValueDecl *D) {
return;
}

if (D->isDynamic() && !D->isObjC()) {
if (D->isNativeDynamic()) {
diagnose(attr->getLocation(), diag::dynamic_replacement_must_not_be_dynamic,
D->getBaseName());
attr->setInvalid();
Expand Down Expand Up @@ -2451,9 +2451,6 @@ TypeChecker::diagnosticIfDeclCannotBePotentiallyUnavailable(const Decl *D) {
}

void TypeChecker::addImplicitDynamicAttribute(Decl *D) {
if (!getLangOpts().EnableImplicitDynamic)
return;

// Add the attribute if the decl kind allows it and it is not an accessor
// decl. Accessor decls should always infer the var/subscript's attribute.
if (!DeclAttribute::canAttributeAppearOnDecl(DAK_Dynamic, D) ||
Expand Down
12 changes: 5 additions & 7 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,11 @@ IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
if (!DeclAttribute::canAttributeAppearOnDecl(DAK_Dynamic, decl))
return false;

// Add dynamic if -enable-implicit-dynamic was requested.
if (decl->getModuleContext()->isImplicitDynamicEnabled()) {
TypeChecker::addImplicitDynamicAttribute(decl);
}

// If 'dynamic' was explicitly specified, check it.
if (decl->getAttrs().hasAttribute<DynamicAttr>()) {
if (decl->getASTContext().LangOpts.isSwiftVersionAtLeast(5))
Expand Down Expand Up @@ -2726,8 +2731,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
}

void visitSubscriptDecl(SubscriptDecl *SD) {
TC.addImplicitDynamicAttribute(SD);

TC.validateDecl(SD);

if (!SD->isInvalid()) {
Expand Down Expand Up @@ -3197,8 +3200,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
}

void visitVarDecl(VarDecl *VD) {
TC.addImplicitDynamicAttribute(VD);

// Delay type-checking on VarDecls until we see the corresponding
// PatternBindingDecl.

Expand Down Expand Up @@ -3250,8 +3251,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
}

void visitFuncDecl(FuncDecl *FD) {
TC.addImplicitDynamicAttribute(FD);

TC.validateDecl(FD);

if (!FD->isInvalid()) {
Expand Down Expand Up @@ -3368,7 +3367,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
}

void visitConstructorDecl(ConstructorDecl *CD) {
TC.addImplicitDynamicAttribute(CD);
TC.validateDecl(CD);

if (!CD->isInvalid()) {
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -1095,7 +1095,7 @@ class TypeChecker final : public LazyResolver {
void typeCheckDecl(Decl *D);

void checkDeclAttributesEarly(Decl *D);
void addImplicitDynamicAttribute(Decl *D);
static void addImplicitDynamicAttribute(Decl *D);
void checkDeclAttributes(Decl *D);
void checkDynamicReplacementAttribute(ValueDecl *D);
void checkTypeModifyingDeclAttributes(VarDecl *var);
Expand Down
2 changes: 1 addition & 1 deletion lib/TBDGen/TBDGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ void TBDGenVisitor::visitAbstractFunctionDecl(AbstractFunctionDecl *AFD) {
addSymbol(SILDeclRef(AFD));

// Add the global function pointer for a dynamically replaceable function.
if (AFD->isDynamic() && ! AFD->isObjC()) {
if (AFD->isNativeDynamic()) {
addSymbol(LinkEntity::forDynamicallyReplaceableFunctionVariable(AFD));
addSymbol(LinkEntity::forDynamicallyReplaceableFunctionImpl(AFD));
addSymbol(LinkEntity::forDynamicallyReplaceableFunctionKey(AFD));
Expand Down
9 changes: 9 additions & 0 deletions test/Interpreter/Inputs/dynamic_replacement_module.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#if MODULE
public dynamic var public_global_var = "public_global_var"

public dynamic func public_global_func() -> String {
return "public_global_func"
}
Expand Down Expand Up @@ -64,6 +66,8 @@ public enum PublicEnumeration<Q> {
}
}
#elseif MODULENODYNAMIC
public var public_global_var = "public_global_var"

public func public_global_func() -> String {
return "public_global_func"
}
Expand Down Expand Up @@ -134,6 +138,11 @@ import Module1

/// Public global functions, struct, class, and enum.

@_dynamicReplacement(for: public_global_var)
public var replacement_for_public_global_var : String {
return "replacement of public_global_var"
}

@_dynamicReplacement(for: public_global_func())
public func replacement_for_public_global_func() -> String {
return "replacement of " + public_global_func()
Expand Down
2 changes: 2 additions & 0 deletions test/Interpreter/dynamic_replacement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ func expectedResult(_ forOriginalLibrary: Bool, _ expectedOriginalString: Strin
}

func checkExpectedResults(forOriginalLibrary useOrig: Bool) {
expectTrue(public_global_var == expectedResult(useOrig, "public_global_var"))

expectTrue(public_global_func() ==
expectedResult(useOrig, "public_global_func"))
expectTrue(public_global_generic_func(Int.self) ==
Expand Down
11 changes: 11 additions & 0 deletions test/SILGen/dynamically_replaceable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,14 @@ extension GenericS {
}
}
}

dynamic var globalX = 0
// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable7globalXSivg : $@convention(thin) () -> Int
// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable7globalXSivs : $@convention(thin) (Int) -> ()
// CHECK-LABEL: sil hidden @$s23dynamically_replaceable7getsetXyS2iF
// CHECK: dynamic_function_ref @$s23dynamically_replaceable7globalXSivs
// CHECK: dynamic_function_ref @$s23dynamically_replaceable7globalXSivg
func getsetX(_ x: Int) -> Int {
globalX = x
return globalX
}