//===--- ModuleFile.cpp - Loading a serialized module -----------*- c++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "ModuleFile.h" #include "ModuleFormat.h" #include "swift/AST/AST.h" #include "swift/AST/ModuleLoader.h" #include "swift/AST/NameLookup.h" #include "swift/Basic/STLExtras.h" #include "swift/Serialization/BCReadingExtras.h" #include "llvm/Support/MemoryBuffer.h" #include using namespace swift; using namespace swift::serialization; static constexpr const auto AF_DontPopBlockAtEnd = llvm::BitstreamCursor::AF_DontPopBlockAtEnd; using ConformancePair = std::pair; static ModuleStatus validateControlBlock(llvm::BitstreamCursor &cursor, llvm::SmallVectorImpl &scratch) { // The control block is malformed until we've at least read a major version // number. ModuleStatus result = ModuleStatus::Malformed; auto next = cursor.advance(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return ModuleStatus::Malformed; if (next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown metadata sub-block, possibly for use by a future version of the // module format. if (cursor.SkipBlock()) return ModuleStatus::Malformed; next = cursor.advance(); continue; } scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case control_block::METADATA: { uint16_t versionMajor = scratch[0]; if (versionMajor > VERSION_MAJOR) return ModuleStatus::FormatTooNew; result = ModuleStatus::Valid; break; } default: // Unknown metadata record, possibly for use by a future version of the // module format. break; } next = cursor.advance(); } return result; } /// Translate from the serialization DefaultArgumentKind enumerators, which are /// guaranteed to be stable, to the AST ones. static Optional getActualDefaultArgKind(uint8_t raw) { switch (static_cast(raw)) { case serialization::DefaultArgumentKind::None: return swift::DefaultArgumentKind::None; case serialization::DefaultArgumentKind::Normal: return swift::DefaultArgumentKind::Normal; case serialization::DefaultArgumentKind::Column: return swift::DefaultArgumentKind::Column; case serialization::DefaultArgumentKind::File: return swift::DefaultArgumentKind::File; case serialization::DefaultArgumentKind::Line: return swift::DefaultArgumentKind::Line; } return Nothing; } Pattern *ModuleFile::maybeReadPattern() { using namespace decls_block; SmallVector scratch; auto next = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); if (next.Kind != llvm::BitstreamEntry::Record) return nullptr; unsigned kind = DeclTypeCursor.readRecord(next.ID, scratch); switch (kind) { case decls_block::PAREN_PATTERN: { bool isImplicit; ParenPatternLayout::readRecord(scratch, isImplicit); Pattern *subPattern = maybeReadPattern(); assert(subPattern); auto result = new (ModuleContext->Ctx) ParenPattern(SourceLoc(), subPattern, SourceLoc(), isImplicit); result->setType(subPattern->getType()); return result; } case decls_block::TUPLE_PATTERN: { TypeID tupleTypeID; unsigned count; bool hasVararg; bool isImplicit; TuplePatternLayout::readRecord(scratch, tupleTypeID, count, hasVararg, isImplicit); SmallVector elements; for ( ; count > 0; --count) { scratch.clear(); next = DeclTypeCursor.advance(); assert(next.Kind == llvm::BitstreamEntry::Record); kind = DeclTypeCursor.readRecord(next.ID, scratch); assert(kind == decls_block::TUPLE_PATTERN_ELT); // FIXME: Add something for this record or remove it. uint8_t rawDefaultArg; TuplePatternEltLayout::readRecord(scratch, rawDefaultArg); Pattern *subPattern = maybeReadPattern(); assert(subPattern); // Decode the default argument kind. // FIXME: Default argument expression, if available. swift::DefaultArgumentKind defaultArgKind = swift::DefaultArgumentKind::None; if (auto defaultArg = getActualDefaultArgKind(rawDefaultArg)) defaultArgKind = *defaultArg; elements.push_back(TuplePatternElt(subPattern, nullptr, defaultArgKind)); } auto result = TuplePattern::create(ModuleContext->Ctx, SourceLoc(), elements, SourceLoc(), hasVararg, SourceLoc(), isImplicit); result->setType(getType(tupleTypeID)); return result; } case decls_block::NAMED_PATTERN: { DeclID varID; bool isImplicit; NamedPatternLayout::readRecord(scratch, varID, isImplicit); auto var = cast(getDecl(varID)); auto result = new (ModuleContext->Ctx) NamedPattern(var, isImplicit); if (var->hasType()) result->setType(var->getType()); return result; } case decls_block::ANY_PATTERN: { TypeID typeID; bool isImplicit; AnyPatternLayout::readRecord(scratch, typeID, isImplicit); auto result = new (ModuleContext->Ctx) AnyPattern(SourceLoc(), isImplicit); result->setType(getType(typeID)); return result; } case decls_block::TYPED_PATTERN: { TypeID typeID; bool isImplicit; TypedPatternLayout::readRecord(scratch, typeID, isImplicit); Pattern *subPattern = maybeReadPattern(); assert(subPattern); TypeLoc typeInfo = TypeLoc::withoutLoc(getType(typeID)); auto result = new (ModuleContext->Ctx) TypedPattern(subPattern, typeInfo, isImplicit); result->setType(typeInfo.getType()); return result; } default: return nullptr; } } ProtocolConformance * ModuleFile::readUnderlyingConformance(ProtocolDecl *proto, DeclID typeID, IdentifierID moduleID) { if (!moduleID) { // The underlying conformance is in the following record. return maybeReadConformance(getType(typeID))->second; } // Dig out the protocol conformance within the nominal declaration. auto nominal = cast(getDecl(typeID)); Module *owningModule; if (moduleID == 1) owningModule = ModuleContext; else owningModule = getModule(getIdentifier(moduleID-2)); (void)owningModule; // FIXME: Currently only used for checking. // Search protocols for (unsigned i = 0, n = nominal->getProtocols().size(); i != n; ++i) { if (nominal->getProtocols()[i] == proto) { // FIXME: Eventually, filter by owning module. assert(nominal->getModuleContext() == owningModule); return nominal->getConformances()[i]; } } // Search extensions. for (auto ext : nominal->getExtensions()) { for (unsigned i = 0, n = ext->getProtocols().size(); i != n; ++i) { if (ext->getProtocols()[i] == proto) { // FIXME: Eventually, filter by owning module. assert(ext->getModuleContext() == owningModule); return ext->getConformances()[i]; } } } llvm_unreachable("Unable to find underlying conformance"); } Optional ModuleFile::maybeReadConformance(Type conformingType){ using namespace decls_block; BCOffsetRAII lastRecordOffset(DeclTypeCursor); SmallVector scratch; auto next = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); if (next.Kind != llvm::BitstreamEntry::Record) return Nothing; unsigned kind = DeclTypeCursor.readRecord(next.ID, scratch); switch (kind) { case NO_CONFORMANCE: { lastRecordOffset.reset(); DeclID protoID; NoConformanceLayout::readRecord(scratch, protoID); return std::make_pair(cast(getDecl(protoID)), nullptr); } case NORMAL_PROTOCOL_CONFORMANCE: // Handled below. break; case SPECIALIZED_PROTOCOL_CONFORMANCE: { DeclID protoID; DeclID typeID; IdentifierID moduleID; unsigned numTypeWitnesses; unsigned numSubstitutions; ArrayRef rawIDs; SpecializedProtocolConformanceLayout::readRecord(scratch, protoID, typeID, moduleID, numTypeWitnesses, numSubstitutions, rawIDs); ASTContext &ctx = ModuleContext->Ctx; auto proto = cast(getDecl(protoID)); // Read the substitutions. SmallVector substitutions; while (numSubstitutions--) { auto sub = maybeReadSubstitution(); assert(sub.hasValue() && "Missing substitution?"); substitutions.push_back(*sub); } // Read the type witnesses. ArrayRef::iterator rawIDIter = rawIDs.begin(); TypeWitnessMap typeWitnesses; while (numTypeWitnesses--) { // FIXME: We don't actually want to allocate an archetype here; we just // want to get an access path within the protocol. auto first = cast(getDecl(*rawIDIter++)); auto second = maybeReadSubstitution(); assert(second.hasValue()); typeWitnesses[first] = *second; } assert(rawIDIter <= rawIDs.end() && "read too much"); ProtocolConformance *genericConformance = readUnderlyingConformance(proto, typeID, moduleID); // Reset the offset RAII to the end of the trailing records. lastRecordOffset.reset(); assert(genericConformance && "Missing generic conformance?"); return { proto, ctx.getSpecializedConformance(conformingType, genericConformance, substitutions, std::move(typeWitnesses)) }; } case INHERITED_PROTOCOL_CONFORMANCE: { DeclID protoID; DeclID typeID; IdentifierID moduleID; InheritedProtocolConformanceLayout::readRecord(scratch, protoID, typeID, moduleID); ASTContext &ctx = ModuleContext->Ctx; auto proto = cast(getDecl(protoID)); ProtocolConformance *inheritedConformance = readUnderlyingConformance(proto, typeID, moduleID); // Reset the offset RAII to the end of the trailing records. lastRecordOffset.reset(); assert(inheritedConformance && "Missing generic conformance?"); return { proto, ctx.getInheritedConformance(conformingType, inheritedConformance) }; } // Not a protocol conformance. default: return Nothing; } lastRecordOffset.reset(); DeclID protoID; unsigned valueCount, typeCount, inheritedCount, defaultedCount; ArrayRef rawIDs; NormalProtocolConformanceLayout::readRecord(scratch, protoID, valueCount, typeCount, inheritedCount, defaultedCount, rawIDs); InheritedConformanceMap inheritedConformances; while (inheritedCount--) { auto inherited = maybeReadConformance(conformingType); assert(inherited.hasValue()); inheritedConformances.insert(inherited.getValue()); } ASTContext &ctx = ModuleContext->Ctx; auto proto = cast(getDecl(protoID)); WitnessMap witnesses; ArrayRef::iterator rawIDIter = rawIDs.begin(); while (valueCount--) { auto first = cast(getDecl(*rawIDIter++)); auto second = cast(getDecl(*rawIDIter++)); unsigned substitutionCount = *rawIDIter++; SmallVector substitutions; while (substitutionCount--) { auto sub = maybeReadSubstitution(); assert(sub.hasValue()); substitutions.push_back(sub.getValue()); } ProtocolConformanceWitness witness{second, ctx.AllocateCopy(substitutions)}; witnesses.insert(std::make_pair(first, witness)); } assert(rawIDIter <= rawIDs.end() && "read too much"); TypeWitnessMap typeWitnesses; while (typeCount--) { // FIXME: We don't actually want to allocate an archetype here; we just // want to get an access path within the protocol. auto first = cast(getDecl(*rawIDIter++)); auto second = maybeReadSubstitution(); assert(second.hasValue()); typeWitnesses[first] = *second; } assert(rawIDIter <= rawIDs.end() && "read too much"); llvm::SmallVector defaultedDefinitions; while (defaultedCount--) { auto decl = cast(getDecl(*rawIDIter++)); defaultedDefinitions.push_back(decl); } assert(rawIDIter <= rawIDs.end() && "read too much"); // Reset the offset RAII to the end of the trailing records. lastRecordOffset.reset(); return { proto, ctx.getConformance(conformingType, proto, ModuleContext, std::move(witnesses), std::move(typeWitnesses), std::move(inheritedConformances), defaultedDefinitions) }; } /// Applies protocol conformances to a decl. template void processConformances(ASTContext &ctx, T *decl, ArrayRef conformances) { SmallVector protoBuf; SmallVector conformanceBuf; for (auto conformancePair : conformances) { auto proto = conformancePair.first; auto conformance = conformancePair.second; protoBuf.push_back(proto); conformanceBuf.push_back(conformance); if (conformance && decl) ctx.recordConformance(proto, decl); } decl->setProtocols(ctx.AllocateCopy(protoBuf)); decl->setConformances(ctx.AllocateCopy(conformanceBuf)); } Optional ModuleFile::maybeReadSubstitution() { BCOffsetRAII lastRecordOffset(DeclTypeCursor); auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); if (entry.Kind != llvm::BitstreamEntry::Record) return Nothing; StringRef blobData; SmallVector scratch; unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, &blobData); if (recordID != decls_block::BOUND_GENERIC_SUBSTITUTION) return Nothing; TypeID archetypeID, replacementID; unsigned numConformances; decls_block::BoundGenericSubstitutionLayout::readRecord(scratch, archetypeID, replacementID, numConformances); auto archetypeTy = getType(archetypeID)->castTo(); auto replacementTy = getType(replacementID); ASTContext &ctx = ModuleContext->Ctx; SmallVector conformanceBuf; while (numConformances--) { auto conformancePair = maybeReadConformance(replacementTy); assert(conformancePair.hasValue() && "Missing conformance"); conformanceBuf.push_back(conformancePair->second); } lastRecordOffset.reset(); return Substitution{archetypeTy, replacementTy, ctx.AllocateCopy(conformanceBuf)}; } GenericParamList *ModuleFile::maybeReadGenericParams(DeclContext *DC) { using namespace decls_block; assert(DC && "need a context for the decls in the list"); BCOffsetRAII lastRecordOffset(DeclTypeCursor); SmallVector scratch; StringRef blobData; auto next = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); if (next.Kind != llvm::BitstreamEntry::Record) return nullptr; unsigned kind = DeclTypeCursor.readRecord(next.ID, scratch, &blobData); if (kind != GENERIC_PARAM_LIST) return nullptr; ArrayRef rawArchetypeIDs; GenericParamListLayout::readRecord(scratch, rawArchetypeIDs); SmallVector archetypes; for (TypeID next : rawArchetypeIDs) archetypes.push_back(getType(next)->castTo()); SmallVector params; SmallVector requirements; while (true) { lastRecordOffset.reset(); bool shouldContinue = true; auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); if (entry.Kind != llvm::BitstreamEntry::Record) break; scratch.clear(); unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, &blobData); switch (recordID) { case GENERIC_PARAM: { DeclID paramDeclID; GenericParamLayout::readRecord(scratch, paramDeclID); auto typeAlias = cast(getDecl(paramDeclID, DC)); params.push_back(GenericParam(typeAlias)); break; } case GENERIC_REQUIREMENT: { uint8_t rawKind; ArrayRef rawTypeIDs; GenericRequirementLayout::readRecord(scratch, rawKind, rawTypeIDs); switch (rawKind) { case GenericRequirementKind::Conformance: { assert(rawTypeIDs.size() == 2); auto subject = TypeLoc::withoutLoc(getType(rawTypeIDs[0])); auto constraint = TypeLoc::withoutLoc(getType(rawTypeIDs[1])); requirements.push_back(Requirement::getConformance(subject, SourceLoc(), constraint)); break; } case GenericRequirementKind::SameType: { assert(rawTypeIDs.size() == 2); auto first = TypeLoc::withoutLoc(getType(rawTypeIDs[0])); auto second = TypeLoc::withoutLoc(getType(rawTypeIDs[1])); requirements.push_back(Requirement::getSameType(first, SourceLoc(), second)); break; } default: // Unknown requirement kind. Drop the requirement and continue, but log // an error so that we don't actually try to generate code. error(); } break; } default: // This record is not part of the GenericParamList. shouldContinue = false; break; } if (!shouldContinue) break; } auto paramList = GenericParamList::create(ModuleContext->Ctx, SourceLoc(), params, SourceLoc(), requirements, SourceLoc()); paramList->setAllArchetypes(ModuleContext->Ctx.AllocateCopy(archetypes)); paramList->setOuterParameters(DC->getGenericParamsOfContext()); return paramList; } Optional> ModuleFile::readMembers() { using namespace decls_block; auto entry = DeclTypeCursor.advance(); if (entry.Kind != llvm::BitstreamEntry::Record) return Nothing; SmallVector memberIDBuffer; unsigned kind = DeclTypeCursor.readRecord(entry.ID, memberIDBuffer); assert(kind == DECL_CONTEXT); (void)kind; ArrayRef rawMemberIDs; decls_block::DeclContextLayout::readRecord(memberIDBuffer, rawMemberIDs); if (rawMemberIDs.empty()) return MutableArrayRef(); ASTContext &ctx = ModuleContext->Ctx; MutableArrayRef members(ctx.Allocate(rawMemberIDs.size()), rawMemberIDs.size()); auto nextMember = members.begin(); for (DeclID rawID : rawMemberIDs) { *nextMember = getDecl(rawID); assert(*nextMember && "unable to deserialize next member"); ++nextMember; } return members; } Identifier ModuleFile::getIdentifier(IdentifierID IID) { if (IID == 0) return Identifier(); assert(IID <= Identifiers.size() && "invalid identifier ID"); auto identRecord = Identifiers[IID-1]; if (identRecord.Offset == 0) return identRecord.Ident; assert(!IdentifierData.empty() && "no identifier data in module"); StringRef rawStrPtr = IdentifierData.substr(identRecord.Offset); size_t terminatorOffset = rawStrPtr.find('\0'); assert(terminatorOffset != StringRef::npos && "unterminated identifier string data"); return ModuleContext->Ctx.getIdentifier(rawStrPtr.slice(0, terminatorOffset)); } DeclContext *ModuleFile::getDeclContext(DeclID DID) { if (DID == 0) return ModuleContext; Decl *D = getDecl(DID); if (auto ND = dyn_cast(D)) return ND; if (auto ED = dyn_cast(D)) return ED; if (auto CD = dyn_cast(D)) return CD; if (auto DD = dyn_cast(D)) return DD; if (auto FD = dyn_cast(D)) return FD->getBody(); llvm_unreachable("unknown DeclContext kind"); } Module *ModuleFile::getModule(Identifier name) { if (name.empty()) return ModuleContext->Ctx.TheBuiltinModule; // FIXME: duplicated from NameBinder::getModule // FIXME: provide a real source location. if (name == ModuleContext->Name) { if (!ShadowedModule) { auto importer = ModuleContext->Ctx.getClangModuleLoader(); assert(importer && "no way to import shadowed module (recursive xref?)"); ShadowedModule = importer->loadModule(SourceLoc(), std::make_pair(name, SourceLoc())); assert(ShadowedModule && "missing shadowed module"); } return ShadowedModule; } // FIXME: provide a real source location. return ModuleContext->Ctx.getModule(std::make_pair(name, SourceLoc())); } /// Translate from the Serialization assocativity enum values to the AST /// strongly-typed enum. /// /// The former is guaranteed to be stable, but may not reflect this version of /// the AST. static Optional getActualAssociativity(uint8_t assoc) { switch (assoc) { case serialization::Associativity::LeftAssociative: return swift::Associativity::Left; case serialization::Associativity::RightAssociative: return swift::Associativity::Right; case serialization::Associativity::NonAssociative: return swift::Associativity::None; default: return Nothing; } } Decl *ModuleFile::getDecl(DeclID DID, Optional ForcedContext, Optional> DidRecord) { if (DID == 0) return nullptr; assert(DID <= Decls.size() && "invalid decl ID"); auto &declOrOffset = Decls[DID-1]; if (declOrOffset.isComplete()) { if (DidRecord) (*DidRecord)(declOrOffset); return declOrOffset; } BCOffsetRAII restoreOffset(DeclTypeCursor); DeclTypeCursor.JumpToBit(declOrOffset); auto entry = DeclTypeCursor.advance(); if (entry.Kind != llvm::BitstreamEntry::Record) { // We don't know how to serialize decls represented by sub-blocks. error(); return nullptr; } ASTContext &ctx = ModuleContext->Ctx; SmallVector scratch; StringRef blobData; unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, &blobData); switch (recordID) { case decls_block::TYPE_ALIAS_DECL: { IdentifierID nameID; DeclID contextID; TypeID underlyingTypeID; bool isGeneric; bool isImplicit; TypeID superclassTypeID; decls_block::TypeAliasLayout::readRecord(scratch, nameID, contextID, underlyingTypeID, isGeneric, isImplicit, superclassTypeID); auto DC = ForcedContext ? *ForcedContext : getDeclContext(contextID); if (declOrOffset.isComplete()) break; auto underlyingType = TypeLoc::withoutLoc(getType(underlyingTypeID)); auto alias = new (ctx) TypeAliasDecl(SourceLoc(), getIdentifier(nameID), SourceLoc(), underlyingType, DC, { }); declOrOffset = alias; if (isImplicit) alias->setImplicit(); if (isGeneric) { alias->setGenericParameter(); alias->setSuperclass(getType(superclassTypeID)); } SmallVector conformances; while (auto conformance = maybeReadConformance(underlyingType.getType())) conformances.push_back(*conformance); processConformances(ctx, alias, conformances); break; } case decls_block::STRUCT_DECL: { IdentifierID nameID; DeclID contextID; bool isImplicit; decls_block::StructLayout::readRecord(scratch, nameID, contextID, isImplicit); auto DC = getDeclContext(contextID); if (declOrOffset.isComplete()) break; auto genericParams = maybeReadGenericParams(DC); auto theStruct = new (ctx) StructDecl(SourceLoc(), getIdentifier(nameID), SourceLoc(), { }, genericParams, DC); declOrOffset = theStruct; if (DidRecord) { (*DidRecord)(theStruct); DidRecord.reset(); } if (isImplicit) theStruct->setImplicit(); if (genericParams) for (auto &genericParam : *theStruct->getGenericParams()) genericParam.getAsTypeParam()->setDeclContext(theStruct); CanType canTy = theStruct->getDeclaredTypeInContext()->getCanonicalType(); SmallVector conformances; while (auto conformance = maybeReadConformance(canTy)) conformances.push_back(*conformance); processConformances(ctx, theStruct, conformances); auto members = readMembers(); assert(members.hasValue() && "could not read struct members"); theStruct->setMembers(members.getValue(), SourceRange()); break; } case decls_block::CONSTRUCTOR_DECL: { DeclID parentID; bool isImplicit, isObjC; TypeID signatureID; DeclID implicitThisID; decls_block::ConstructorLayout::readRecord(scratch, parentID, isImplicit, isObjC, signatureID, implicitThisID); auto parent = getDeclContext(parentID); if (declOrOffset.isComplete()) break; auto thisDecl = cast(getDecl(implicitThisID, nullptr)); auto genericParams = maybeReadGenericParams(parent); auto ctor = new (ctx) ConstructorDecl(ctx.getIdentifier("constructor"), SourceLoc(), /*args=*/nullptr, thisDecl, genericParams, parent); declOrOffset = ctor; thisDecl->setDeclContext(ctor); Pattern *args = maybeReadPattern(); assert(args && "missing arguments for constructor"); ctor->setArguments(args); // This must be set after recording the constructor in the map. // A polymorphic constructor type needs to refer to the constructor to get // its generic parameters. ctor->setType(getType(signatureID)); if (isImplicit) ctor->setImplicit(); ctor->setIsObjC(isObjC); if (genericParams) for (auto &genericParam : *ctor->getGenericParams()) genericParam.getAsTypeParam()->setDeclContext(ctor); break; } case decls_block::VAR_DECL: { IdentifierID nameID; DeclID contextID; bool isImplicit, isObjC, isIBOutlet; TypeID typeID; DeclID getterID, setterID; DeclID overriddenID; decls_block::VarLayout::readRecord(scratch, nameID, contextID, isImplicit, isObjC, isIBOutlet, typeID, getterID, setterID, overriddenID); auto DC = ForcedContext ? *ForcedContext : getDeclContext(contextID); auto var = new (ctx) VarDecl(SourceLoc(), getIdentifier(nameID), getType(typeID), DC); declOrOffset = var; if (getterID || setterID) { var->setProperty(ctx, SourceLoc(), cast_or_null(getDecl(getterID)), cast_or_null(getDecl(setterID)), SourceLoc()); } if (isImplicit) var->setImplicit(); var->setIsObjC(isObjC); if (isIBOutlet) var->getMutableAttrs().IBOutlet = true; var->setOverriddenDecl(cast_or_null(getDecl(overriddenID))); break; } case decls_block::FUNC_DECL: { IdentifierID nameID; DeclID contextID; bool isImplicit; bool isClassMethod; bool isAssignmentOrConversion; bool isObjC, isIBAction; TypeID signatureID; DeclID associatedDeclID; DeclID overriddenID; decls_block::FuncLayout::readRecord(scratch, nameID, contextID, isImplicit, isClassMethod, isAssignmentOrConversion, isObjC, isIBAction, signatureID, associatedDeclID, overriddenID); auto DC = getDeclContext(contextID); if (declOrOffset.isComplete()) break; // Read generic params before reading the type, because the type may // reference generic parameters, and we want them to have a dummy // DeclContext for now. GenericParamList *genericParams = maybeReadGenericParams(DC); auto fn = new (ctx) FuncDecl(SourceLoc(), SourceLoc(), getIdentifier(nameID), SourceLoc(), genericParams, /*type=*/nullptr, /*body=*/nullptr, DC); declOrOffset = fn; // This must be set after recording the constructor in the map. // A polymorphic constructor type needs to refer to the constructor to get // its generic parameters. auto signature = getType(signatureID)->castTo(); fn->setType(signature); SmallVector patternBuf; while (Pattern *pattern = maybeReadPattern()) patternBuf.push_back(pattern); assert(!patternBuf.empty()); size_t patternCount = patternBuf.size() / 2; assert(patternCount * 2 == patternBuf.size() && "two sets of patterns don't match up"); ArrayRef patterns(patternBuf); ArrayRef argPatterns = patterns.slice(0, patternCount); ArrayRef bodyPatterns = patterns.slice(patternCount); auto body = FuncExpr::create(ctx, SourceLoc(), argPatterns, bodyPatterns, TypeLoc::withoutLoc(signature->getResult()), DC); body->setType(signature); fn->setBody(body); if (genericParams) for (auto &genericParam : *fn->getGenericParams()) genericParam.getAsTypeParam()->setDeclContext(body); fn->setOverriddenDecl(cast_or_null(getDecl(overriddenID))); fn->setStatic(isClassMethod); if (isImplicit) fn->setImplicit(); if (!blobData.empty()) fn->getMutableAttrs().AsmName = ctx.AllocateCopy(blobData); if (isAssignmentOrConversion) { if (fn->isOperator()) fn->getMutableAttrs().Assignment = true; else fn->getMutableAttrs().Conversion = true; } fn->setIsObjC(isObjC); if (isIBAction) fn->getMutableAttrs().IBAction = true; if (Decl *associated = getDecl(associatedDeclID)) { if (auto op = dyn_cast(associated)) { fn->setOperatorDecl(op); if (isa(op)) fn->getMutableAttrs().ExplicitPrefix = true; else if (isa(op)) fn->getMutableAttrs().ExplicitPostfix = true; // Note that an explicit [infix] is not required. } // Otherwise, unknown associated decl kind. } break; } case decls_block::PATTERN_BINDING_DECL: { DeclID contextID; bool isImplicit; decls_block::PatternBindingLayout::readRecord(scratch, contextID, isImplicit); Pattern *pattern = maybeReadPattern(); assert(pattern); auto binding = new (ctx) PatternBindingDecl(SourceLoc(), pattern, /*init=*/nullptr, getDeclContext(contextID)); declOrOffset = binding; if (isImplicit) binding->setImplicit(); break; } case decls_block::PROTOCOL_DECL: { IdentifierID nameID; DeclID contextID; bool isImplicit, isClassProtocol, isObjC; ArrayRef protocolIDs; decls_block::ProtocolLayout::readRecord(scratch, nameID, contextID, isImplicit, isClassProtocol, isObjC, protocolIDs); auto DC = getDeclContext(contextID); if (declOrOffset.isComplete()) break; auto proto = new (ctx) ProtocolDecl(DC, SourceLoc(), SourceLoc(), getIdentifier(nameID), { }); declOrOffset = proto; if (DidRecord) { (*DidRecord)(proto); DidRecord.reset(); } if (isImplicit) proto->setImplicit(); if (isClassProtocol) proto->getMutableAttrs().ClassProtocol = true; proto->setIsObjC(isObjC); // Deserialize the list of protocols. SmallVector protocols; for (auto protoID : protocolIDs) { protocols.push_back(cast(getDecl(protoID))); } proto->setProtocols(ctx.AllocateCopy(protocols)); auto members = readMembers(); assert(members.hasValue() && "could not read struct members"); proto->setMembers(members.getValue(), SourceRange()); break; } case decls_block::PREFIX_OPERATOR_DECL: { IdentifierID nameID; DeclID contextID; decls_block::PrefixOperatorLayout::readRecord(scratch, nameID, contextID); declOrOffset = new (ctx) PrefixOperatorDecl(getDeclContext(contextID), SourceLoc(), SourceLoc(), getIdentifier(nameID), SourceLoc(), SourceLoc(), SourceLoc()); break; } case decls_block::POSTFIX_OPERATOR_DECL: { IdentifierID nameID; DeclID contextID; decls_block::PostfixOperatorLayout::readRecord(scratch, nameID, contextID); declOrOffset = new (ctx) PostfixOperatorDecl(getDeclContext(contextID), SourceLoc(), SourceLoc(), getIdentifier(nameID), SourceLoc(), SourceLoc(), SourceLoc()); break; } case decls_block::INFIX_OPERATOR_DECL: { IdentifierID nameID; DeclID contextID; uint8_t rawAssociativity; unsigned precedence; decls_block::InfixOperatorLayout::readRecord(scratch, nameID, contextID, rawAssociativity, precedence); auto associativity = getActualAssociativity(rawAssociativity); if (!associativity.hasValue()) { error(); return nullptr; } InfixData infixData(precedence, associativity.getValue()); declOrOffset = new (ctx) InfixOperatorDecl(getDeclContext(contextID), SourceLoc(), SourceLoc(), getIdentifier(nameID), SourceLoc(), SourceLoc(), SourceLoc(), SourceLoc(), SourceLoc(), SourceLoc(), SourceLoc(), infixData); break; } case decls_block::CLASS_DECL: { IdentifierID nameID; DeclID contextID; bool isImplicit, isObjC; TypeID superclassID; decls_block::ClassLayout::readRecord(scratch, nameID, contextID, isImplicit, isObjC, superclassID); auto DC = getDeclContext(contextID); if (declOrOffset.isComplete()) break; auto genericParams = maybeReadGenericParams(DC); auto theClass = new (ctx) ClassDecl(SourceLoc(), getIdentifier(nameID), SourceLoc(), { }, genericParams, DC); declOrOffset = theClass; if (DidRecord) { (*DidRecord)(theClass); DidRecord.reset(); } if (isImplicit) theClass->setImplicit(); if (superclassID) theClass->setSuperclass(getType(superclassID)); if (genericParams) for (auto &genericParam : *theClass->getGenericParams()) genericParam.getAsTypeParam()->setDeclContext(theClass); theClass->setIsObjC(isObjC); CanType canTy = theClass->getDeclaredTypeInContext()->getCanonicalType(); SmallVector conformances; while (auto conformance = maybeReadConformance(canTy)) conformances.push_back(*conformance); processConformances(ctx, theClass, conformances); auto members = readMembers(); assert(members.hasValue() && "could not read class members"); theClass->setMembers(members.getValue(), SourceRange()); break; } case decls_block::UNION_DECL: { IdentifierID nameID; DeclID contextID; bool isImplicit; decls_block::UnionLayout::readRecord(scratch, nameID, contextID, isImplicit); auto DC = getDeclContext(contextID); if (declOrOffset.isComplete()) break; auto genericParams = maybeReadGenericParams(DC); // FIXME Preserve the isEnum bit. auto theUnion = new (ctx) UnionDecl(SourceLoc(), /*isEnum*/ false, getIdentifier(nameID), SourceLoc(), { }, genericParams, DC); declOrOffset = theUnion; if (DidRecord) { (*DidRecord)(theUnion); DidRecord.reset(); } if (isImplicit) theUnion->setImplicit(); if (genericParams) for (auto &genericParam : *theUnion->getGenericParams()) genericParam.getAsTypeParam()->setDeclContext(theUnion); CanType canTy = theUnion->getDeclaredTypeInContext()->getCanonicalType(); SmallVector conformances; while (auto conformance = maybeReadConformance(canTy)) conformances.push_back(*conformance); processConformances(ctx, theUnion, conformances); auto members = readMembers(); assert(members.hasValue() && "could not read union members"); theUnion->setMembers(members.getValue(), SourceRange()); break; } case decls_block::UNION_ELEMENT_DECL: { IdentifierID nameID; DeclID contextID; TypeID argTypeID, resTypeID, ctorTypeID; bool isImplicit; decls_block::UnionElementLayout::readRecord(scratch, nameID, contextID, argTypeID, resTypeID, ctorTypeID, isImplicit); DeclContext *DC = getDeclContext(contextID); if (declOrOffset.isComplete()) break; auto argTy = getType(argTypeID); auto resTy = getType(resTypeID); auto elem = new (ctx) UnionElementDecl(SourceLoc(), SourceLoc(), getIdentifier(nameID), TypeLoc::withoutLoc(argTy), SourceLoc(), TypeLoc::withoutLoc(resTy), DC); declOrOffset = elem; elem->setType(getType(ctorTypeID)); if (isImplicit) elem->setImplicit(); break; } case decls_block::SUBSCRIPT_DECL: { DeclID contextID; bool isImplicit, isObjC; TypeID declTypeID, elemTypeID; DeclID getterID, setterID; DeclID overriddenID; decls_block::SubscriptLayout::readRecord(scratch, contextID, isImplicit, isObjC, declTypeID, elemTypeID, getterID, setterID, overriddenID); auto DC = getDeclContext(contextID); if (declOrOffset.isComplete()) break; Pattern *indices = maybeReadPattern(); assert(indices); auto elemTy = TypeLoc::withoutLoc(getType(elemTypeID)); auto getter = cast_or_null(getDecl(getterID)); auto setter = cast_or_null(getDecl(setterID)); auto subscript = new (ctx) SubscriptDecl(ctx.getIdentifier("__subscript"), SourceLoc(), indices, SourceLoc(), elemTy, SourceRange(), getter, setter, DC); declOrOffset = subscript; subscript->setType(getType(declTypeID)); if (isImplicit) subscript->setImplicit(); subscript->setIsObjC(isObjC); auto overriddenDecl = cast_or_null(getDecl(overriddenID)); subscript->setOverriddenDecl(overriddenDecl); if (getter) getter->makeGetter(subscript); if (setter) setter->makeSetter(subscript); break; } case decls_block::EXTENSION_DECL: { TypeID baseID; DeclID contextID; bool isImplicit; decls_block::ExtensionLayout::readRecord(scratch, baseID, contextID, isImplicit); auto DC = getDeclContext(contextID); if (declOrOffset.isComplete()) break; auto baseTy = TypeLoc::withoutLoc(getType(baseID)); auto extension = new (ctx) ExtensionDecl(SourceLoc(), baseTy, { }, DC); declOrOffset = extension; if (isImplicit) extension->setImplicit(); CanType canBaseTy = baseTy.getType()->getCanonicalType(); SmallVector conformances; while (auto conformance = maybeReadConformance(canBaseTy)) conformances.push_back(*conformance); processConformances(ctx, extension, conformances); auto members = readMembers(); assert(members.hasValue() && "could not read extension members"); extension->setMembers(members.getValue(), SourceRange()); baseTy.getType()->getAnyNominal()->addExtension(extension); break; } case decls_block::DESTRUCTOR_DECL: { DeclID parentID; bool isImplicit, isObjC; TypeID signatureID; DeclID implicitThisID; decls_block::DestructorLayout::readRecord(scratch, parentID, isImplicit, isObjC, signatureID, implicitThisID); DeclContext *parent = getDeclContext(parentID); if (declOrOffset.isComplete()) break; auto thisDecl = cast(getDecl(implicitThisID, nullptr)); auto dtor = new (ctx) DestructorDecl(ctx.getIdentifier("destructor"), SourceLoc(), thisDecl, parent); declOrOffset = dtor; thisDecl->setDeclContext(dtor); dtor->setType(getType(signatureID)); if (isImplicit) dtor->setImplicit(); dtor->setIsObjC(isObjC); break; } case decls_block::XREF: { uint8_t kind; TypeID expectedTypeID; bool isWithinExtension; ArrayRef rawAccessPath; decls_block::XRefLayout::readRecord(scratch, kind, expectedTypeID, isWithinExtension, rawAccessPath); // First, find the module this reference is referring to. Module *M = getModule(getIdentifier(rawAccessPath.front())); assert(M && "missing dependency"); rawAccessPath = rawAccessPath.slice(1); switch (kind) { case XRefKind::SwiftValue: case XRefKind::SwiftGenericParameter: { // Start by looking up the top-level decl in the module. Module *baseModule = M; if (isWithinExtension) { baseModule = getModule(getIdentifier(rawAccessPath.front())); rawAccessPath = rawAccessPath.slice(1); } SmallVector values; baseModule->lookupValue(Module::AccessPathTy(), getIdentifier(rawAccessPath.front()), NLKind::QualifiedLookup, values); // FIXME: Yuck. The concept of a shadowed module needs to be moved up // higher, and it needs to be clear whether they are always reexported. if (auto loadedModule = dyn_cast(baseModule)) { if (loadedModule->File && loadedModule->File->ShadowedModule) { Module *shadowed = loadedModule->File->ShadowedModule; shadowed->lookupValue(Module::AccessPathTy(), getIdentifier(rawAccessPath.front()), NLKind::QualifiedLookup, values); } } rawAccessPath = rawAccessPath.slice(1); // Then, follow the chain of nested ValueDecls until we run out of // identifiers in the access path. SmallVector baseValues; for (IdentifierID nextID : rawAccessPath) { baseValues.swap(values); values.clear(); for (auto base : baseValues) { if (auto nominal = dyn_cast(base)) { Identifier memberName = getIdentifier(nextID); auto members = nominal->lookupDirect(memberName); values.append(members.begin(), members.end()); } } } // If we have a type to validate against, filter out any ValueDecls that // don't match that type. CanType expectedTy; if (kind == XRefKind::SwiftValue) if (Type maybeExpectedTy = getType(expectedTypeID)) expectedTy = maybeExpectedTy->getCanonicalType(); ValueDecl *result = nullptr; for (auto value : values) { if (!value->hasClangNode() && value->getModuleContext() != M) continue; if (expectedTy && value->getType()->getCanonicalType() != expectedTy) continue; if (!result || result == value) { result = value; continue; } // It's an error if more than one value has the same type. // FIXME: Functions and constructors can overload based on parameter // names. error(); return nullptr; } // It's an error if lookup doesn't actually find anything -- that means // the module's out of date. if (!result) { error(); return nullptr; } if (kind == XRefKind::SwiftGenericParameter) { GenericParamList *paramList = nullptr; if (auto nominal = dyn_cast(result)) paramList = nominal->getGenericParams(); else if (auto fn = dyn_cast(result)) paramList = fn->getGenericParams(); else if (auto ctor = dyn_cast(result)) paramList = ctor->getGenericParams(); if (!paramList) { error(); return nullptr; } if (expectedTypeID >= paramList->size()) { error(); return nullptr; } result = paramList->getParams()[expectedTypeID].getDecl(); assert(result); } declOrOffset = result; break; } case XRefKind::SwiftOperator: { assert(rawAccessPath.size() == 1 && "can't import operators not at module scope"); Identifier opName = getIdentifier(rawAccessPath.back()); switch (expectedTypeID) { case OperatorKind::Infix: { auto op = M->lookupInfixOperator(opName); declOrOffset = op.hasValue() ? op.getValue() : nullptr; break; } case OperatorKind::Prefix: { auto op = M->lookupPrefixOperator(opName); declOrOffset = op.hasValue() ? op.getValue() : nullptr; break; } case OperatorKind::Postfix: { auto op = M->lookupPostfixOperator(opName); declOrOffset = op.hasValue() ? op.getValue() : nullptr; break; } default: // Unknown operator kind. error(); return nullptr; } break; } default: // Unknown cross-reference kind. error(); return nullptr; } break; } default: // We don't know how to deserialize this kind of decl. error(); return nullptr; } if (DidRecord) (*DidRecord)(declOrOffset); return declOrOffset; } /// Translate from the Serialization calling convention enum values to the AST /// strongly-typed enum. /// /// The former is guaranteed to be stable, but may not reflect this version of /// the AST. static Optional getActualCC(uint8_t cc) { switch (cc) { #define CASE(THE_CC) \ case serialization::AbstractCC::THE_CC: \ return swift::AbstractCC::THE_CC; CASE(C) CASE(ObjCMethod) CASE(Freestanding) CASE(Method) #undef CASE default: return Nothing; } } /// Translate from the serialization Ownership enumerators, which are /// guaranteed to be stable, to the AST ones. static Optional getActualOwnership(serialization::Ownership raw) { switch (raw) { case serialization::Ownership::Strong: return swift::Ownership::Strong; case serialization::Ownership::Unowned: return swift::Ownership::Unowned; case serialization::Ownership::Weak: return swift::Ownership::Weak; } return Nothing; } Type ModuleFile::getType(TypeID TID) { if (TID == 0) return Type(); assert(TID <= Types.size() && "invalid decl ID"); auto &typeOrOffset = Types[TID-1]; if (typeOrOffset.isComplete()) return typeOrOffset; BCOffsetRAII restoreOffset(DeclTypeCursor); DeclTypeCursor.JumpToBit(typeOrOffset); auto entry = DeclTypeCursor.advance(); if (entry.Kind != llvm::BitstreamEntry::Record) { // We don't know how to serialize types represented by sub-blocks. error(); return nullptr; } ASTContext &ctx = ModuleContext->Ctx; SmallVector scratch; StringRef blobData; unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, &blobData); switch (recordID) { case decls_block::NAME_ALIAS_TYPE: { DeclID underlyingID; decls_block::NameAliasTypeLayout::readRecord(scratch, underlyingID); auto alias = dyn_cast_or_null(getDecl(underlyingID)); if (!alias) { error(); return nullptr; } typeOrOffset = alias->getDeclaredType(); break; } case decls_block::NOMINAL_TYPE: { DeclID declID; TypeID parentID; decls_block::NominalTypeLayout::readRecord(scratch, declID, parentID); Type parentTy = getType(parentID); // Record the type as soon as possible. Members of a nominal type often // try to refer back to the type. getDecl(declID, Nothing, makeStackLambda([&](Decl *D) { // FIXME: Hack for "typedef struct CGRect CGRect". In the long run we need // something less brittle that would also handle pointer typedefs and // typedefs that just /happen/ to match a tagged name but don't actually // point to the tagged type. if (auto alias = dyn_cast(D)) D = alias->getUnderlyingType()->getAnyNominal(); auto nominal = cast(D); typeOrOffset = NominalType::get(nominal, parentTy, ctx); })); assert(typeOrOffset.isComplete()); break; } case decls_block::PAREN_TYPE: { TypeID underlyingID; decls_block::ParenTypeLayout::readRecord(scratch, underlyingID); typeOrOffset = ParenType::get(ctx, getType(underlyingID)); break; } case decls_block::TUPLE_TYPE: { // The tuple record itself is empty. Read all trailing elements. SmallVector elements; while (true) { auto entry = DeclTypeCursor.advance(AF_DontPopBlockAtEnd); if (entry.Kind != llvm::BitstreamEntry::Record) break; scratch.clear(); unsigned recordID = DeclTypeCursor.readRecord(entry.ID, scratch, &blobData); if (recordID != decls_block::TUPLE_TYPE_ELT) break; IdentifierID nameID; TypeID typeID; uint8_t rawDefArg; bool isVararg; decls_block::TupleTypeEltLayout::readRecord(scratch, nameID, typeID, rawDefArg, isVararg); DefaultArgumentKind defArg = DefaultArgumentKind::None; if (auto actualDefArg = getActualDefaultArgKind(rawDefArg)) defArg = *actualDefArg; elements.push_back({getType(typeID), getIdentifier(nameID), defArg, isVararg}); } typeOrOffset = TupleType::get(elements, ctx); break; } case decls_block::FUNCTION_TYPE: { TypeID inputID; TypeID resultID; uint8_t rawCallingConvention; bool autoClosure; bool thin; bool noreturn; bool blockCompatible; decls_block::FunctionTypeLayout::readRecord(scratch, inputID, resultID, rawCallingConvention, autoClosure, thin, noreturn, blockCompatible); auto callingConvention = getActualCC(rawCallingConvention); if (!callingConvention.hasValue()) { error(); return nullptr; } auto Info = FunctionType::ExtInfo(callingConvention.getValue(), thin, noreturn, autoClosure, blockCompatible); typeOrOffset = FunctionType::get(getType(inputID), getType(resultID), Info, ctx); break; } case decls_block::METATYPE_TYPE: { TypeID instanceID; decls_block::MetaTypeTypeLayout::readRecord(scratch, instanceID); typeOrOffset = MetaTypeType::get(getType(instanceID), ctx); break; } case decls_block::LVALUE_TYPE: { TypeID objectTypeID; bool isImplicit, isNonSettable; decls_block::LValueTypeLayout::readRecord(scratch, objectTypeID, isImplicit, isNonSettable); LValueType::Qual quals; if (isImplicit) quals |= LValueType::Qual::Implicit; if (isNonSettable) quals |= LValueType::Qual::NonSettable; typeOrOffset = LValueType::get(getType(objectTypeID), quals, ctx); break; } case decls_block::REFERENCE_STORAGE_TYPE: { uint8_t rawOwnership; TypeID referentTypeID; decls_block::ReferenceStorageTypeLayout::readRecord(scratch, rawOwnership, referentTypeID); auto ownership = getActualOwnership((serialization::Ownership) rawOwnership); if (!ownership.hasValue()) { error(); break; } typeOrOffset = ReferenceStorageType::get(getType(referentTypeID), ownership.getValue(), ctx); break; } case decls_block::ARCHETYPE_TYPE: { IdentifierID nameID; bool isPrimary; TypeID parentOrIndex; TypeID superclassID; ArrayRef rawConformanceIDs; decls_block::ArchetypeTypeLayout::readRecord(scratch, nameID, isPrimary, parentOrIndex, superclassID, rawConformanceIDs); ArchetypeType *parent = nullptr; Type superclass; Optional index; SmallVector conformances; if (isPrimary) index = parentOrIndex; else parent = getType(parentOrIndex)->castTo(); superclass = getType(superclassID); for (DeclID protoID : rawConformanceIDs) conformances.push_back(cast(getDecl(protoID))); // See if we triggered deserialization through our conformances. if (typeOrOffset.isComplete()) break; auto archetype = ArchetypeType::getNew(ctx, parent, getIdentifier(nameID), conformances, superclass, index); typeOrOffset = archetype; auto entry = DeclTypeCursor.advance(); if (entry.Kind != llvm::BitstreamEntry::Record) { error(); break; } scratch.clear(); unsigned kind = DeclTypeCursor.readRecord(entry.ID, scratch); if (kind != decls_block::ARCHETYPE_NESTED_TYPE_NAMES) { error(); break; } ArrayRef rawNameIDs; decls_block::ArchetypeNestedTypeNamesLayout::readRecord(scratch, rawNameIDs); entry = DeclTypeCursor.advance(); if (entry.Kind != llvm::BitstreamEntry::Record) { error(); break; } SmallVector scratch2; kind = DeclTypeCursor.readRecord(entry.ID, scratch2); if (kind != decls_block::ARCHETYPE_NESTED_TYPES) { error(); break; } ArrayRef rawTypeIDs; decls_block::ArchetypeNestedTypesLayout::readRecord(scratch2, rawTypeIDs); SmallVector, 4> nestedTypes; for_each(rawNameIDs, rawTypeIDs, [&](IdentifierID nameID, TypeID nestedID) { auto nestedTy = getType(nestedID)->castTo(); nestedTypes.push_back(std::make_pair(getIdentifier(nameID), nestedTy)); }); archetype->setNestedTypes(ctx, nestedTypes); break; } case decls_block::PROTOCOL_COMPOSITION_TYPE: { ArrayRef rawProtocolIDs; decls_block::ProtocolCompositionTypeLayout::readRecord(scratch, rawProtocolIDs); SmallVector protocols; for (TypeID protoID : rawProtocolIDs) protocols.push_back(getType(protoID)); typeOrOffset = ProtocolCompositionType::get(ctx, protocols); break; } case decls_block::SUBSTITUTED_TYPE: { TypeID originalID, replacementID; decls_block::SubstitutedTypeLayout::readRecord(scratch, originalID, replacementID); typeOrOffset = SubstitutedType::get(getType(originalID), getType(replacementID), ctx); break; } case decls_block::BOUND_GENERIC_TYPE: { DeclID declID; TypeID parentID; unsigned numSubstitutions; ArrayRef rawArgumentIDs; decls_block::BoundGenericTypeLayout::readRecord(scratch, declID, parentID, numSubstitutions, rawArgumentIDs); SmallVector genericArgs; for (TypeID type : rawArgumentIDs) genericArgs.push_back(getType(type)); auto nominal = cast(getDecl(declID)); auto parentTy = getType(parentID); auto boundTy = BoundGenericType::get(nominal, parentTy, genericArgs); typeOrOffset = boundTy; // BoundGenericTypes get uniqued in the ASTContext, so it's possible this // type already has its substitutions. In that case, ignore the module's. if (boundTy->hasSubstitutions()) break; SmallVector substitutions; while (numSubstitutions--) { substitutions.push_back(*maybeReadSubstitution()); } boundTy->setSubstitutions(ctx.AllocateCopy(substitutions)); break; } case decls_block::POLYMORPHIC_FUNCTION_TYPE: { TypeID inputID; TypeID resultID; DeclID genericContextID; uint8_t rawCallingConvention; bool thin; bool noreturn = false; //todo add noreturn serialization. decls_block::PolymorphicFunctionTypeLayout::readRecord(scratch, inputID, resultID, genericContextID, rawCallingConvention, thin, noreturn); auto callingConvention = getActualCC(rawCallingConvention); if (!callingConvention.hasValue()) { error(); return nullptr; } Decl *genericContext = getDecl(genericContextID); assert(genericContext && "loading PolymorphicFunctionType before its decl"); GenericParamList *paramList = nullptr; switch (genericContext->getKind()) { case DeclKind::Constructor: paramList = cast(genericContext)->getGenericParams(); break; case DeclKind::Func: paramList = cast(genericContext)->getGenericParams(); break; case DeclKind::Class: case DeclKind::Struct: case DeclKind::Union: paramList = cast(genericContext)->getGenericParams(); break; default: break; } assert(paramList && "missing generic params for polymorphic function"); auto Info = PolymorphicFunctionType::ExtInfo(callingConvention.getValue(), thin, noreturn); typeOrOffset = PolymorphicFunctionType::get(getType(inputID), getType(resultID), paramList, Info, ctx); break; } case decls_block::ARRAY_SLICE_TYPE: { TypeID baseID, implID; decls_block::ArraySliceTypeLayout::readRecord(scratch, baseID, implID); auto sliceTy = ArraySliceType::get(getType(baseID), ctx); typeOrOffset = sliceTy; // Slice types are uniqued by the ASTContext, so they may already have // type information. If so, ignore the information in the module. if (!sliceTy->hasImplementationType()) sliceTy->setImplementationType(getType(implID)); break; } case decls_block::ARRAY_TYPE: { TypeID baseID; uint64_t size; decls_block::ArrayTypeLayout::readRecord(scratch, baseID, size); typeOrOffset = ArrayType::get(getType(baseID), size, ctx); break; } case decls_block::UNBOUND_GENERIC_TYPE: { DeclID genericID; TypeID parentID; decls_block::UnboundGenericTypeLayout::readRecord(scratch, genericID, parentID); auto genericDecl = cast(getDecl(genericID)); typeOrOffset = UnboundGenericType::get(genericDecl, getType(parentID), ctx); break; } default: // We don't know how to deserialize this kind of type. error(); return nullptr; } return typeOrOffset; } ModuleFile::ModuleFile(llvm::OwningPtr &&input) : ModuleContext(nullptr), InputFile(std::move(input)), InputReader(reinterpret_cast(InputFile->getBufferStart()), reinterpret_cast(InputFile->getBufferEnd())), Status(ModuleStatus::Valid) { llvm::BitstreamCursor cursor{InputReader}; for (unsigned char byte : SIGNATURE) { if (cursor.AtEndOfStream() || cursor.Read(8) != byte) return error(); } // Future-proofing: make sure we validate the control block before we try to // read any other blocks. bool hasValidControlBlock = false; SmallVector scratch; auto topLevelEntry = cursor.advance(); while (topLevelEntry.Kind == llvm::BitstreamEntry::SubBlock) { switch (topLevelEntry.ID) { case llvm::bitc::BLOCKINFO_BLOCK_ID: if (cursor.ReadBlockInfoBlock()) return error(); break; case CONTROL_BLOCK_ID: { cursor.EnterSubBlock(CONTROL_BLOCK_ID); ModuleStatus err = validateControlBlock(cursor, scratch); if (err != ModuleStatus::Valid) return error(err); hasValidControlBlock = true; break; } case INPUT_BLOCK_ID: { if (!hasValidControlBlock) return error(); cursor.EnterSubBlock(INPUT_BLOCK_ID); auto next = cursor.advance(); while (next.Kind == llvm::BitstreamEntry::Record) { scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case input_block::SOURCE_FILE: assert(scratch.empty()); SourcePaths.push_back(blobData); break; case input_block::IMPORTED_MODULE: assert(scratch.empty()); Dependencies.push_back(blobData); break; default: // Unknown input kind, possibly for use by a future version of the // module format. // FIXME: Should we warn about this? break; } next = cursor.advance(); } if (next.Kind != llvm::BitstreamEntry::EndBlock) return error(); break; } case DECLS_AND_TYPES_BLOCK_ID: { if (!hasValidControlBlock) return error(); // The decls-and-types block is lazily loaded. Save the cursor and load // any abbrev records at the start of the block. DeclTypeCursor = cursor; DeclTypeCursor.EnterSubBlock(DECLS_AND_TYPES_BLOCK_ID); if (DeclTypeCursor.advance().Kind == llvm::BitstreamEntry::Error) return error(); // With the main cursor, skip over the block and continue. if (cursor.SkipBlock()) return error(); break; } case IDENTIFIER_DATA_BLOCK_ID: { if (!hasValidControlBlock) return error(); cursor.EnterSubBlock(IDENTIFIER_DATA_BLOCK_ID); auto next = cursor.advanceSkippingSubblocks(); while (next.Kind == llvm::BitstreamEntry::Record) { scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case identifier_block::IDENTIFIER_DATA: assert(scratch.empty()); IdentifierData = blobData; break; default: // Unknown identifier data, which this version of the compiler won't // use. break; } next = cursor.advanceSkippingSubblocks(); } if (next.Kind != llvm::BitstreamEntry::EndBlock) return error(); break; } case INDEX_BLOCK_ID: { if (!hasValidControlBlock) return error(); cursor.EnterSubBlock(INDEX_BLOCK_ID); auto next = cursor.advanceSkippingSubblocks(); while (next.Kind == llvm::BitstreamEntry::Record) { scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case index_block::DECL_OFFSETS: assert(blobData.empty()); Decls.assign(scratch.begin(), scratch.end()); break; case index_block::TYPE_OFFSETS: assert(blobData.empty()); Types.assign(scratch.begin(), scratch.end()); break; case index_block::IDENTIFIER_OFFSETS: assert(blobData.empty()); Identifiers.assign(scratch.begin(), scratch.end()); break; case index_block::TOP_LEVEL_DECLS: assert(blobData.empty()); RawTopLevelIDs.assign(scratch.begin(), scratch.end()); break; case index_block::OPERATORS: assert(blobData.empty()); RawOperatorIDs.assign(scratch.begin(), scratch.end()); break; default: // Unknown index kind, which this version of the compiler won't use. break; } next = cursor.advanceSkippingSubblocks(); } if (next.Kind != llvm::BitstreamEntry::EndBlock) return error(); break; } case FALL_BACK_TO_TRANSLATION_UNIT_ID: // This is a bring-up hack and will eventually go away. Status = ModuleStatus::FallBackToTranslationUnit; break; default: // Unknown top-level block, possibly for use by a future version of the // module format. if (cursor.SkipBlock()) return error(); break; } topLevelEntry = cursor.advance(AF_DontPopBlockAtEnd); } if (topLevelEntry.Kind != llvm::BitstreamEntry::EndBlock) return error(); } bool ModuleFile::associateWithModule(Module *module) { assert(Status == ModuleStatus::Valid && "invalid module file"); assert(!ModuleContext && "already associated with an AST module"); ModuleContext = module; ASTContext &ctx = module->Ctx; bool missingDependency = false; for (auto &dependency : Dependencies) { assert(!dependency.Mod && "already loaded?"); dependency.Mod = getModule(ctx.getIdentifier(dependency.Name)); if (!dependency.Mod) missingDependency = true; } if (missingDependency) { error(ModuleStatus::MissingDependency); return false; } // FIXME: The only reason we're deserializing eagerly is to force extensions // to load, which they shouldn't yet anyway. buildTopLevelDeclMap(); return Status == ModuleStatus::Valid; } void ModuleFile::buildTopLevelDeclMap() { // FIXME: be more lazy about deserialization by encoding this some other way. for (DeclID ID : RawTopLevelIDs) { // FIXME: Right now ExtensionDecls are in here as well. if (auto value = dyn_cast(getDecl(ID))) TopLevelDecls[value->getName()].push_back(value); } RawTopLevelIDs.clear(); } void ModuleFile::lookupValue(Identifier name, SmallVectorImpl &results) { auto iter = TopLevelDecls.find(name); if (iter == TopLevelDecls.end()) return; results.append(iter->second.begin(), iter->second.end()); } OperatorKind getOperatorKind(DeclKind kind) { switch (kind) { case DeclKind::PrefixOperator: return Prefix; case DeclKind::PostfixOperator: return Postfix; case DeclKind::InfixOperator: return Infix; default: llvm_unreachable("unknown operator fixity"); } } OperatorDecl *ModuleFile::lookupOperator(Identifier name, DeclKind fixity) { if (!RawOperatorIDs.empty()) { for (DeclID ID : RawOperatorIDs) { auto op = cast(getDecl(ID)); OperatorKey key(op->getName(), getOperatorKind(op->getKind())); Operators[key] = op; } RawOperatorIDs = {}; } return Operators.lookup(OperatorKey(name, getOperatorKind(fixity))); } void ModuleFile::getReexportedModules( SmallVectorImpl &results) { // FIXME: Lock down on re-exports. // FIXME: Handle imports with access paths. for (auto &dep : Dependencies) results.push_back({Module::AccessPathTy(), dep.Mod}); } void ModuleFile::lookupVisibleDecls(Module::AccessPathTy accessPath, VisibleDeclConsumer &consumer, NLKind lookupKind) { assert(accessPath.size() <= 1 && "can only refer to top-level decls"); if (!accessPath.empty()) { auto I = TopLevelDecls.find(accessPath.front().first); if (I == TopLevelDecls.end()) return; for (auto vd : I->second) consumer.foundDecl(vd); return; } for (auto &topLevelEntry : TopLevelDecls) { for (auto &value : topLevelEntry.second) consumer.foundDecl(value); } }