Skip to content
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
7 changes: 7 additions & 0 deletions include/swift/AST/DeclAttr.def
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,13 @@ SIMPLE_DECL_ATTR(sensitive, Sensitive,
OnStruct | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
159)

SIMPLE_DECL_ATTR(_needsStringsInEmbedded, NeedsStringsInEmbedded,
OnAnyDecl | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
160)
SIMPLE_DECL_ATTR(_needsUnicodeDataTablesInEmbedded, NeedsUnicodeDataTablesInEmbedded,
OnAnyDecl | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
161)

LAST_DECL_ATTR(PreInverseGenerics)

#undef DECL_ATTR_ALIAS
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticsFrontend.def
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,8 @@ ERROR(objc_with_embedded,none,
"Objective-C interoperability cannot be enabled with embedded Swift.", ())
ERROR(no_allocations_without_embedded,none,
"-no-allocations is only applicable with embedded Swift.", ())
ERROR(enable_strings_without_embedded,none,
"-enable-strings is only applicable with embedded Swift.", ())
ERROR(no_swift_sources_with_embedded,none,
"embedded swift cannot be enabled in a compiler built without Swift sources", ())

Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -6829,6 +6829,11 @@ ERROR(conformance_availability_only_version_newer, none,
"conformance of %0 to %1 is only available in %2 %3 or newer",
(Type, Type, StringRef, llvm::VersionTuple))

ERROR(embedded_string_without_opt_in, none,
"String has large associated codesize cost; only available in embedded Swift by explicitly passing -enable-strings", ())
ERROR(embedded_string_needs_data_tables, none,
"This operation on String needs Unicode data tables; pass -enable-string=full-unicode-data-tables to acknowledge the associated codesize cost", ())

//------------------------------------------------------------------------------
// MARK: @discardableResult
//------------------------------------------------------------------------------
Expand Down
9 changes: 9 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ namespace swift {
TaskToThread,
};

enum class EmbeddedSwiftStringsLevel : uint8_t {
None,
Core,
FullWithUnicodeDataTables,
};

/// Describes the code size optimization behavior for code associated with
/// declarations that are marked unavailable.
enum class UnavailableDeclOptimization : uint8_t {
Expand Down Expand Up @@ -595,6 +601,9 @@ namespace swift {
/// Disables `DynamicActorIsolation` feature.
bool DisableDynamicActorIsolation = false;

/// Whether the user has opted in to using Swift.String via -enable-strings
EmbeddedSwiftStringsLevel EnableStringsInEmbeddedSwift = EmbeddedSwiftStringsLevel::None;

/// Whether or not to allow experimental features that are only available
/// in "production".
#ifdef NDEBUG
Expand Down
7 changes: 7 additions & 0 deletions include/swift/Option/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,13 @@ def no_allocations : Flag<["-"], "no-allocations">,
Flags<[FrontendOption, HelpHidden]>,
HelpText<"Diagnose any code that needs to heap allocate (classes, closures, etc.)">;

def enable_strings : Flag<["-"], "enable-strings">,
Copy link
Contributor

Choose a reason for hiding this comment

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

Would -enable-embedded-strings work? It's not a big deal, but -enable-strings seems a bit overly generic to me at first glance, unless we can think of other scenarios where String would be restricted outside of -embedded mode (and we could always introduce a more generic option later).

Copy link
Member

Choose a reason for hiding this comment

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

I don't think we want flags unique to embedded if we can avoid it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Strings are implicitly available by default, so unless you are in embedded mode this flag doesn't make sense. In this phrasing, how can it not be specific to embedded? In order to make it generic, I think it would have to be -disable-strings, which could theoretically work in any configuration (but it's unclear why you'd use it outside of embedded).

Flags<[FrontendOption, HelpHidden]>,
HelpText<"Allow using the String type in embedded Swift (which has a large associated codesize cost)">;
def enable_strings_full_unicode_data_tables : Flag<["-"], "enable-strings-full-unicode-data-tables">,
Flags<[FrontendOption, HelpHidden]>,
HelpText<"Allow using the String type in embedded Swift, including Unicode data tables (which has a large associated codesize cost)">;

// Platform options.
def enable_app_extension : Flag<["-"], "application-extension">,
Flags<[FrontendOption, NoInteractiveOption]>,
Expand Down
4 changes: 4 additions & 0 deletions lib/ASTGen/Sources/ASTGen/DeclAttrs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ extension ASTGenVisitor {
fatalError("unimplemented")
case .noObjCBridging:
fatalError("unimplemented")
case .needsStringsInEmbedded:
fatalError("unimplemented")
case .needsUnicodeDataTablesInEmbedded:
fatalError("unimplemented")
case .noRuntime:
fatalError("unimplemented")
case .nonSendable:
Expand Down
7 changes: 7 additions & 0 deletions lib/Driver/ToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,13 @@ ToolChain::constructInvocation(const CompileJobAction &job,
Arguments.push_back("-track-system-dependencies");
}

if (context.Args.hasArg(options::OPT_enable_strings)) {
Arguments.push_back("-enable-strings");
}
if (context.Args.hasArg(options::OPT_enable_strings_full_unicode_data_tables)) {
Arguments.push_back("-enable-strings-full-unicode-data-tables");
}

if (context.Args.hasFlag(options::OPT_static_executable,
options::OPT_no_static_executable, false) ||
context.Args.hasFlag(options::OPT_static_stdlib,
Expand Down
11 changes: 11 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1511,6 +1511,13 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
}
}

if (Args.hasArg(OPT_enable_strings)) {
Opts.EnableStringsInEmbeddedSwift = EmbeddedSwiftStringsLevel::Core;
}
if (Args.hasArg(OPT_enable_strings_full_unicode_data_tables)) {
Opts.EnableStringsInEmbeddedSwift = EmbeddedSwiftStringsLevel::FullWithUnicodeDataTables;
}

if (auto A = Args.getLastArg(OPT_checked_async_objc_bridging)) {
auto value = llvm::StringSwitch<std::optional<bool>>(A->getValue())
.Case("off", false)
Expand Down Expand Up @@ -3457,6 +3464,10 @@ bool CompilerInvocation::parseArgs(
Diags.diagnose(SourceLoc(), diag::no_allocations_without_embedded);
return true;
}
if (LangOpts.EnableStringsInEmbeddedSwift != EmbeddedSwiftStringsLevel::None) {
Diags.diagnose(SourceLoc(), diag::enable_strings_without_embedded);
return true;
}
}

// With Swift 6, enable @_spiOnly by default and forbid the old version.
Expand Down
2 changes: 2 additions & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
IGNORED_ATTR(NoRuntime)
IGNORED_ATTR(NoExistentials)
IGNORED_ATTR(NoObjCBridging)
IGNORED_ATTR(NeedsStringsInEmbedded)
IGNORED_ATTR(NeedsUnicodeDataTablesInEmbedded)
IGNORED_ATTR(EmitAssemblyVisionRemarks)
IGNORED_ATTR(ShowInInterface)
IGNORED_ATTR(SILGenName)
Expand Down
57 changes: 57 additions & 0 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3819,6 +3819,54 @@ diagnoseDeclAsyncAvailability(const ValueDecl *D, SourceRange R,
return true;
}

/// Diagnose uses of String when in Embedded Swift mode and strings are not
/// opted in.
static bool diagnoseStringUsageInEmbeddedSwift(const Decl *D, SourceRange R,
const Expr *call,
const ExportContext &Where) {
ASTContext &ctx = Where.getDeclContext()->getASTContext();
if (!ctx.LangOpts.hasFeature(Feature::Embedded))
return false;

bool DeclNeedsStringsCore = false;
bool DeclNeedsStringsFullUnicode = false;
if (D->getAttrs().hasAttribute<NeedsStringsInEmbeddedAttr>()) {
DeclNeedsStringsCore = true;
}
if (D->getAttrs().hasAttribute<NeedsUnicodeDataTablesInEmbeddedAttr>()) {
DeclNeedsStringsCore = true;
DeclNeedsStringsFullUnicode = true;
}

if (!DeclNeedsStringsCore && !DeclNeedsStringsFullUnicode)
return false;

DiagRef diagRef = diag::invalid_diagnostic;
switch (ctx.LangOpts.EnableStringsInEmbeddedSwift) {
case EmbeddedSwiftStringsLevel::None:
if (!DeclNeedsStringsCore)
return false;
diagRef = diag::embedded_string_without_opt_in;
break;
case EmbeddedSwiftStringsLevel::Core:
if (!DeclNeedsStringsFullUnicode)
return false;
diagRef = diag::embedded_string_needs_data_tables;
break;
case EmbeddedSwiftStringsLevel::FullWithUnicodeDataTables:
return false;
}

// Are we in an unavailable context? Then don't diagnose.
auto referencedPlatform = Where.getUnavailablePlatformKind();
if (referencedPlatform && (*referencedPlatform == PlatformKind::none))
return false;

SourceLoc diagLoc = call ? call->getLoc() : R.Start;
ctx.Diags.diagnose(diagLoc, diagRef);
return true;
}

/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
/// was emitted.
bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
Expand Down Expand Up @@ -3855,6 +3903,9 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
if (diagnoseDeclAsyncAvailability(D, R, call, Where))
return true;

if (diagnoseStringUsageInEmbeddedSwift(D, R, call, Where))
return true;

// Make sure not to diagnose an accessor's deprecation if we already
// complained about the property/subscript.
bool isAccessorWithDeprecatedStorage =
Expand Down Expand Up @@ -4359,6 +4410,12 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
return true;
}

if (diagnoseStringUsageInEmbeddedSwift(ext, SourceRange(loc), nullptr,
where)) {
maybeEmitAssociatedTypeNote();
return true;
}

// Diagnose (and possibly signal) for potential unavailability
auto maybeUnavail = TypeChecker::checkConformanceAvailability(
rootConf, ext, where);
Expand Down
2 changes: 2 additions & 0 deletions lib/Sema/TypeCheckDeclOverride.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1513,6 +1513,8 @@ namespace {
UNINTERESTING_ATTR(NoRuntime)
UNINTERESTING_ATTR(NoExistentials)
UNINTERESTING_ATTR(NoObjCBridging)
UNINTERESTING_ATTR(NeedsStringsInEmbedded)
UNINTERESTING_ATTR(NeedsUnicodeDataTablesInEmbedded)
UNINTERESTING_ATTR(Inlinable)
UNINTERESTING_ATTR(Effects)
UNINTERESTING_ATTR(Expose)
Expand Down
1 change: 1 addition & 0 deletions stdlib/public/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB)
SWIFT_COMPILE_FLAGS
${swift_stdlib_compile_flags} -Xcc -D__MACH__ -Xcc -D__APPLE__ -Xcc -ffreestanding -enable-experimental-feature Embedded
-Xfrontend -enable-ossa-modules
-Xfrontend -enable-strings-full-unicode-data-tables
MODULE_DIR "${CMAKE_BINARY_DIR}/lib/swift/embedded"
SDK "embedded"
ARCHITECTURE "${arch}"
Expand Down
5 changes: 5 additions & 0 deletions stdlib/public/core/Character.swift
Original file line number Diff line number Diff line change
Expand Up @@ -211,22 +211,27 @@ extension String {
}
}

@_needsUnicodeDataTablesInEmbedded
extension Character: Equatable {
@inlinable @inline(__always)
@_effects(readonly)
@_needsUnicodeDataTablesInEmbedded
Copy link
Contributor

Choose a reason for hiding this comment

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

In order for this to be intuitive for library authors, I think we should probably aim to support lexical scoping for this attribute so that you don't have to repeat it like this

public static func == (lhs: Character, rhs: Character) -> Bool {
return lhs._str == rhs._str
}
}

@_needsUnicodeDataTablesInEmbedded
extension Character: Comparable {
@inlinable @inline(__always)
@_effects(readonly)
@_needsUnicodeDataTablesInEmbedded
public static func < (lhs: Character, rhs: Character) -> Bool {
return lhs._str < rhs._str
}
}

@_needsUnicodeDataTablesInEmbedded
extension Character: Hashable {
// not @inlinable (performance)
/// Hashes the essential components of this value by feeding them into the
Expand Down
1 change: 1 addition & 0 deletions stdlib/public/core/String.swift
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ internal func unimplemented_utf8_32bit(
/// [equivalence]: http://www.unicode.org/glossary/#canonical_equivalent
@frozen
@_eagerMove
@_needsStringsInEmbedded
public struct String {
public // @SPI(Foundation)
var _guts: _StringGuts
Expand Down
13 changes: 13 additions & 0 deletions stdlib/public/core/StringComparable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ extension StringProtocol {
@_specialize(where Self == Substring, RHS == String)
@_specialize(where Self == Substring, RHS == Substring)
@_effects(readonly)
@_needsUnicodeDataTablesInEmbedded
public static func == <RHS: StringProtocol>(lhs: Self, rhs: RHS) -> Bool {
return _stringCompare(
lhs._wholeGuts, lhs._offsetRange,
Expand All @@ -28,6 +29,7 @@ extension StringProtocol {

@inlinable @inline(__always) // forward to other operator
@_effects(readonly)
@_needsUnicodeDataTablesInEmbedded
public static func != <RHS: StringProtocol>(lhs: Self, rhs: RHS) -> Bool {
return !(lhs == rhs)
}
Expand All @@ -38,6 +40,7 @@ extension StringProtocol {
@_specialize(where Self == Substring, RHS == String)
@_specialize(where Self == Substring, RHS == Substring)
@_effects(readonly)
@_needsUnicodeDataTablesInEmbedded
public static func < <RHS: StringProtocol>(lhs: Self, rhs: RHS) -> Bool {
return _stringCompare(
lhs._wholeGuts, lhs._offsetRange,
Expand All @@ -47,40 +50,48 @@ extension StringProtocol {

@inlinable @inline(__always) // forward to other operator
@_effects(readonly)
@_needsUnicodeDataTablesInEmbedded
public static func > <RHS: StringProtocol>(lhs: Self, rhs: RHS) -> Bool {
return rhs < lhs
}

@inlinable @inline(__always) // forward to other operator
@_effects(readonly)
@_needsUnicodeDataTablesInEmbedded
public static func <= <RHS: StringProtocol>(lhs: Self, rhs: RHS) -> Bool {
return !(rhs < lhs)
}

@inlinable @inline(__always) // forward to other operator
@_effects(readonly)
@_needsUnicodeDataTablesInEmbedded
public static func >= <RHS: StringProtocol>(lhs: Self, rhs: RHS) -> Bool {
return !(lhs < rhs)
}
}

@_needsUnicodeDataTablesInEmbedded
extension String: Equatable {
@inlinable @inline(__always) // For the bitwise comparison
@_effects(readonly)
@_semantics("string.equals")
@_needsUnicodeDataTablesInEmbedded
public static func == (lhs: String, rhs: String) -> Bool {
return _stringCompare(lhs._guts, rhs._guts, expecting: .equal)
}
}

@_needsUnicodeDataTablesInEmbedded
extension String: Comparable {
@inlinable @inline(__always) // For the bitwise comparison
@_effects(readonly)
@_needsUnicodeDataTablesInEmbedded
public static func < (lhs: String, rhs: String) -> Bool {
return _stringCompare(lhs._guts, rhs._guts, expecting: .less)
}
}

@_needsUnicodeDataTablesInEmbedded
extension Substring: Equatable {}

// TODO: Generalize `~=` over `StringProtocol` (https://github.com/apple/swift/issues/54896)
Expand All @@ -90,6 +101,7 @@ extension String {
@_alwaysEmitIntoClient
@inline(__always)
@_effects(readonly)
@_needsUnicodeDataTablesInEmbedded
public static func ~= (lhs: String, rhs: Substring) -> Bool {
return lhs == rhs
}
Expand All @@ -98,6 +110,7 @@ extension Substring {
@_alwaysEmitIntoClient
@inline(__always)
@_effects(readonly)
@_needsUnicodeDataTablesInEmbedded
public static func ~= (lhs: Substring, rhs: String) -> Bool {
return lhs == rhs
}
Expand Down
Loading