//===--- ParseSIL.cpp - SIL File Parsing logic ----------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "SILParser.h" #include "SILParserFunctionBuilder.h" #include "SILParserState.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/ConformanceLookup.h" #include "swift/AST/DiagnosticsParse.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/SILGenRequests.h" #include "swift/AST/SourceFile.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/Defer.h" #include "swift/Demangling/Demangle.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/ParseSILSupport.h" #include "swift/SIL/AbstractionPattern.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/OwnershipUtils.h" #include "swift/SIL/ParseTestSpecification.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILDebugScope.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILMoveOnlyDeinit.h" #include "swift/SIL/SILUndef.h" #include "swift/SIL/TypeLowering.h" #include "swift/Sema/SILTypeResolutionContext.h" #include "swift/Subsystems.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/SaveAndRestore.h" #include using namespace swift; static llvm::cl::opt ParseSerializedSIL("parse-serialized-sil", llvm::cl::desc("Parse the output of a serialized module")); static llvm::cl::opt DisableInputVerify("sil-disable-input-verify", llvm::cl::desc("Disable verification of input SIL"), llvm::cl::init(false)); // Option for testing -silgen-cleanup -enable-complete-ossa static llvm::cl::opt ParseIncompleteOSSA("parse-incomplete-ossa", llvm::cl::desc("Parse OSSA with incomplete lifetimes")); //===----------------------------------------------------------------------===// // SILParserState implementation //===----------------------------------------------------------------------===// SILParserState::~SILParserState() { if (!ForwardRefFns.empty()) { for (auto Entry : ForwardRefFns) { if (Entry.second.Loc.isValid()) { M.getASTContext().Diags.diagnose(Entry.second.Loc, diag::sil_use_of_undefined_value, Entry.first.str()); } } } // Turn any debug-info-only function declarations into zombies. markZombies(); } void SILParserState::markZombies() { for (auto *Fn : PotentialZombieFns) { if (Fn->isExternalDeclaration() && !Fn->isZombie()) { Fn->setInlined(); M.eraseFunction(Fn); } } } std::unique_ptr ParseSILModuleRequest::evaluate(Evaluator &evaluator, ASTLoweringDescriptor desc) const { auto *SF = desc.getSourceFileToParse(); assert(SF); auto bufferID = SF->getBufferID(); auto silMod = SILModule::createEmptyModule(desc.context, desc.conv, desc.opts); SILParserState parserState(*silMod.get()); Parser parser(bufferID, *SF, &parserState); PrettyStackTraceParser StackTrace(parser); if (ParseSerializedSIL) { silMod.get()->setParsedAsSerializedSIL(); } auto hadError = parser.parseTopLevelSIL(); if (hadError) { // The rest of the SIL pipeline expects well-formed SIL, so if we encounter // a parsing error, just return an empty SIL module. // // Because the SIL parser's notion of failing with an error is distinct from // the ASTContext's notion of having emitted a diagnostic, it's possible for // the parser to fail silently without emitting a diagnostic. This assertion // ensures that +asserts builds will fail fast. If you crash here, please go // back and add a diagnostic after identifying where the SIL parser failed. assert(SF->getASTContext().hadError() && "Failed to parse SIL but did not emit any errors!"); return SILModule::createEmptyModule(desc.context, desc.conv, desc.opts); } // Mark functions as zombies before calling SILVerifier as functions referred //to by debug scopes only can fail verifier checks parserState.markZombies(); // If SIL parsing succeeded, verify the generated SIL. if (!parser.Diags.hadAnyError() && !DisableInputVerify) { silMod->verify(/*SingleFunction=*/true, !ParseIncompleteOSSA); } return silMod; } //===----------------------------------------------------------------------===// // SILParser //===----------------------------------------------------------------------===// bool SILParser::parseSILIdentifier(Identifier &Result, SourceLoc &Loc, DiagRef D) { switch (P.Tok.getKind()) { case tok::identifier: case tok::dollarident: Result = P.Context.getIdentifier(P.Tok.getText()); break; case tok::string_literal: { // Drop the double quotes. StringRef rawString = P.Tok.getText().drop_front().drop_back(); Result = P.Context.getIdentifier(rawString); break; } case tok::oper_binary_unspaced: // fixme? case tok::oper_binary_spaced: case tok::kw_init: // A binary operator or `init` can be part of a SILDeclRef. Result = P.Context.getIdentifier(P.Tok.getText()); break; default: // If it's some other keyword, grab an identifier for it. if (P.Tok.isKeyword()) { Result = P.Context.getIdentifier(P.Tok.getText()); break; } P.diagnose(P.Tok, D); return true; } Loc = P.Tok.getLoc(); P.consumeToken(); return false; } bool SILParser::parseVerbatim(StringRef name) { Identifier tok; SourceLoc loc; if (parseSILIdentifier(tok, loc, diag::expected_tok_in_sil_instr, name)) { return true; } if (tok.str() != name) { P.diagnose(loc, diag::expected_tok_in_sil_instr, name); return true; } return false; } SILParser::~SILParser() { for (auto &Entry : ForwardRefLocalValues) { if (ValueBase *dummyVal = LocalValues[Entry.first()]) { dummyVal->replaceAllUsesWith( SILUndef::get(dummyVal->getFunction(), dummyVal->getType())); ::delete cast(dummyVal); } } } /// diagnoseProblems - After a function is fully parse, emit any diagnostics /// for errors and return true if there were any. bool SILParser::diagnoseProblems() { // Check for any uses of basic blocks that were not defined. if (!UndefinedBlocks.empty()) { // FIXME: These are going to come out in nondeterministic order. for (auto Entry : UndefinedBlocks) P.diagnose(Entry.second.Loc, diag::sil_undefined_basicblock_use, Entry.second.Item); HadError = true; } if (!ForwardRefLocalValues.empty()) { // FIXME: These are going to come out in nondeterministic order. for (auto &Entry : ForwardRefLocalValues) P.diagnose(Entry.second, diag::sil_use_of_undefined_value, Entry.first()); HadError = true; } return HadError; } /// getGlobalNameForDefinition - Given a definition of a global name, look /// it up and return an appropriate SIL function. SILFunction *SILParser::getGlobalNameForDefinition(Identifier name, CanSILFunctionType ty, SourceLoc sourceLoc) { SILParserFunctionBuilder builder(SILMod); auto silLoc = RegularLocation(sourceLoc); // Check to see if a function of this name has been forward referenced. If so // complete the forward reference. auto iter = TUState.ForwardRefFns.find(name); if (iter != TUState.ForwardRefFns.end()) { SILFunction *fn = iter->second.Item; // Verify that the types match up. if (fn->getLoweredFunctionType() != ty) { P.diagnose(sourceLoc, diag::sil_value_use_type_mismatch, name.str(), fn->getLoweredFunctionType(), ty); P.diagnose(iter->second.Loc, diag::sil_prior_reference); fn = builder.createFunctionForForwardReference("" /*name*/, ty, silLoc); } assert(fn->isExternalDeclaration() && "Forward defns cannot have bodies!"); TUState.ForwardRefFns.erase(iter); // Move the function to this position in the module. // // FIXME: Should we move this functionality into SILParserFunctionBuilder? SILMod.getFunctionList().remove(fn); SILMod.getFunctionList().push_back(fn); return fn; } // If we don't have a forward reference, make sure the function hasn't been // defined already. if (SILMod.lookUpFunction(name.str()) != nullptr) { P.diagnose(sourceLoc, diag::sil_value_redefinition, name.str()); return builder.createFunctionForForwardReference("" /*name*/, ty, silLoc); } // Otherwise, this definition is the first use of this name. return builder.createFunctionForForwardReference(name.str(), ty, silLoc); } /// getGlobalNameForReference - Given a reference to a global name, look it /// up and return an appropriate SIL function. SILFunction *SILParser::getGlobalNameForReference(Identifier name, CanSILFunctionType funcTy, SourceLoc sourceLoc, bool ignoreFwdRef) { SILParserFunctionBuilder builder(SILMod); auto silLoc = RegularLocation(sourceLoc); // Check to see if we have a function by this name already. if (SILFunction *fn = SILMod.lookUpFunction(name.str())) { // If so, check for matching types. if (fn->getLoweredFunctionType() == funcTy) { return fn; } P.diagnose(sourceLoc, diag::sil_value_use_type_mismatch, name.str(), fn->getLoweredFunctionType(), funcTy); return builder.createFunctionForForwardReference("" /*name*/, funcTy, silLoc); } // If we didn't find a function, create a new one - it must be a forward // reference. auto *fn = builder.createFunctionForForwardReference(name.str(), funcTy, silLoc); TUState.ForwardRefFns[name] = {fn, ignoreFwdRef ? SourceLoc() : sourceLoc}; return fn; } /// getBBForDefinition - Return the SILBasicBlock for a definition of the /// specified block. SILBasicBlock *SILParser::getBBForDefinition(Identifier Name, SourceLoc Loc) { // If there was no name specified for this block, just create a new one. if (Name.empty()) return F->createBasicBlock(); SILBasicBlock *&BB = BlocksByName[Name]; // If the block has never been named yet, just create it. if (BB == nullptr) return BB = F->createBasicBlock(); // If it already exists, it was either a forward reference or a redefinition. // If it is a forward reference, it should be in our undefined set. if (!UndefinedBlocks.erase(BB)) { // If we have a redefinition, return a new BB to avoid inserting // instructions after the terminator. P.diagnose(Loc, diag::sil_basicblock_redefinition, Name); HadError = true; return F->createBasicBlock(); } // FIXME: Splice the block to the end of the function so they come out in the // right order. return BB; } /// getBBForReference - return the SILBasicBlock of the specified name. The /// source location is used to diagnose a failure if the block ends up never /// being defined. SILBasicBlock *SILParser::getBBForReference(Identifier Name, SourceLoc Loc) { // If the block has already been created, use it. SILBasicBlock *&BB = BlocksByName[Name]; if (BB != nullptr) return BB; // Otherwise, create it and remember that this is a forward reference so // that we can diagnose use without definition problems. BB = F->createBasicBlock(); UndefinedBlocks[BB] = {Name, Loc}; return BB; } /// sil-global-name: /// '@' identifier bool SILParser::parseGlobalName(Identifier &Name) { return P.parseToken(tok::at_sign, diag::expected_sil_value_name) || parseSILIdentifier(Name, diag::expected_sil_value_name); } /// getLocalValue - Get a reference to a local value with the specified name /// and type. SILValue SILParser::getLocalValue(UnresolvedValueName Name, SILType Type, SILLocation Loc, SILBuilder &B) { if (Name.isUndef()) return SILUndef::get(B.getFunction(), Type); // Check to see if this is already defined. ValueBase *&Entry = LocalValues[Name.Name]; if (Entry) { // If this value is already defined, check it to make sure types match. SILType EntryTy = Entry->getType(); if (EntryTy != Type) { HadError = true; P.diagnose(Name.NameLoc, diag::sil_value_use_type_mismatch, Name.Name, EntryTy.getRawASTType(), Type.getRawASTType()); // Make sure to return something of the requested type. return SILUndef::get(B.getFunction(), Type); } return SILValue(Entry); } // Otherwise, this is a forward reference. Create a dummy node to represent // it until we see a real definition. ForwardRefLocalValues[Name.Name] = Name.NameLoc; Entry = ::new PlaceholderValue(&B.getFunction(), Type); return Entry; } /// setLocalValue - When an instruction or block argument is defined, this /// method is used to register it and update our symbol table. void SILParser::setLocalValue(ValueBase *Value, StringRef Name, SourceLoc NameLoc) { ValueBase *&Entry = LocalValues[Name]; // If this value was already defined, it is either a redefinition, or a // specification for a forward referenced value. if (Entry) { if (!ForwardRefLocalValues.erase(Name)) { P.diagnose(NameLoc, diag::sil_value_redefinition, Name); HadError = true; return; } // If the forward reference was of the wrong type, diagnose this now. if (Entry->getType() != Value->getType()) { P.diagnose(NameLoc, diag::sil_value_def_type_mismatch, Name, Entry->getType().getRawASTType(), Value->getType().getRawASTType()); HadError = true; } else { if (TestSpecsWithRefs.find(Name) != TestSpecsWithRefs.end()) { for (auto *tsi : TestSpecsWithRefs[Name]) { tsi->setValueForName(Name, Value); } } // Forward references only live here if they have a single result. Entry->replaceAllUsesWith(Value); ::delete cast(Entry); } Entry = Value; return; } if (TestSpecsWithRefs.find(Name) != TestSpecsWithRefs.end()) { for (auto *tsi : TestSpecsWithRefs[Name]) { tsi->setValueForName(Name, Value); } } // Otherwise, just store it in our map. Entry = Value; } //===----------------------------------------------------------------------===// // SIL Parsing Logic //===----------------------------------------------------------------------===// /// parseSILLinkage - Parse a linkage specifier if present. /// sil-linkage: /// /*empty*/ // default depends on whether this is a definition /// 'public' /// 'hidden' /// 'shared' /// 'private' /// 'public_external' /// 'hidden_external' /// 'private_external' static bool parseSILLinkage(std::optional &Result, Parser &P) { // Begin by initializing result to our base value of None. Result = std::nullopt; // Unfortunate collision with access control keywords. if (P.Tok.is(tok::kw_public)) { Result = SILLinkage::Public; P.consumeToken(); return false; } // Unfortunate collision with access control keywords. if (P.Tok.is(tok::kw_private)) { Result = SILLinkage::Private; P.consumeToken(); return false; } // If we do not have an identifier, bail. All SILLinkages that we are parsing // are identifiers. if (P.Tok.isNot(tok::identifier)) return false; // Then use a string switch to try and parse the identifier. Result = llvm::StringSwitch>(P.Tok.getText()) .Case("non_abi", SILLinkage::PublicNonABI) .Case("package_non_abi", SILLinkage::PackageNonABI) .Case("package", SILLinkage::Package) .Case("hidden", SILLinkage::Hidden) .Case("shared", SILLinkage::Shared) .Case("public_external", SILLinkage::PublicExternal) .Case("package_external", SILLinkage::PackageExternal) .Case("hidden_external", SILLinkage::HiddenExternal) .Default(std::nullopt); // If we succeed, consume the token. if (Result) { P.consumeToken(tok::identifier); } return false; } /// Given whether it's known to be a definition, resolve an optional /// SIL linkage to a real one. static SILLinkage resolveSILLinkage(std::optional linkage, bool isDefinition) { if (linkage.has_value()) { return linkage.value(); } else if (isDefinition) { return SILLinkage::DefaultForDefinition; } else { return SILLinkage::DefaultForDeclaration; } } namespace { using SILOptionalAttrValue = std::optional>; } // namespace /// Returns false if no optional exists. Returns true on both success and /// failure. On success, the Result string is nonempty. If the optional is /// assigned to an integer value using an equal, \p value contains the parsed /// value. Otherwise, value is set to the maximum uint64_t. /// /// Example: [alignment=$NUM] static bool parseSILOptional(StringRef &parsedName, SourceLoc &parsedNameLoc, SILOptionalAttrValue &parsedValue, SourceLoc &parsedValueLoc, SILParser &parser) { if (!parser.P.consumeIf(tok::l_square)) return false; Identifier parsedNameId; if (parser.parseSILIdentifier(parsedNameId, parsedNameLoc, diag::expected_in_attribute_list)) return true; parsedName = parsedNameId.str(); uint64_t parsedIntValue = ~uint64_t(0); Identifier parsedStringId; if (parser.P.consumeIf(tok::equal)) { auto currentTok = parser.P.Tok; parsedValueLoc = currentTok.getLoc(); if (currentTok.is(tok::integer_literal)) { if (parser.parseInteger(parsedIntValue, diag::expected_in_attribute_list)) { return true; } parsedValue = parsedIntValue; } else { if (parser.parseSILIdentifier(parsedStringId, parsedValueLoc, diag::expected_in_attribute_list)) { return true; } parsedValue = parsedStringId.str(); } } if (parser.P.parseToken(tok::r_square, diag::expected_in_attribute_list)) return true; return true; } static bool parseSILOptional(StringRef &attrName, SourceLoc &attrLoc, SILParser &SP) { SILOptionalAttrValue parsedValue; SourceLoc parsedValueLoc; return parseSILOptional(attrName, attrLoc, parsedValue, parsedValueLoc, SP); } static bool parseSILOptional(StringRef &attrName, SILParser &SP) { SourceLoc attrLoc; return parseSILOptional(attrName, attrLoc, SP); } /// Parse an option attribute ('[' Expected ']')? static bool parseSILOptional(bool &Result, SILParser &SP, StringRef Expected) { StringRef Optional; SourceLoc Loc; if (parseSILOptional(Optional, Loc, SP)) { if (Optional != Expected) { SP.P.diagnose(Loc, diag::sil_invalid_attribute_for_expected, Optional, Expected); return true; } Result = true; } return false; } // If the qualifier string is unrecognized, then diagnose and fail. // // If the qualifier is absent, then succeed and set the result to None. // The caller can decide how to proceed with an absent qualifier. // // Usage: // auto parseQualifierName = [](StringRef Str) { // return llvm::StringSwitch>(Str) // .Case("one", SomeQualifier::One) // .Case("two", SomeQualifier::Two) // .Default(None); // }; // if (parseSILQualifier(Qualifier, parseQualifierName)) // return true; template bool SILParser::parseSILQualifier( std::optional &result, llvm::function_ref(StringRef)> parseName) { auto loc = P.Tok.getLoc(); StringRef Str; // If we do not parse '[' ... ']', if (!parseSILOptional(Str, *this)) { result = std::nullopt; return false; } result = parseName(Str); if (!result) { P.diagnose(loc, diag::unrecognized_sil_qualifier); return true; } return false; } /// Remap RequirementReps to Requirements. void SILParser::convertRequirements(ArrayRef From, SmallVectorImpl &To, SmallVectorImpl &typeErasedParams) { if (From.empty()) { To.clear(); return; } // Use parser lexical scopes to resolve references // to the generic parameters. auto ResolveToInterfaceType = [&](TypeRepr *TyR) -> Type { return performTypeResolution(TyR, /*IsSILType=*/false, ContextGenericSig, ContextGenericParams); }; for (auto &Req : From) { if (Req.getKind() == RequirementReprKind::SameType) { auto FirstType = ResolveToInterfaceType(Req.getFirstTypeRepr()); auto SecondType = ResolveToInterfaceType(Req.getSecondTypeRepr()); Requirement ConvertedRequirement(RequirementKind::SameType, FirstType, SecondType); To.push_back(ConvertedRequirement); continue; } if (Req.getKind() == RequirementReprKind::TypeConstraint) { auto Subject = ResolveToInterfaceType(Req.getSubjectRepr()); auto Constraint = ResolveToInterfaceType(Req.getConstraintRepr()); Requirement ConvertedRequirement(RequirementKind::Conformance, Subject, Constraint); To.push_back(ConvertedRequirement); continue; } if (Req.getKind() == RequirementReprKind::LayoutConstraint) { auto Subject = ResolveToInterfaceType(Req.getSubjectRepr()); Requirement ConvertedRequirement(RequirementKind::Layout, Subject, Req.getLayoutConstraint()); To.push_back(ConvertedRequirement); if (auto *attributedTy = dyn_cast(Req.getSubjectRepr())) { if (attributedTy->has(TypeAttrKind::NoMetadata)) { typeErasedParams.push_back(Subject); } } continue; } llvm_unreachable("Unsupported requirement kind"); } } static bool parseDeclSILOptional( bool *isTransparent, SerializedKind_t *serializedKind, bool *isCanonical, bool *hasOwnershipSSA, IsThunk_t *isThunk, IsDynamicallyReplaceable_t *isDynamic, IsDistributed_t *isDistributed, IsRuntimeAccessible_t *isRuntimeAccessible, ForceEnableLexicalLifetimes_t *forceEnableLexicalLifetimes, UseStackForPackMetadata_t *useStackForPackMetadata, bool *hasUnsafeNonEscapableResult, IsExactSelfClass_t *isExactSelfClass, SILFunction **dynamicallyReplacedFunction, SILFunction **usedAdHocRequirementWitness, Identifier *objCReplacementFor, SILFunction::Purpose *specialPurpose, Inline_t *inlineStrategy, OptimizationMode *optimizationMode, PerformanceConstraints *perfConstraints, bool *isPerformanceConstraint, bool *markedAsUsed, StringRef *asmName, StringRef *section, bool *isLet, bool *isWeakImported, bool *needStackProtection, bool *isSpecialized, AvailabilityRange *availability, bool *isWithoutActuallyEscapingThunk, SmallVectorImpl *Semantics, SmallVectorImpl *SpecAttrs, ValueDecl **ClangDecl, EffectsKind *MRK, ActorIsolation *actorIsolation, SILParser &SP, SILModule &M) { while (SP.P.consumeIf(tok::l_square)) { if (isLet && SP.P.Tok.is(tok::kw_let)) { *isLet = true; SP.P.consumeToken(tok::kw_let); SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); continue; } else if (SP.P.Tok.isNot(tok::identifier)) { SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); return true; } else if (isTransparent && SP.P.Tok.getText() == "transparent") *isTransparent = true; else if (serializedKind && SP.P.Tok.getText() == "serialized") *serializedKind = IsSerialized; else if (serializedKind && SP.P.Tok.getText() == "serialized_for_package") *serializedKind = IsSerializedForPackage; else if (isDynamic && SP.P.Tok.getText() == "dynamically_replacable") *isDynamic = IsDynamic; else if (isDistributed && SP.P.Tok.getText() == "distributed") *isDistributed = IsDistributed; else if (isRuntimeAccessible && SP.P.Tok.getText() == "runtime_accessible") *isRuntimeAccessible = IsRuntimeAccessible; else if (forceEnableLexicalLifetimes && SP.P.Tok.getText() == "lexical_lifetimes") *forceEnableLexicalLifetimes = DoForceEnableLexicalLifetimes; else if (useStackForPackMetadata && SP.P.Tok.getText() == "no_onstack_pack_metadata") *useStackForPackMetadata = DoNotUseStackForPackMetadata; else if (hasUnsafeNonEscapableResult && SP.P.Tok.getText() == "unsafe_nonescapable_result") *hasUnsafeNonEscapableResult = hasUnsafeNonEscapableResult; else if (isExactSelfClass && SP.P.Tok.getText() == "exact_self_class") *isExactSelfClass = IsExactSelfClass; else if (isCanonical && SP.P.Tok.getText() == "canonical") *isCanonical = true; else if (hasOwnershipSSA && SP.P.Tok.getText() == "ossa") *hasOwnershipSSA = true; else if (needStackProtection && SP.P.Tok.getText() == "stack_protection") *needStackProtection = true; else if (isSpecialized && SP.P.Tok.getText() == "specialized") *isSpecialized = true; else if (isThunk && SP.P.Tok.getText() == "thunk") *isThunk = IsThunk; else if (isThunk && SP.P.Tok.getText() == "signature_optimized_thunk") *isThunk = IsSignatureOptimizedThunk; else if (isThunk && SP.P.Tok.getText() == "reabstraction_thunk") *isThunk = IsReabstractionThunk; else if (isThunk && SP.P.Tok.getText() == "back_deployed_thunk") *isThunk = IsBackDeployedThunk; else if (isWithoutActuallyEscapingThunk && SP.P.Tok.getText() == "without_actually_escaping") *isWithoutActuallyEscapingThunk = true; else if (specialPurpose && SP.P.Tok.getText() == "global_init") *specialPurpose = SILFunction::Purpose::GlobalInit; else if (specialPurpose && SP.P.Tok.getText() == "lazy_getter") *specialPurpose = SILFunction::Purpose::LazyPropertyGetter; else if (specialPurpose && SP.P.Tok.getText() == "global_init_once_fn") *specialPurpose = SILFunction::Purpose::GlobalInitOnceFunction; else if (isWeakImported && SP.P.Tok.getText() == "weak_imported") { if (M.getASTContext().LangOpts.Target.isOSBinFormatCOFF()) SP.P.diagnose(SP.P.Tok, diag::attr_name_unsupported_on_target, SP.P.Tok.getText(), M.getASTContext().LangOpts.Target.str()); else *isWeakImported = true; } else if (availability && SP.P.Tok.getText() == "available") { SP.P.consumeToken(tok::identifier); SourceRange range; llvm::VersionTuple version; if (SP.P.parseVersionTuple(version, range, diag::sil_availability_expected_version)) return true; *availability = AvailabilityRange(version); SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); continue; } else if (inlineStrategy && SP.P.Tok.getText() == "noinline") *inlineStrategy = NoInline; else if (optimizationMode && SP.P.Tok.getText() == "Onone") *optimizationMode = OptimizationMode::NoOptimization; else if (optimizationMode && SP.P.Tok.getText() == "Ospeed") *optimizationMode = OptimizationMode::ForSpeed; else if (optimizationMode && SP.P.Tok.getText() == "Osize") *optimizationMode = OptimizationMode::ForSize; else if (perfConstraints && SP.P.Tok.getText() == "no_locks") *perfConstraints = PerformanceConstraints::NoLocks; else if (perfConstraints && SP.P.Tok.getText() == "no_allocation") *perfConstraints = PerformanceConstraints::NoAllocation; else if (perfConstraints && SP.P.Tok.getText() == "no_runtime") *perfConstraints = PerformanceConstraints::NoRuntime; else if (perfConstraints && SP.P.Tok.getText() == "no_existentials") *perfConstraints = PerformanceConstraints::NoExistentials; else if (perfConstraints && SP.P.Tok.getText() == "no_objc_bridging") *perfConstraints = PerformanceConstraints::NoObjCBridging; else if (perfConstraints && SP.P.Tok.getText() == "manual_ownership") *perfConstraints = PerformanceConstraints::ManualOwnership; else if (isPerformanceConstraint && SP.P.Tok.getText() == "perf_constraint") *isPerformanceConstraint = true; else if (markedAsUsed && SP.P.Tok.getText() == "used") *markedAsUsed = true; else if (actorIsolation && SP.P.Tok.getText() == "isolation") { SP.P.consumeToken(tok::identifier); if (SP.P.Tok.getKind() != tok::string_literal) { SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); return true; } StringRef rawString = SP.P.Tok.getText().drop_front().drop_back(); // TODO: By using a raw string here, we can perhaps put in a simple string // representation of an actor that can be parsed back. For now this is // just a quick hack so we can write tests. auto optIsolation = ActorIsolation::forSILString( SP.P.Context.getIdentifier(rawString).str()); if (!optIsolation) { SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); return true; } *actorIsolation = *optIsolation; SP.P.consumeToken(tok::string_literal); SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); continue; } else if (asmName && SP.P.Tok.getText() == "asmname") { SP.P.consumeToken(tok::identifier); if (SP.P.Tok.getKind() != tok::string_literal) { SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); return true; } // Drop the double quotes. StringRef rawString = SP.P.Tok.getText().drop_front().drop_back(); *asmName = SP.P.Context.getIdentifier(rawString).str(); SP.P.consumeToken(tok::string_literal); SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); continue; } else if (section && SP.P.Tok.getText() == "section") { SP.P.consumeToken(tok::identifier); if (SP.P.Tok.getKind() != tok::string_literal) { SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); return true; } // Drop the double quotes. StringRef rawString = SP.P.Tok.getText().drop_front().drop_back(); *section = SP.P.Context.getIdentifier(rawString).str(); SP.P.consumeToken(tok::string_literal); SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); continue; } else if (inlineStrategy && SP.P.Tok.getText() == "heuristic_always_inline") *inlineStrategy = HeuristicAlwaysInline; else if (inlineStrategy && SP.P.Tok.getText() == "always_inline") *inlineStrategy = AlwaysInline; else if (MRK && SP.P.Tok.getText() == "readnone") *MRK = EffectsKind::ReadNone; else if (MRK && SP.P.Tok.getText() == "readonly") *MRK = EffectsKind::ReadOnly; else if (MRK && SP.P.Tok.getText() == "readwrite") *MRK = EffectsKind::ReadWrite; else if (MRK && SP.P.Tok.getText() == "releasenone") *MRK = EffectsKind::ReleaseNone; else if (dynamicallyReplacedFunction && SP.P.Tok.getText() == "dynamic_replacement_for") { SP.P.consumeToken(tok::identifier); if (SP.P.Tok.getKind() != tok::string_literal) { SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); return true; } // Drop the double quotes. StringRef replacedFunc = SP.P.Tok.getText().drop_front().drop_back(); SILFunction *Func = M.lookUpFunction(replacedFunc.str()); if (!Func) { Identifier Id = SP.P.Context.getIdentifier(replacedFunc); SP.P.diagnose(SP.P.Tok, diag::sil_dynamically_replaced_func_not_found, Id); return true; } *dynamicallyReplacedFunction = Func; SP.P.consumeToken(tok::string_literal); SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); continue; } else if (usedAdHocRequirementWitness && SP.P.Tok.getText() == "ref_adhoc_requirement_witness") { SP.P.consumeToken(tok::identifier); if (SP.P.Tok.getKind() != tok::string_literal) { SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); return true; } // Drop the double quotes. StringRef witnessFunc = SP.P.Tok.getText().drop_front().drop_back(); SILFunction *Func = M.lookUpFunction(witnessFunc.str()); if (!Func) { Identifier Id = SP.P.Context.getIdentifier(witnessFunc); SP.P.diagnose(SP.P.Tok, diag::sil_adhoc_requirement_witness_func_not_found, Id); return true; } *usedAdHocRequirementWitness = Func; SP.P.consumeToken(tok::string_literal); SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); continue; } else if (objCReplacementFor && SP.P.Tok.getText() == "objc_replacement_for") { SP.P.consumeToken(tok::identifier); if (SP.P.Tok.getKind() != tok::string_literal) { SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); return true; } // Drop the double quotes. StringRef replacedFunc = SP.P.Tok.getText().drop_front().drop_back(); *objCReplacementFor = SP.P.Context.getIdentifier(replacedFunc); SP.P.consumeToken(tok::string_literal); SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); continue; } else if (Semantics && SP.P.Tok.getText() == "_semantics") { SP.P.consumeToken(tok::identifier); if (SP.P.Tok.getKind() != tok::string_literal) { SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); return true; } // Drop the double quotes. StringRef rawString = SP.P.Tok.getText().drop_front().drop_back(); Semantics->push_back(rawString.str()); SP.P.consumeToken(tok::string_literal); SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); continue; } else if (SpecAttrs && SP.P.Tok.getText() == "_specialize") { SourceLoc AtLoc = SP.P.Tok.getLoc(); SourceLoc Loc(AtLoc); // Parse a _specialized attribute, building a parsed substitution list // and pushing a new ParsedSpecAttr on the SpecAttrs list. Conformances // cannot be generated until the function declaration is fully parsed so // that the function's generic signature can be consulted. ParsedSpecAttr SpecAttr; SpecAttr.requirements = {}; SpecAttr.exported = false; SpecAttr.kind = SILSpecializeAttr::SpecializationKind::Full; AbstractSpecializeAttr *Attr; StringRef targetFunctionName; ModuleDecl *module = nullptr; bool isPublic = false; // In SIL we don't care to disambiguate @specialize // and @_specialize. We use the "more permissive" // non-public version when parsing SIL. AvailabilityRange availability = AvailabilityRange::alwaysAvailable(); if (!SP.P.parseSpecializeAttribute( tok::r_square, AtLoc, Loc, isPublic, Attr, &availability, [&targetFunctionName](Parser &P) -> bool { if (P.Tok.getKind() != tok::string_literal) { P.diagnose(P.Tok, diag::expected_in_attribute_list); return true; } // Drop the double quotes. targetFunctionName = P.Tok.getText().drop_front().drop_back(); P.consumeToken(tok::string_literal); return true; }, [&module](Parser &P) -> bool { if (P.Tok.getKind() != tok::identifier) { P.diagnose(P.Tok, diag::expected_in_attribute_list); return true; } auto ident = P.Context.getIdentifier(P.Tok.getText()); module = P.Context.getModuleByIdentifier(ident); assert(module); P.consumeToken(); return true; })) return true; SILFunction *targetFunction = nullptr; if (!targetFunctionName.empty()) { targetFunction = M.lookUpFunction(targetFunctionName.str()); if (!targetFunction) { Identifier Id = SP.P.Context.getIdentifier(targetFunctionName); SP.P.diagnose(SP.P.Tok, diag::sil_specialize_target_func_not_found, Id); return true; } } // Convert SpecializeAttr into ParsedSpecAttr. SpecAttr.requirements = Attr->getTrailingWhereClause()->getRequirements(); SpecAttr.kind = Attr->getSpecializationKind() == swift::SpecializeAttr::SpecializationKind::Full ? SILSpecializeAttr::SpecializationKind::Full : SILSpecializeAttr::SpecializationKind::Partial; SpecAttr.exported = Attr->isExported(); SpecAttr.target = targetFunction; SpecAttr.availability = availability; SpecAttrs->emplace_back(SpecAttr); if (!Attr->getSPIGroups().empty()) { SpecAttr.spiGroupID = Attr->getSPIGroups()[0]; } continue; } else if (ClangDecl && SP.P.Tok.getText() == "clang") { SP.P.consumeToken(tok::identifier); if (SP.parseSILDottedPathWithoutPound(*ClangDecl)) return true; SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); continue; } else { SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); return true; } SP.P.consumeToken(tok::identifier); SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); } return false; } Type SILParser::performTypeResolution(TypeRepr *TyR, bool IsSILType, GenericSignature GenericSig, GenericParamList *GenericParams) { if (!GenericSig) GenericSig = ContextGenericSig; SILTypeResolutionContext SILContext(IsSILType, GenericParams, &OpenedPackElements); return swift::performTypeResolution(TyR, P.Context, GenericSig, &SILContext, &P.SF); } /// Find the top-level ValueDecl or Module given a name. static llvm::PointerUnion lookupTopDecl(Parser &P, DeclBaseName Name, bool typeLookup) { // Use UnqualifiedLookup to look through all of the imports. UnqualifiedLookupOptions options; if (typeLookup) options |= UnqualifiedLookupFlags::TypeLookup; auto &ctx = P.SF.getASTContext(); auto descriptor = UnqualifiedLookupDescriptor(DeclNameRef(Name), &P.SF); auto lookup = evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{descriptor}, {}); lookup.filter([](LookupResultEntry entry, bool isOuter) -> bool { return !isa(entry.getValueDecl()); }); assert(lookup.size() == 1); return lookup.back().getValueDecl(); } /// Find the ValueDecl given an interface type and a member name. static ValueDecl *lookupMember(Parser &P, Type Ty, DeclBaseName Name, SourceLoc Loc, SmallVectorImpl &Lookup, bool ExpectMultipleResults) { Type CheckTy = Ty; if (auto MetaTy = CheckTy->getAs()) CheckTy = MetaTy->getInstanceType(); if (auto nominal = CheckTy->getAnyNominal()) { if (Name == DeclBaseName::createDestructor() && isa(nominal)) { auto *classDecl = cast(nominal); Lookup.push_back(classDecl->getDestructor()); } else { auto found = nominal->lookupDirect(Name); Lookup.append(found.begin(), found.end()); } } else if (auto moduleTy = CheckTy->getAs()) { moduleTy->getModule()->lookupValue(Name, NLKind::QualifiedLookup, Lookup); } else { P.diagnose(Loc, diag::sil_member_lookup_bad_type, Name, Ty); return nullptr; } if (Lookup.empty() || (!ExpectMultipleResults && Lookup.size() != 1)) { P.diagnose(Loc, diag::sil_named_member_decl_not_found, Name, Ty); return nullptr; } return Lookup[0]; } bool SILParser::parseASTType(CanType &result, GenericSignature genericSig, GenericParamList *genericParams, bool forceContextualType) { ParserResult parsedType = P.parseType(); if (parsedType.isNull()) return true; // If we weren't given a specific generic context to resolve the type // within, use the contextual generic parameters and always produce // a contextual type. Otherwise, produce a contextual type only if // we were asked for one. bool wantContextualType = forceContextualType; if (!genericSig) { genericSig = ContextGenericSig; wantContextualType = true; } if (genericParams == nullptr) genericParams = ContextGenericParams; bindSILGenericParams(parsedType.get()); auto resolvedType = performTypeResolution( parsedType.get(), /*isSILType=*/false, genericSig, genericParams); if (wantContextualType && genericSig) { resolvedType = genericSig.getGenericEnvironment() ->mapTypeIntoContext(resolvedType); } if (resolvedType->hasError()) return true; result = resolvedType->getCanonicalType(); return false; } bool SILParser::parseASTTypeOrValue(CanType &result, GenericSignature genericSig, GenericParamList *genericParams, bool forceContextualType) { auto parsedType = P.parseTypeOrValue(); if (parsedType.isNull()) return true; // If we weren't given a specific generic context to resolve the type // within, use the contextual generic parameters and always produce // a contextual type. Otherwise, produce a contextual type only if // we were asked for one. bool wantContextualType = forceContextualType; if (!genericSig) { genericSig = ContextGenericSig; wantContextualType = true; } if (genericParams == nullptr) genericParams = ContextGenericParams; bindSILGenericParams(parsedType.get()); auto resolvedType = performTypeResolution( parsedType.get(), /*isSILType=*/false, genericSig, genericParams); if (wantContextualType && genericSig) { resolvedType = genericSig.getGenericEnvironment() ->mapTypeIntoContext(resolvedType); } if (resolvedType->hasError()) return true; result = resolvedType->getCanonicalType(); return false; } void SILParser::bindSILGenericParams(TypeRepr *TyR) { // Resolve the generic environments for parsed generic function and box types. class HandleSILGenericParamsWalker : public ASTWalker { SourceFile *SF; public: HandleSILGenericParamsWalker(SourceFile *SF) : SF(SF) {} /// Walk everything in a macro MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::ArgumentsAndExpansion; } PreWalkAction walkToTypeReprPre(TypeRepr *T) override { if (auto fnType = dyn_cast(T)) { if (auto *genericParams = fnType->getGenericParams()) { auto sig = handleSILGenericParams(genericParams, SF); fnType->setGenericSignature(sig); } if (auto *genericParams = fnType->getPatternGenericParams()) { auto sig = handleSILGenericParams(genericParams, SF, /*allowInverses=*/false); fnType->setPatternGenericSignature(sig); } } if (auto boxType = dyn_cast(T)) { if (auto *genericParams = boxType->getGenericParams()) { auto sig = handleSILGenericParams(genericParams, SF); boxType->setGenericSignature(sig); } } return Action::Continue(); } }; TyR->walk(HandleSILGenericParamsWalker(&P.SF)); } /// sil-type: /// '$' '*'? attribute-list (generic-params)? type /// bool SILParser::parseSILType(SILType &Result, GenericSignature &ParsedGenericSig, GenericParamList *&ParsedGenericParams, bool IsFuncDecl, GenericSignature OuterGenericSig, GenericParamList *OuterGenericParams) { ParsedGenericSig = GenericSignature(); ParsedGenericParams = nullptr; if (!OuterGenericSig) OuterGenericSig = ContextGenericSig; if (OuterGenericParams == nullptr) OuterGenericParams = ContextGenericParams; if (P.parseToken(tok::sil_dollar, diag::expected_sil_type)) return true; // If we have a '*', then this is an address type. SILValueCategory category = SILValueCategory::Object; if (P.Tok.isAnyOperator() && P.Tok.getText().starts_with("*")) { category = SILValueCategory::Address; P.consumeStartingCharacterOfCurrentToken(); } // Parse attributes. Parser::ParsedTypeAttributeList parsedAttrs( Parser::ParseTypeReason::Unspecified); parsedAttrs.parse(P); // Global functions are implicitly @convention(thin) if not specified otherwise. if (IsFuncDecl && !llvm::any_of(parsedAttrs.Attributes, [](TypeOrCustomAttr attr) { if (auto typeAttr = attr.dyn_cast()) return isa(typeAttr); return false; })) { // Use a random location. auto loc = P.PreviousLoc; parsedAttrs.Attributes.push_back( new (P.Context) ConventionTypeAttr(loc, loc, {loc, loc}, {"thin", loc}, DeclNameRef(), {})); } ParserResult TyR = P.parseType(diag::expected_sil_type); if (TyR.isNull()) return true; bindSILGenericParams(TyR.get()); // Apply attributes to the type. auto *attrRepr = parsedAttrs.applyAttributesToType(P, TyR.get()); auto Ty = performTypeResolution(attrRepr, /*IsSILType=*/true, OuterGenericSig, OuterGenericParams); if (OuterGenericSig) { Ty = OuterGenericSig.getGenericEnvironment()->mapTypeIntoContext(Ty); } if (Ty->hasError()) return true; // Save the top-level function generic environment if there was one. if (auto fnType = dyn_cast(TyR.get())) { if (auto genericSig = fnType->getGenericSignature()) ParsedGenericSig = genericSig; if (auto *genericParams = fnType->getGenericParams()) ParsedGenericParams = genericParams; } Result = SILType::getPrimitiveType(Ty->getCanonicalType(), category); return false; } bool SILParser::parseSILDottedPath(ValueDecl *&Decl, SmallVectorImpl &values) { if (P.parseToken(tok::pound, diag::expected_sil_constant)) return true; return parseSILDottedPathWithoutPound(Decl, values); } bool SILParser::parseSILDottedPathWithoutPound(ValueDecl *&Decl, SmallVectorImpl &values) { // Handle sil-dotted-path. Identifier Id; SmallVector FullName; SmallVector Locs; do { Locs.push_back(P.Tok.getLoc()); switch (P.Tok.getKind()) { case tok::kw_subscript: P.consumeToken(); FullName.push_back(DeclBaseName::createSubscript()); break; case tok::kw_init: P.consumeToken(); FullName.push_back(DeclBaseName::createConstructor()); break; case tok::kw_deinit: P.consumeToken(); FullName.push_back(DeclBaseName::createDestructor()); break; default: if (parseSILIdentifier(Id, diag::expected_sil_constant)) return true; FullName.push_back(Id); break; } } while (P.consumeIf(tok::period)); // Look up ValueDecl from a dotted path. If there are multiple components, // the first one must be a type declaration. ValueDecl *VD; llvm::PointerUnion Res = lookupTopDecl( P, FullName[0], /*typeLookup=*/FullName.size() > 1); // It is possible that the last member lookup can return multiple lookup // results. One example is the overloaded member functions. if (isa(Res)) { assert(FullName.size() > 1 && "A single module is not a full path to SILDeclRef"); auto Mod = cast(Res); values.clear(); VD = lookupMember(P, ModuleType::get(Mod), FullName[1], Locs[1], values, FullName.size() == 2/*ExpectMultipleResults*/); for (unsigned I = 2, E = FullName.size(); I < E; ++I) { values.clear(); VD = lookupMember(P, VD->getInterfaceType(), FullName[I], Locs[I], values, I == FullName.size() - 1/*ExpectMultipleResults*/); } } else { VD = cast(Res); for (unsigned I = 1, E = FullName.size(); I < E; ++I) { values.clear(); VD = lookupMember(P, VD->getInterfaceType(), FullName[I], Locs[I], values, I == FullName.size() - 1/*ExpectMultipleResults*/); } } Decl = VD; return false; } static std::optional getAccessorKind(StringRef ident) { return llvm::StringSwitch>(ident) .Case("getter", AccessorKind::Get) .Case("setter", AccessorKind::Set) .Case("addressor", AccessorKind::Address) .Case("mutableAddressor", AccessorKind::MutableAddress) .Case("read", AccessorKind::Read) .Case("read2", AccessorKind::Read2) .Case("modify", AccessorKind::Modify) .Case("modify2", AccessorKind::Modify2) .Default(std::nullopt); } /// sil-decl-ref ::= '#' sil-identifier ('.' sil-identifier)* sil-decl-subref? /// sil-decl-subref ::= '!' sil-decl-subref-part ('.' sil-decl-lang)? /// ('.' sil-decl-autodiff)? /// sil-decl-subref ::= '!' sil-decl-lang /// sil-decl-subref ::= '!' sil-decl-autodiff /// sil-decl-subref-part ::= 'getter' /// sil-decl-subref-part ::= 'setter' /// sil-decl-subref-part ::= 'allocator' /// sil-decl-subref-part ::= 'initializer' /// sil-decl-subref-part ::= 'enumelt' /// sil-decl-subref-part ::= 'destroyer' /// sil-decl-subref-part ::= 'globalaccessor' /// sil-decl-lang ::= 'foreign' /// sil-decl-autodiff ::= sil-decl-autodiff-kind '.' sil-decl-autodiff-indices /// sil-decl-autodiff-kind ::= 'jvp' /// sil-decl-autodiff-kind ::= 'vjp' /// sil-decl-autodiff-indices ::= [SU]+ bool SILParser::parseSILDeclRef(SILDeclRef &Result, SmallVectorImpl &values) { ValueDecl *VD; if (parseSILDottedPath(VD, values)) return true; // Initialize SILDeclRef components. SILDeclRef::Kind Kind = SILDeclRef::Kind::Func; bool IsObjC = false; AutoDiffDerivativeFunctionIdentifier *DerivativeId = nullptr; if (!P.consumeIf(tok::sil_exclamation)) { // Construct SILDeclRef. Result = SILDeclRef(VD, Kind, IsObjC, /*distributed=*/false, /*knownToBeLocal=*/false, /*runtimeAccessible=*/false, SILDeclRef::BackDeploymentKind::None, DerivativeId); return false; } // Handle SILDeclRef components. ParseState tracks the last parsed component. // // When ParseState is 0, accept kind (`func|getter|setter|...`) and set // ParseState to 1. // // Always accept `foreign` and derivative function identifier. unsigned ParseState = 0; Identifier Id; do { if (P.Tok.is(tok::identifier)) { auto IdLoc = P.Tok.getLoc(); if (parseSILIdentifier(Id, diag::expected_sil_constant)) return true; std::optional accessorKind; if (!ParseState && Id.str() == "func") { Kind = SILDeclRef::Kind::Func; ParseState = 1; } else if (!ParseState && (accessorKind = getAccessorKind(Id.str())).has_value()) { // Drill down to the corresponding accessor for each declaration, // compacting away decls that lack it. size_t destI = 0; for (size_t srcI = 0, e = values.size(); srcI != e; ++srcI) { if (auto storage = dyn_cast(values[srcI])) if (auto accessor = storage->getOpaqueAccessor(*accessorKind)) values[destI++] = accessor; } values.resize(destI); // Complain if none of the decls had a corresponding accessor. if (destI == 0) { P.diagnose(IdLoc, diag::referenced_value_no_accessor, 0); return true; } Kind = SILDeclRef::Kind::Func; VD = values[0]; ParseState = 1; } else if (!ParseState && Id.str() == "allocator") { Kind = SILDeclRef::Kind::Allocator; ParseState = 1; } else if (!ParseState && Id.str() == "initializer") { Kind = SILDeclRef::Kind::Initializer; ParseState = 1; } else if (!ParseState && Id.str() == "enumelt") { Kind = SILDeclRef::Kind::EnumElement; ParseState = 1; } else if (!ParseState && Id.str() == "destroyer") { Kind = SILDeclRef::Kind::Destroyer; ParseState = 1; } else if (!ParseState && Id.str() == "deallocator") { Kind = SILDeclRef::Kind::Deallocator; ParseState = 1; } else if (!ParseState && Id.str() == "isolateddeallocator") { Kind = SILDeclRef::Kind::IsolatedDeallocator; ParseState = 1; } else if (!ParseState && Id.str() == "globalaccessor") { Kind = SILDeclRef::Kind::GlobalAccessor; ParseState = 1; } else if (!ParseState && Id.str() == "ivardestroyer") { Kind = SILDeclRef::Kind::IVarDestroyer; ParseState = 1; } else if (!ParseState && Id.str() == "ivarinitializer") { Kind = SILDeclRef::Kind::IVarInitializer; ParseState = 1; } else if (!ParseState && Id.str() == "defaultarg") { Kind = SILDeclRef::Kind::IVarInitializer; ParseState = 1; } else if (!ParseState && Id.str() == "propertyinit") { Kind = SILDeclRef::Kind::StoredPropertyInitializer; ParseState = 1; } else if (!ParseState && Id.str() == "backinginit") { Kind = SILDeclRef::Kind::PropertyWrapperBackingInitializer; ParseState = 1; } else if (!ParseState && Id.str() == "wrappedfieldinitaccessor") { Kind = SILDeclRef::Kind::PropertyWrappedFieldInitAccessor; ParseState = 1; } else if (!ParseState && Id.str() == "projectedvalueinit") { Kind = SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue; ParseState = 1; } else if (Id.str() == "foreign") { IsObjC = true; break; } else if (Id.str() == "jvp" || Id.str() == "vjp") { IndexSubset *parameterIndices = nullptr; GenericSignature derivativeGenSig; // Parse derivative function kind. AutoDiffDerivativeFunctionKind derivativeKind(Id.str()); if (!P.consumeIf(tok::period)) { P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "."); return true; } // Parse parameter indices. parameterIndices = IndexSubset::getFromString(SILMod.getASTContext(), P.Tok.getText()); if (!parameterIndices) { P.diagnose(P.Tok, diag::invalid_index_subset); return true; } P.consumeToken(); // Parse derivative generic signature (optional). if (P.Tok.is(tok::oper_binary_unspaced) && P.Tok.getText() == ".<") { P.consumeStartingCharacterOfCurrentToken(tok::period); // Create a new scope to avoid type redefinition errors. auto *genericParams = P.maybeParseGenericParams().getPtrOrNull(); assert(genericParams); derivativeGenSig = handleSILGenericParams(genericParams, &P.SF); } DerivativeId = AutoDiffDerivativeFunctionIdentifier::get( derivativeKind, parameterIndices, derivativeGenSig, SILMod.getASTContext()); break; } else { break; } } else break; } while (P.consumeIf(tok::period)); // Construct SILDeclRef. Result = SILDeclRef(VD, Kind, IsObjC, /*distributed=*/false, /*knownToBeLocal=*/false, /*runtimeAccessible=*/false, SILDeclRef::BackDeploymentKind::None, DerivativeId); return false; } /// parseValueName - Parse a value name without a type available yet. /// /// sil-value-name: /// sil-local-name /// 'undef' /// bool SILParser::parseValueName(UnresolvedValueName &Result) { Result.Name = P.Tok.getText(); if (P.Tok.is(tok::kw_undef)) { Result.NameLoc = P.consumeToken(tok::kw_undef); return false; } // Parse the local-name. if (P.parseToken(tok::sil_local_name, Result.NameLoc, diag::expected_sil_value_name)) return true; return false; } /// parseValueRef - Parse a value, given a contextual type. /// /// sil-value-ref: /// sil-local-name /// bool SILParser::parseValueRef(SILValue &Result, SILType Ty, SILLocation Loc, SILBuilder &B) { UnresolvedValueName Name; if (parseValueName(Name)) return true; Result = getLocalValue(Name, Ty, Loc, B); return false; } /// parseTypedValueRef - Parse a type/value reference pair. /// /// sil-typed-valueref: /// sil-value-ref ':' sil-type /// bool SILParser::parseTypedValueRef(SILValue &Result, SourceLoc &Loc, SILBuilder &B) { Loc = P.Tok.getLoc(); UnresolvedValueName Name; if (parseValueName(Name)) return true; if (P.consumeIf(tok::colon)) { SILType Ty; parseSILType(Ty); Result = getLocalValue(Name, Ty, RegularLocation(Loc), B); return false; } else { ValueBase *&Entry = LocalValues[Name.Name]; if (!Entry) { P.diagnose(Name.NameLoc, diag::sil_forward_ref_value_needs_type, Name.Name); return true; } Result = SILValue(Entry); return false; } } /// Look up whether the given string corresponds to a SIL opcode. static std::optional getOpcodeByName(StringRef OpcodeName) { return llvm::StringSwitch>(OpcodeName) #define FULL_INST(Id, TextualName, Parent, MemBehavior, MayRelease) \ .Case(#TextualName, SILInstructionKind::Id) #include "swift/SIL/SILNodes.def" .Default(std::nullopt); } /// getInstructionKind - This method maps the string form of a SIL instruction /// opcode to an enum. bool SILParser::parseSILOpcode(SILInstructionKind &Opcode, SourceLoc &OpcodeLoc, StringRef &OpcodeName) { OpcodeLoc = P.Tok.getLoc(); OpcodeName = P.Tok.getText(); // Parse this textually to avoid Swift keywords (like 'return') from // interfering with opcode recognition. auto MaybeOpcode = getOpcodeByName(OpcodeName); if (!MaybeOpcode) { P.diagnose(OpcodeLoc, diag::expected_sil_instr_opcode); return true; } Opcode = MaybeOpcode.value(); P.consumeToken(); return false; } bool SILParser::parseSILDebugInfoExpression(SILDebugInfoExpression &DIExpr) { if (P.Tok.getText() != "expr") return true; // All operators that we currently support static const SILDIExprOperator AllOps[] = { SILDIExprOperator::Dereference, SILDIExprOperator::Fragment, SILDIExprOperator::Plus, SILDIExprOperator::Minus, SILDIExprOperator::ConstUInt, SILDIExprOperator::ConstSInt, SILDIExprOperator::TupleFragment }; do { P.consumeToken(); bool FoundOp = false; auto OpLoc = P.Tok.getLoc(); for (const auto &Op : AllOps) { const auto *ExprInfo = SILDIExprInfo::get(Op); auto OpText = ExprInfo->OpText; if (OpText != P.Tok.getText()) continue; auto NewOperator = SILDIExprElement::createOperator(Op); DIExpr.push_back(NewOperator); P.consumeToken(); // Ready to parse the operands for (const auto &OpKind : ExprInfo->OperandKinds) { if (P.parseToken(tok::colon, diag::expected_sil_colon, "debug info expression operand")) return true; switch (OpKind) { case SILDIExprElement::DeclKind: { SILDeclRef Result; if (parseSILDeclRef(Result) || !Result.hasDecl()) { P.diagnose(P.Tok.getLoc(), diag::sil_dbg_expr_expect_operand_kind, OpText, "declaration"); return true; } auto NewOperand = SILDIExprElement::createDecl(Result.getDecl()); DIExpr.push_back(NewOperand); break; } case SILDIExprElement::ConstIntKind: { bool IsNegative = false; if (P.Tok.is(tok::oper_prefix) && P.Tok.getRawText() == "-") { P.consumeToken(); IsNegative = true; } uint64_t Val; if (parseInteger(Val, diag::sil_invalid_constant)) return true; if (IsNegative) Val = -Val; auto NewOperand = SILDIExprElement::createConstInt(static_cast(Val)); DIExpr.push_back(NewOperand); break; } case SILDIExprElement::TypeKind: { SILType Result; if (parseSILType(Result)) { P.diagnose(P.Tok.getLoc(), diag::sil_dbg_expr_expect_operand_kind, OpText, "type"); return true; } auto NewOperand = SILDIExprElement::createType( Result.getASTType().getPointer()); DIExpr.push_back(NewOperand); break; } default: P.diagnose(P.Tok.getLoc(), diag::sil_dbg_unknown_expr_part, "operand kind"); return true; } } FoundOp = true; break; } if (!FoundOp) { P.diagnose(OpLoc, diag::sil_dbg_unknown_expr_part, "operator"); return true; } } while (P.Tok.is(tok::colon)); return false; } static bool peekSILDebugLocation(Parser &P) { auto T = P.peekToken().getText(); return P.Tok.is(tok::comma) && (T == "loc" || T == "scope"); } bool SILParser::parseSILDebugVar(SILDebugVariable &Var) { auto parseVariableName = [&, this](bool Consume) -> bool { if (Consume) P.consumeToken(); if (P.Tok.getKind() != tok::string_literal) { P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "string"); return true; } // Drop the double quotes. StringRef Val = P.Tok.getText().drop_front().drop_back(); Var.Name = Val; return false; }; while (P.Tok.is(tok::comma) && !peekSILDebugLocation(P)) { P.consumeToken(); StringRef Key = P.Tok.getText(); bool NoConsume = false; if (P.consumeIf(tok::l_paren)) { if (parseVerbatim("name")) return true; if (parseVariableName(/*Consume=*/false)) return true; P.consumeToken(); // Optional operands if (peekSILDebugLocation(P)) { P.consumeToken(tok::comma); bool requireScope = false; if (P.Tok.getText() == "loc") { SILLocation VarLoc = RegularLocation::getAutoGeneratedLocation(); if (parseSILLocation(VarLoc)) return true; Var.Loc = VarLoc; requireScope = P.consumeIf(tok::comma); } if (P.Tok.getText() == "scope" || requireScope) { parseVerbatim("scope"); SILDebugScope *DS = nullptr; if (parseScopeRef(DS)) return true; if (DS) Var.Scope = DS; } } if (P.parseToken(tok::r_paren, diag::expected_tok_in_sil_instr, ")")) return true; NoConsume = true; } else if (Key == "name") { if (parseVariableName(/*Consume=*/true)) return true; } else if (Key == "argno") { P.consumeToken(); if (P.Tok.getKind() != tok::integer_literal) { P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "integer"); return true; } uint16_t ArgNo; if (parseIntegerLiteral(P.Tok.getText(), 0, ArgNo)) return true; Var.ArgNo = ArgNo; } else if (Key == "expr") { if (parseSILDebugInfoExpression(Var.DIExpr)) return true; NoConsume = true; } else if (Key == "type") { // Auxiliary type information P.consumeToken(); SILType Ty; if (parseSILType(Ty)) return true; Var.Type = Ty; NoConsume = true; } else if (Key == "let") { Var.Constant = true; } else if (Key == "var") { Var.Constant = false; } else if (Key == "loc") { Var.Constant = false; } else { P.diagnose(P.Tok, diag::sil_dbg_unknown_key, Key); return true; } if (!NoConsume) P.consumeToken(); } return false; } bool SILParser::parseSILBBArgsAtBranch(SmallVector &Args, SILBuilder &B) { if (P.Tok.is(tok::l_paren)) { SourceLoc LParenLoc = P.consumeToken(tok::l_paren); SourceLoc RParenLoc; bool HasError = false; if (P.parseList(tok::r_paren, LParenLoc, RParenLoc, /*AllowSepAfterLast=*/false, diag::sil_basicblock_arg_rparen, [&]() -> ParserStatus { SILValue Arg; SourceLoc ArgLoc; if (parseTypedValueRef(Arg, ArgLoc, B)) { HasError = true; return makeParserError(); } Args.push_back(Arg); return makeParserSuccess(); }).isErrorOrHasCompletion() || HasError) return true; } return false; } /// Parse the substitution list for an apply instruction or /// specialized protocol conformance. bool SILParser::parseSubstitutions(SmallVectorImpl &parsed, GenericSignature GenericSig, GenericParamList *GenericParams) { // Check for an opening '<' bracket. if (!P.startsWithLess(P.Tok)) return false; if (!GenericSig) GenericSig = ContextGenericSig; if (GenericParams == nullptr) GenericParams = ContextGenericParams; P.consumeStartingLess(); // Parse a list of Substitutions. do { SourceLoc Loc = P.Tok.getLoc(); // Parse substitution as AST type. ParserResult TyR = P.parseType(); if (TyR.isNull()) return true; auto Ty = performTypeResolution(TyR.get(), /*IsSILType=*/false, GenericSig, GenericParams); if (GenericSig) { Ty = GenericSig.getGenericEnvironment()->mapTypeIntoContext(Ty); } if (Ty->hasError()) return true; parsed.push_back({Loc, Ty}); } while (P.consumeIf(tok::comma)); // Consume the closing '>'. if (!P.startsWithGreater(P.Tok)) { P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, ">"); return true; } P.consumeStartingGreater(); return false; } /// Collect conformances by looking up the conformance from replacement /// type and protocol decl. static bool getConformancesForSubstitution(Parser &P, ArrayRef protocols, Type subReplacement, SourceLoc loc, SmallVectorImpl &conformances) { subReplacement = subReplacement->getReferenceStorageReferent(); for (auto protoDecl : protocols) { auto conformance = lookupConformance(subReplacement, protoDecl); if (conformance.isInvalid()) { P.diagnose(loc, diag::sil_substitution_mismatch, subReplacement, protoDecl->getDeclaredInterfaceType()); return true; } conformances.push_back(conformance); } return false; } /// Reconstruct an AST substitution map from parsed substitutions. SubstitutionMap getApplySubstitutionsFromParsed( SILParser &SP, GenericSignature genericSig, ArrayRef parses) { if (parses.empty()) { assert(!genericSig); return SubstitutionMap(); } assert(genericSig); auto loc = parses[0].loc; // Ensure that we have the right number of type arguments. if (parses.size() != genericSig.getGenericParams().size()) { bool hasTooFew = parses.size() < genericSig.getGenericParams().size(); SP.P.diagnose(loc, hasTooFew ? diag::sil_missing_substitutions : diag::sil_too_many_substitutions); return SubstitutionMap(); } bool failed = false; auto subMap = SubstitutionMap::get( genericSig, [&](SubstitutableType *type) -> Type { auto genericParam = dyn_cast(type); if (!genericParam) return nullptr; auto index = genericSig->getGenericParamOrdinal(genericParam); assert(index < genericSig.getGenericParams().size()); assert(index < parses.size()); // Provide the replacement type. return parses[index].replacement; }, [&](InFlightSubstitution &IFS, Type dependentType, ProtocolDecl *proto) -> ProtocolConformanceRef { auto replacementType = dependentType.subst(IFS) ->getReferenceStorageReferent(); if (auto conformance = lookupConformance(replacementType, proto)) return conformance; SP.P.diagnose(loc, diag::sil_substitution_mismatch, replacementType, proto->getDeclaredInterfaceType()); failed = true; return ProtocolConformanceRef::forInvalid(); }); return failed ? SubstitutionMap() : subMap; } static ArrayRef collectExistentialConformances(Parser &P, CanType conformingType, SourceLoc loc, CanType protocolType) { auto layout = protocolType.getExistentialLayout(); if (layout.requiresClass()) { if (!conformingType->mayHaveSuperclass() && !conformingType->isObjCExistentialType()) { P.diagnose(loc, diag::sil_not_class, conformingType); } } // FIXME: Check superclass also. auto protocols = layout.getProtocols(); if (protocols.empty()) return {}; SmallVector conformances; getConformancesForSubstitution(P, protocols, conformingType, loc, conformances); return P.Context.AllocateCopy(conformances); } /// sil-loc ::= 'loc' string-literal ':' [0-9]+ ':' [0-9]+ bool SILParser::parseSILLocation(SILLocation &Loc) { if (parseVerbatim("loc")) return true; bool isAutoGenerated = false; if (P.Tok.isAnyOperator() && P.Tok.getText().starts_with("*")) { isAutoGenerated = true; P.consumeStartingCharacterOfCurrentToken(); } if (P.Tok.getKind() != tok::string_literal) { P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "string"); return true; } // Drop the double quotes. StringRef File = P.Tok.getText().drop_front().drop_back(); P.consumeToken(tok::string_literal); if (P.parseToken(tok::colon, diag::expected_colon_in_sil_location)) return true; unsigned Line = 0; if (parseInteger(Line, diag::sil_invalid_line_in_sil_location)) return true; if (P.parseToken(tok::colon, diag::expected_colon_in_sil_location)) return true; unsigned Column = 0; if (parseInteger(Column, diag::sil_invalid_column_in_sil_location)) return true; auto fnl = SILLocation::FilenameAndLocation::alloc(Line, Column, P.Context.getIdentifier(File).str().data(), SILMod); Loc = RegularLocation(fnl); if (isAutoGenerated) Loc.markAutoGenerated(); return false; } bool SILParser::parseScopeRef(SILDebugScope *&DS) { unsigned Slot; SourceLoc SlotLoc = P.Tok.getLoc(); if (parseInteger(Slot, diag::sil_invalid_scope_slot)) return true; DS = TUState.ScopeSlots[Slot]; if (!DS) { P.diagnose(SlotLoc, diag::sil_scope_undeclared, Slot); return true; } return false; } bool SILParser::parseForwardingOwnershipKind( ValueOwnershipKind &forwardingKind) { if (P.Tok.is(tok::comma)) { P.consumeToken(); parsedComma = true; } if (!parsedComma) return false; if (P.Tok.is(tok::identifier) && P.Tok.getText() == "forwarding") { parsedComma = false; P.consumeToken(); return P.parseToken(tok::colon, diag::expected_tok_in_sil_instr, ":") || parseSILOwnership(forwardingKind); } return false; } /// (',' sil-loc)? (',' sil-scope-ref)? bool SILParser::parseSILDebugLocation(SILLocation &L, SILBuilder &B) { // Parse the debug information, if any. if (P.Tok.is(tok::comma)) { P.consumeToken(); parsedComma = true; } if (!parsedComma) return false; bool requireScope = false; if (P.Tok.getText() == "loc") { parsedComma = false; if (parseSILLocation(L)) return true; if (P.Tok.is(tok::comma)) { P.consumeToken(); requireScope = true; } } if (P.Tok.getText() == "scope" || requireScope) { parsedComma = false; parseVerbatim("scope"); SILDebugScope *DS = nullptr; if (parseScopeRef(DS)) return true; if (DS) B.setCurrentDebugScope(DS); } return false; } static bool parseAssignOrInitMode(AssignOrInitInst::Mode &Result, SILParser &P) { StringRef Str; if (!parseSILOptional(Str, P)) { Result = AssignOrInitInst::Unknown; return false; } auto Tmp = llvm::StringSwitch(Str) .Case("init", AssignOrInitInst::Init) .Case("set", AssignOrInitInst::Set) .Default(AssignOrInitInst::Unknown); // Return true (following the conventions in this file) if we fail. if (Tmp == AssignOrInitInst::Unknown) return true; Result = Tmp; return false; } static bool parseAssignOrInitAssignments(llvm::SmallVectorImpl &assignments, SILParser &SP) { // Could be more than one [assign=] attributes. for (;;) { SourceLoc loc; // Consume '[' if (!SP.P.consumeIf(tok::l_square)) return false; // Consume the identifier which should be "assign" { Identifier Id; if (SP.parseSILIdentifier(Id, loc, diag::expected_in_attribute_list)) return true; if (!Id.is("assign")) { SP.P.diagnose(loc, diag::sil_invalid_attribute_for_expected, Id.str(), "assign"); return true; } } uint64_t index; // Consume '=' if (!SP.P.consumeIf(tok::equal)) { SP.P.diagnose(loc, diag::expected_equal_in_sil_instr); return true; } // Consume the property index. if (SP.parseInteger(index, diag::expected_in_attribute_list)) return true; // Consume ']' if (SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list)) return true; assignments.push_back(index); } } // Parse a list of integer indices, prefaced with the given string label. // Returns true on error. static bool parseIndexList(Parser &P, StringRef label, SmallVectorImpl &indices, DiagRef parseIndexDiag) { SourceLoc loc; // Parse `[