Skip to content

Commit 68876a6

Browse files
authored
Merge pull request #76636 from allevato/rich-identifiers
Support raw identifiers (backtick-delimited identifiers containing non-identifier characters).
2 parents ae0936f + d71b42e commit 68876a6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1052
-158
lines changed

include/swift/AST/ASTContext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,10 @@ class ASTContext final {
614614
/// are the real (physical) module names on disk.
615615
void setModuleAliases(const llvm::StringMap<StringRef> &aliasMap);
616616

617+
/// Adds a given alias to the map of Identifiers between module aliases and
618+
/// their actual names.
619+
void addModuleAlias(StringRef moduleAlias, StringRef realName);
620+
617621
/// Look up option used in \c getRealModuleName when module aliasing is applied.
618622
enum class ModuleAliasLookupOption {
619623
alwaysRealName,

include/swift/AST/ASTDemangler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,10 @@ class ASTBuilder {
306306

307307
static GenericTypeDecl *getAcceptableTypeDeclCandidate(ValueDecl *decl,
308308
Demangle::Node::Kind kind);
309+
310+
/// Returns an identifier with the given name, automatically removing any
311+
/// surrounding backticks that are present for raw identifiers.
312+
Identifier getIdentifier(StringRef name);
309313
};
310314

311315
SWIFT_END_INLINE_NAMESPACE

include/swift/AST/ASTPrinter.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,8 @@ class ASTPrinter {
293293
void printEscapedStringLiteral(StringRef str);
294294

295295
void printName(Identifier Name,
296-
PrintNameContext Context = PrintNameContext::Normal);
296+
PrintNameContext Context = PrintNameContext::Normal,
297+
bool IsSpecializedCxxType = false);
297298

298299
void setIndent(unsigned NumSpaces) {
299300
CurrentIndentation = NumSpaces;
@@ -444,9 +445,10 @@ void printWithCompatibilityFeatureChecks(ASTPrinter &printer,
444445
Decl *decl,
445446
llvm::function_ref<void()> printBody);
446447

447-
/// Determine whether we need to escape the given keyword within the
448-
/// given context, by wrapping it in backticks.
449-
bool escapeKeywordInContext(StringRef keyword, PrintNameContext context);
448+
/// Determine whether we need to escape the given name within the given
449+
/// context, by wrapping it in backticks.
450+
bool escapeIdentifierInContext(Identifier name, PrintNameContext context,
451+
bool isSpecializedCxxType = false);
450452

451453
} // namespace swift
452454

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6429,6 +6429,14 @@ ERROR(objc_enum_case_multi,none,
64296429
ERROR(objc_extension_not_class,none,
64306430
"'@objc' can only be applied to an extension of a class", ())
64316431

6432+
ERROR(objc_name_not_valid_identifier,none,
6433+
"'@objc' %0 name is not a valid Objective-C identifier",
6434+
(DescriptiveDeclKind))
6435+
ERROR(objc_cannot_infer_name_raw_identifier,none,
6436+
"cannot infer '@objc' %0 name because the Swift name is not a valid "
6437+
"Objective-C identifier; specify the name in '@objc' explicitly",
6438+
(DescriptiveDeclKind))
6439+
64326440
// If you change this, also change enum ObjCReason
64336441
#define OBJC_ATTR_SELECT "select{marked @_cdecl|marked dynamic|marked @objc|marked @objcMembers|marked @IBOutlet|marked @IBAction|marked @IBSegueAction|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override|an implementation of an @objc requirement|marked @IBInspectable|marked @GKInspectable|in an @objc extension of a class (without @nonobjc)|in an @objc @implementation extension of a class (without final or @nonobjc)|marked @objc by an access note}"
64346442

include/swift/AST/Identifier.h

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,19 +101,22 @@ class Identifier {
101101

102102
/// isOperator - Return true if this identifier is an operator, false if it is
103103
/// a normal identifier.
104-
/// FIXME: We should maybe cache this.
105104
bool isOperator() const {
106105
if (empty())
107106
return false;
108107
if (isEditorPlaceholder())
109108
return false;
110-
if ((unsigned char)Pointer[0] < 0x80)
111-
return isOperatorStartCodePoint((unsigned char)Pointer[0]);
112109

113110
// Handle the high unicode case out of line.
114111
return isOperatorSlow();
115112
}
116113

114+
/// Returns true if this identifier contains non-identifier characters and
115+
/// must always be escaped with backticks, even in contexts were other
116+
/// escaped identifiers could omit backticks (like keywords as argument
117+
/// labels).
118+
bool mustAlwaysBeEscaped() const;
119+
117120
bool isArithmeticOperator() const {
118121
return is("+") || is("-") || is("*") || is("/") || is("%");
119122
}
@@ -350,6 +353,10 @@ class DeclBaseName {
350353
return !isSpecial() && getIdentifier().isOperator();
351354
}
352355

356+
bool mustAlwaysBeEscaped() const {
357+
return !isSpecial() && getIdentifier().mustAlwaysBeEscaped();
358+
}
359+
353360
bool isEditorPlaceholder() const {
354361
return !isSpecial() && getIdentifier().isEditorPlaceholder();
355362
}
@@ -571,7 +578,12 @@ class DeclName {
571578
bool isOperator() const {
572579
return getBaseName().isOperator();
573580
}
574-
581+
582+
/// True if this name is an escaped identifier.
583+
bool mustAlwaysBeEscaped() const {
584+
return getBaseName().mustAlwaysBeEscaped();
585+
}
586+
575587
/// True if this name should be found by a decl ref or member ref under the
576588
/// name specified by 'refName'.
577589
///
@@ -728,6 +740,8 @@ class DeclNameRef {
728740
return FullName.isOperator();
729741
}
730742

743+
bool mustAlwaysBeEscaped() const { return FullName.mustAlwaysBeEscaped(); }
744+
731745
bool isCompoundName() const {
732746
return FullName.isCompoundName();
733747
}

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ LANGUAGE_FEATURE(NonescapableTypes, 446, "Nonescapable types")
252252
LANGUAGE_FEATURE(BuiltinEmplaceTypedThrows, 0, "Builtin.emplace typed throws")
253253
SUPPRESSIBLE_LANGUAGE_FEATURE(MemorySafetyAttributes, 458, "@unsafe attribute")
254254
LANGUAGE_FEATURE(ValueGenerics, 452, "Value generics feature (integer generics)")
255+
LANGUAGE_FEATURE(RawIdentifiers, 451, "Raw identifiers")
255256

256257
// Swift 6
257258
UPCOMING_FEATURE(ConciseMagicFile, 274, 6)

include/swift/Basic/Mangler.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ class Mangler {
114114
print(llvm::dbgs());
115115
}
116116

117+
/// Appends the given raw identifier to the buffer in the form required to
118+
/// mangle it. This handles the transformations needed for such identifiers
119+
/// to retain compatibility with older runtimes.
120+
static void
121+
appendRawIdentifierForRuntime(StringRef ident,
122+
llvm::SmallVectorImpl<char> &buffer);
123+
117124
protected:
118125
/// Removes the last characters of the buffer by setting it's size to a
119126
/// smaller value.
@@ -143,7 +150,7 @@ class Mangler {
143150
SWIFT_DEBUG_DUMP;
144151

145152
/// Appends a mangled identifier string.
146-
void appendIdentifier(StringRef ident);
153+
void appendIdentifier(StringRef ident, bool allowRawIdentifiers = true);
147154

148155
// NOTE: the addSubstitution functions perform the value computation before
149156
// the assignment because there is no sequence point synchronising the

include/swift/Demangling/ManglingUtils.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,16 @@ inline bool isWordEnd(char ch, char prevCh) {
5858
return false;
5959
}
6060

61+
/// Returns true if \p ch is a valid character which may appear at the start
62+
/// of a symbol mangling.
63+
inline bool isValidSymbolStart(char ch) {
64+
return isLetter(ch) || ch == '_' || ch == '$';
65+
}
66+
6167
/// Returns true if \p ch is a valid character which may appear in a symbol
62-
/// mangling.
68+
/// mangling anywhere other than the first character.
6369
inline bool isValidSymbolChar(char ch) {
64-
return isLetter(ch) || isDigit(ch) || ch == '_' || ch == '$';
70+
return isValidSymbolStart(ch) || isDigit(ch);
6571
}
6672

6773
/// Returns true if \p str contains any character which may not appear in a

include/swift/Frontend/ModuleInterfaceLoader.h

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -318,10 +318,11 @@ class ExplicitModuleMapParser {
318318
public:
319319
ExplicitModuleMapParser(llvm::BumpPtrAllocator &Allocator) : Saver(Allocator) {}
320320

321-
std::error_code
322-
parseSwiftExplicitModuleMap(llvm::MemoryBufferRef BufferRef,
323-
llvm::StringMap<ExplicitSwiftModuleInputInfo> &swiftModuleMap,
324-
llvm::StringMap<ExplicitClangModuleInputInfo> &clangModuleMap) {
321+
std::error_code parseSwiftExplicitModuleMap(
322+
llvm::MemoryBufferRef BufferRef,
323+
llvm::StringMap<ExplicitSwiftModuleInputInfo> &swiftModuleMap,
324+
llvm::StringMap<ExplicitClangModuleInputInfo> &clangModuleMap,
325+
llvm::StringMap<std::string> &moduleAliases) {
325326
using namespace llvm::yaml;
326327
// Use a new source manager instead of the one from ASTContext because we
327328
// don't want the JSON file to be persistent.
@@ -331,7 +332,8 @@ class ExplicitModuleMapParser {
331332
assert(DI != Stream.end() && "Failed to read a document");
332333
if (auto *MN = dyn_cast_or_null<SequenceNode>(DI->getRoot())) {
333334
for (auto &entry : *MN) {
334-
if (parseSingleModuleEntry(entry, swiftModuleMap, clangModuleMap)) {
335+
if (parseSingleModuleEntry(entry, swiftModuleMap, clangModuleMap,
336+
moduleAliases)) {
335337
return std::make_error_code(std::errc::invalid_argument);
336338
}
337339
}
@@ -359,16 +361,19 @@ class ExplicitModuleMapParser {
359361
llvm_unreachable("Unexpected JSON value for isFramework");
360362
}
361363

362-
bool parseSingleModuleEntry(llvm::yaml::Node &node,
363-
llvm::StringMap<ExplicitSwiftModuleInputInfo> &swiftModuleMap,
364-
llvm::StringMap<ExplicitClangModuleInputInfo> &clangModuleMap) {
364+
bool parseSingleModuleEntry(
365+
llvm::yaml::Node &node,
366+
llvm::StringMap<ExplicitSwiftModuleInputInfo> &swiftModuleMap,
367+
llvm::StringMap<ExplicitClangModuleInputInfo> &clangModuleMap,
368+
llvm::StringMap<std::string> &moduleAliases) {
365369
using namespace llvm::yaml;
366370
auto *mapNode = dyn_cast<MappingNode>(&node);
367371
if (!mapNode)
368372
return true;
369373
StringRef moduleName;
370374
std::optional<std::string> swiftModulePath, swiftModuleDocPath,
371-
swiftModuleSourceInfoPath, swiftModuleCacheKey, clangModuleCacheKey;
375+
swiftModuleSourceInfoPath, swiftModuleCacheKey, clangModuleCacheKey,
376+
moduleAlias;
372377
std::optional<std::vector<std::string>> headerDependencyPaths;
373378
std::string clangModuleMapPath = "", clangModulePath = "";
374379
bool isFramework = false, isSystem = false,
@@ -405,6 +410,8 @@ class ExplicitModuleMapParser {
405410
clangModuleCacheKey = val.str();
406411
} else if (key == "isBridgingHeaderDependency") {
407412
isBridgingHeaderDependency = parseBoolValue(val);
413+
} else if (key == "moduleAlias") {
414+
moduleAlias = val.str();
408415
} else {
409416
// Being forgiving for future fields.
410417
continue;
@@ -439,6 +446,9 @@ class ExplicitModuleMapParser {
439446
clangModuleCacheKey);
440447
didInsert = clangModuleMap.try_emplace(moduleName, std::move(entry)).second;
441448
}
449+
if (didInsert && moduleAlias.has_value()) {
450+
moduleAliases[*moduleAlias] = moduleName;
451+
}
442452
// Prevent duplicate module names.
443453
return !didInsert;
444454
}

include/swift/IDE/CompletionLookup.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
340340
void addValueBaseName(CodeCompletionResultBuilder &Builder,
341341
DeclBaseName Name);
342342

343+
void addIdentifier(CodeCompletionResultBuilder &Builder, Identifier Name);
344+
343345
void addLeadingDot(CodeCompletionResultBuilder &Builder);
344346

345347
void addTypeAnnotation(CodeCompletionResultBuilder &Builder, Type T,

include/swift/Parse/Lexer.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,15 @@ class Lexer {
395395
/// identifier, without escaping characters.
396396
static bool isIdentifier(StringRef identifier);
397397

398+
// Returns true if the given string is a raw identifier that must always
399+
// be escaped by backticks when printing it back in source form or writing
400+
// its name into runtime metadata.
401+
static bool identifierMustAlwaysBeEscaped(StringRef str);
402+
403+
/// Determines if the given string is a valid non-operator
404+
/// identifier if it were surrounded by backticks.
405+
static bool isValidAsEscapedIdentifier(StringRef identifier);
406+
398407
/// Determine the token kind of the string, given that it is a valid
399408
/// non-operator identifier. Return tok::identifier if the string is not a
400409
/// reserved word.

lib/AST/ASTContext.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2136,16 +2136,20 @@ void ASTContext::setModuleAliases(const llvm::StringMap<StringRef> &aliasMap) {
21362136
for (auto k: aliasMap.keys()) {
21372137
auto v = aliasMap.lookup(k);
21382138
if (!v.empty()) {
2139-
auto key = getIdentifier(k);
2140-
auto val = getIdentifier(v);
2141-
// key is a module alias, val is its corresponding real name
2142-
ModuleAliasMap[key] = std::make_pair(val, true);
2143-
// add an entry with an alias as key for an easier lookup later
2144-
ModuleAliasMap[val] = std::make_pair(key, false);
2139+
addModuleAlias(k, v);
21452140
}
21462141
}
21472142
}
21482143

2144+
void ASTContext::addModuleAlias(StringRef moduleAlias, StringRef realName) {
2145+
auto key = getIdentifier(moduleAlias);
2146+
auto val = getIdentifier(realName);
2147+
// key is a module alias, val is its corresponding real name
2148+
ModuleAliasMap[key] = std::make_pair(val, true);
2149+
// add an entry with an alias as key for an easier lookup later
2150+
ModuleAliasMap[val] = std::make_pair(key, false);
2151+
}
2152+
21492153
Identifier ASTContext::getRealModuleName(Identifier key, ModuleAliasLookupOption option) const {
21502154
auto found = ModuleAliasMap.find(key);
21512155
if (found == ModuleAliasMap.end())

0 commit comments

Comments
 (0)