/// When we see an expression in a TopLevelCodeDecl in the REPL, process it, /// adding the proper decls back to the top level of the file. void REPLChecker::processREPLTopLevelExpr(Expr *E) { CanType T = E->getType()->getCanonicalType(); // Don't try to print invalid expressions, module exprs, or void expressions. if (T->hasError() || isa<ModuleType>(T) || T->isVoid()) return; // Okay, we need to print this expression. We generally do this by creating a // REPL metavariable (e.g. r4) to hold the result, so it can be referred to // in the future. However, if this is a direct reference to a decl (e.g. "x") // then don't create a repl metavariable. if (VarDecl *d = getObviousDeclFromExpr(E)) { generatePrintOfExpression(d->getName().str(), E); return; } // Remove the expression from being in the list of decls to execute, we're // going to reparent it. auto TLCD = cast<TopLevelCodeDecl>(SF.Decls.back()); E = TC.coerceToRValue(E); // Create the meta-variable, let the typechecker name it. Identifier name = TC.getNextResponseVariableName(SF.getParentModule()); VarDecl *vd = new (Context) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Let, /*IsCaptureList*/false, E->getStartLoc(), name, E->getType(), &SF); vd->setInterfaceType(E->getType()); SF.Decls.push_back(vd); // Create a PatternBindingDecl to bind the expression into the decl. Pattern *metavarPat = new (Context) NamedPattern(vd); metavarPat->setType(E->getType()); PatternBindingDecl *metavarBinding = PatternBindingDecl::create( Context, /*StaticLoc*/ SourceLoc(), StaticSpellingKind::None, /*VarLoc*/ E->getStartLoc(), metavarPat, /*EqualLoc*/ SourceLoc(), E, TLCD); // Overwrite the body of the existing TopLevelCodeDecl. TLCD->setBody(BraceStmt::create(Context, metavarBinding->getStartLoc(), ASTNode(metavarBinding), metavarBinding->getEndLoc(), /*implicit*/true)); // Finally, print the variable's value. E = TC.buildCheckedRefExpr(vd, &SF, DeclNameLoc(E->getStartLoc()), /*Implicit=*/true); generatePrintOfExpression(vd->getName().str(), E); }
std::pair<VarDecl *, PatternBindingDecl *> DerivedConformance::declareDerivedProperty(Identifier name, Type propertyInterfaceType, Type propertyContextType, bool isStatic, bool isFinal) { auto &C = TC.Context; auto parentDC = getConformanceContext(); VarDecl *propDecl = new (C) VarDecl(/*IsStatic*/isStatic, VarDecl::Specifier::Var, /*IsCaptureList*/false, SourceLoc(), name, propertyContextType, parentDC); propDecl->setImplicit(); propDecl->copyFormalAccessFrom(Nominal, /*sourceIsParentContext*/ true); propDecl->setInterfaceType(propertyInterfaceType); propDecl->setValidationStarted(); // If this is supposed to be a final property, mark it as such. assert(isFinal || !parentDC->getAsClassOrClassExtensionContext()); if (isFinal && parentDC->getAsClassOrClassExtensionContext() && !propDecl->isFinal()) propDecl->getAttrs().add(new (C) FinalAttr(/*IsImplicit=*/true)); Pattern *propPat = new (C) NamedPattern(propDecl, /*implicit*/ true); propPat->setType(propertyContextType); propPat = new (C) TypedPattern(propPat, TypeLoc::withoutLoc(propertyContextType), /*implicit*/ true); propPat->setType(propertyContextType); auto pbDecl = PatternBindingDecl::create(C, SourceLoc(), StaticSpellingKind::None, SourceLoc(), propPat, nullptr, parentDC); pbDecl->setImplicit(); return {propDecl, pbDecl}; }
std::pair<VarDecl *, PatternBindingDecl *> DerivedConformance::declareDerivedReadOnlyProperty(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *typeDecl, Identifier name, Type propertyInterfaceType, Type propertyContextType, FuncDecl *getterDecl, bool isStatic, bool isFinal) { auto &C = tc.Context; auto parentDC = cast<DeclContext>(parentDecl); VarDecl *propDecl = new (C) VarDecl(/*IsStatic*/isStatic, /*IsLet*/false, /*IsCaptureList*/false, SourceLoc(), name, propertyContextType, parentDC); propDecl->setImplicit(); propDecl->makeComputed(SourceLoc(), getterDecl, nullptr, nullptr, SourceLoc()); propDecl->setAccessibility(getterDecl->getFormalAccess()); propDecl->setInterfaceType(propertyInterfaceType); // If this is supposed to be a final property, mark it as such. assert(isFinal || !parentDC->getAsClassOrClassExtensionContext()); if (isFinal && parentDC->getAsClassOrClassExtensionContext() && !propDecl->isFinal()) propDecl->getAttrs().add(new (C) FinalAttr(/*IsImplicit=*/true)); Pattern *propPat = new (C) NamedPattern(propDecl, /*implicit*/ true); propPat->setType(propertyContextType); propPat = new (C) TypedPattern(propPat, TypeLoc::withoutLoc(propertyContextType), /*implicit*/ true); auto pbDecl = PatternBindingDecl::create(C, SourceLoc(), StaticSpellingKind::None, SourceLoc(), propPat, nullptr, parentDC); pbDecl->setImplicit(); return {propDecl, pbDecl}; }
std::pair<VarDecl *, PatternBindingDecl *> DerivedConformance::declareDerivedReadOnlyProperty(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *typeDecl, Identifier name, Type propertyInterfaceType, Type propertyContextType, FuncDecl *getterDecl, bool isStatic) { auto &C = tc.Context; auto parentDC = cast<DeclContext>(parentDecl); VarDecl *propDecl = new (C) VarDecl(isStatic, /*let*/ false, SourceLoc(), name, propertyContextType, parentDC); propDecl->setImplicit(); propDecl->makeComputed(SourceLoc(), getterDecl, nullptr, nullptr, SourceLoc()); propDecl->setAccessibility(typeDecl->getFormalAccess()); propDecl->setInterfaceType(propertyInterfaceType); Pattern *propPat = new (C) NamedPattern(propDecl, /*implicit*/ true); propPat->setType(propertyContextType); propPat = new (C) TypedPattern(propPat, TypeLoc::withoutLoc(propertyContextType), /*implicit*/ true); auto pbDecl = PatternBindingDecl::create(C, SourceLoc(), StaticSpellingKind::None, SourceLoc(), propPat, nullptr, parentDC); pbDecl->setImplicit(); return {propDecl, pbDecl}; }
/// processREPLTopLevelPatternBinding - When we see a new PatternBinding parsed /// into the REPL, process it by generating code to print it out. void REPLChecker::processREPLTopLevelPatternBinding(PatternBindingDecl *PBD) { // If there is no initializer for the new variable, don't auto-print it. // This would just cause a confusing definite initialization error. Some // day we will do some high level analysis of uninitialized variables // (rdar://15157729) but until then, output a specialized error. unsigned entryIdx = 0U-1; for (auto patternEntry : PBD->getPatternList()) { ++entryIdx; if (!patternEntry.getInit()) { TC.diagnose(PBD->getStartLoc(), diag::repl_must_be_initialized); continue; } auto pattern = patternEntry.getPattern(); llvm::SmallString<16> PatternString; PatternBindingPrintLHS(PatternString).visit(pattern); // If the bound pattern is a single value, use a DeclRefExpr on the // underlying Decl to print it. if (auto *NP = dyn_cast<NamedPattern>(pattern-> getSemanticsProvidingPattern())) { Expr *E = TC.buildCheckedRefExpr(NP->getDecl(), &SF, PBD->getStartLoc(), /*Implicit=*/true); generatePrintOfExpression(PatternString, E); continue; } // Otherwise, we may not have a way to name all of the pieces of the pattern. // Create a repl metavariable to capture the whole thing so we can reference // it, then assign that into the pattern. For example, translate: // var (x, y, _) = foo() // into: // var r123 = foo() // var (x, y, _) = r123 // replPrint(r123) // Remove PBD from the list of Decls so we can insert before it. auto PBTLCD = cast<TopLevelCodeDecl>(SF.Decls.back()); SF.Decls.pop_back(); // Create the meta-variable, let the typechecker name it. Identifier name = TC.getNextResponseVariableName(SF.getParentModule()); VarDecl *vd = new (Context) VarDecl(/*static*/ false, /*IsLet*/true, PBD->getStartLoc(), name, pattern->getType(), &SF); SF.Decls.push_back(vd); // Create a PatternBindingDecl to bind the expression into the decl. Pattern *metavarPat = new (Context) NamedPattern(vd); metavarPat->setType(vd->getType()); PatternBindingDecl *metavarBinding = PatternBindingDecl::create(Context, SourceLoc(), StaticSpellingKind::None, PBD->getStartLoc(), metavarPat, patternEntry.getInit(), &SF); auto MVBrace = BraceStmt::create(Context, metavarBinding->getStartLoc(), ASTNode(metavarBinding), metavarBinding->getEndLoc()); auto *MVTLCD = new (Context) TopLevelCodeDecl(&SF, MVBrace); SF.Decls.push_back(MVTLCD); // Replace the initializer of PBD with a reference to our repl temporary. Expr *E = TC.buildCheckedRefExpr(vd, &SF, vd->getStartLoc(), /*Implicit=*/true); E = TC.coerceToMaterializable(E); PBD->setInit(entryIdx, E); SF.Decls.push_back(PBTLCD); // Finally, print out the result, by referring to the repl temp. E = TC.buildCheckedRefExpr(vd, &SF, vd->getStartLoc(), /*Implicit=*/true); generatePrintOfExpression(PatternString, E); } }
/// Create AST statements which convert from an enum to an Int with a switch. /// \p stmts The generated statements are appended to this vector. /// \p parentDC Either an extension or the enum itself. /// \p enumDecl The enum declaration. /// \p enumVarDecl The enum input variable. /// \p funcDecl The parent function. /// \p indexName The name of the output variable. /// \return A DeclRefExpr of the output variable (of type Int). static DeclRefExpr *convertEnumToIndex(SmallVectorImpl<ASTNode> &stmts, DeclContext *parentDC, EnumDecl *enumDecl, VarDecl *enumVarDecl, AbstractFunctionDecl *funcDecl, const char *indexName) { ASTContext &C = enumDecl->getASTContext(); Type enumType = enumVarDecl->getType(); Type intType = C.getIntDecl()->getDeclaredType(); auto indexVar = new (C) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Var, /*IsCaptureList*/false, SourceLoc(), C.getIdentifier(indexName), intType, funcDecl); indexVar->setInterfaceType(intType); indexVar->setImplicit(); // generate: var indexVar Pattern *indexPat = new (C) NamedPattern(indexVar, /*implicit*/ true); indexPat->setType(intType); indexPat = new (C) TypedPattern(indexPat, TypeLoc::withoutLoc(intType)); indexPat->setType(intType); auto *indexBind = PatternBindingDecl::createImplicit( C, StaticSpellingKind::None, indexPat, /*InitExpr*/ nullptr, funcDecl); unsigned index = 0; SmallVector<ASTNode, 4> cases; for (auto elt : enumDecl->getAllElements()) { // generate: case .<Case>: auto pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), SourceLoc(), SourceLoc(), Identifier(), elt, nullptr); pat->setImplicit(); auto labelItem = CaseLabelItem(pat); // generate: indexVar = <index> auto indexExpr = IntegerLiteralExpr::createFromUnsigned(C, index++); auto indexRef = new (C) DeclRefExpr(indexVar, DeclNameLoc(), /*implicit*/true); auto assignExpr = new (C) AssignExpr(indexRef, SourceLoc(), indexExpr, /*implicit*/ true); auto body = BraceStmt::create(C, SourceLoc(), ASTNode(assignExpr), SourceLoc()); cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, /*HasBoundDecls=*/false, SourceLoc(), SourceLoc(), body)); } // generate: switch enumVar { } auto enumRef = new (C) DeclRefExpr(enumVarDecl, DeclNameLoc(), /*implicit*/true); auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), enumRef, SourceLoc(), cases, SourceLoc(), C); stmts.push_back(indexBind); stmts.push_back(switchStmt); return new (C) DeclRefExpr(indexVar, DeclNameLoc(), /*implicit*/ true, AccessSemantics::Ordinary, intType); }
/// Derive a 'hashValue' implementation. static ValueDecl *deriveHashable_hashValue(DerivedConformance &derived) { // @derived var hashValue: Int { // return _hashValue(for: self) // } auto &tc = derived.TC; ASTContext &C = tc.Context; auto parentDC = derived.getConformanceContext(); Type intType = C.getIntDecl()->getDeclaredType(); // We can't form a Hashable conformance if Int isn't Hashable or // ExpressibleByIntegerLiteral. if (!tc.conformsToProtocol(intType, C.getProtocol(KnownProtocolKind::Hashable), parentDC, None)) { tc.diagnose(derived.ConformanceDecl, diag::broken_int_hashable_conformance); return nullptr; } ProtocolDecl *intLiteralProto = C.getProtocol(KnownProtocolKind::ExpressibleByIntegerLiteral); if (!tc.conformsToProtocol(intType, intLiteralProto, parentDC, None)) { tc.diagnose(derived.ConformanceDecl, diag::broken_int_integer_literal_convertible_conformance); return nullptr; } VarDecl *hashValueDecl = new (C) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Var, /*IsCaptureList*/false, SourceLoc(), C.Id_hashValue, intType, parentDC); auto *selfDecl = ParamDecl::createSelf(SourceLoc(), parentDC); ParameterList *params = ParameterList::createEmpty(C); AccessorDecl *getterDecl = AccessorDecl::create(C, /*FuncLoc=*/SourceLoc(), /*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::Get, AddressorKind::NotAddressor, hashValueDecl, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), /*GenericParams=*/nullptr, selfDecl, params, TypeLoc::withoutLoc(intType), parentDC); getterDecl->setImplicit(); getterDecl->setBodySynthesizer(&deriveBodyHashable_hashValue); // Compute the interface type of hashValue(). if (auto env = parentDC->getGenericEnvironmentOfContext()) getterDecl->setGenericEnvironment(env); getterDecl->computeType(); getterDecl->setValidationToChecked(); getterDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); // Finish creating the property. hashValueDecl->setImplicit(); hashValueDecl->setInterfaceType(intType); hashValueDecl->setValidationToChecked(); hashValueDecl->setAccessors(StorageImplInfo::getImmutableComputed(), SourceLoc(), {getterDecl}, SourceLoc()); hashValueDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); Pattern *hashValuePat = new (C) NamedPattern(hashValueDecl, /*implicit*/true); hashValuePat->setType(intType); hashValuePat = new (C) TypedPattern(hashValuePat, TypeLoc::withoutLoc(intType), /*implicit*/ true); hashValuePat->setType(intType); auto *patDecl = PatternBindingDecl::createImplicit( C, StaticSpellingKind::None, hashValuePat, /*InitExpr*/ nullptr, parentDC); C.addSynthesizedDecl(hashValueDecl); C.addSynthesizedDecl(getterDecl); derived.addMembersToConformanceContext({getterDecl, hashValueDecl, patDecl}); return hashValueDecl; }
/// Derive a 'hashValue' implementation for an enum. static ValueDecl * deriveHashable_hashValue(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *typeDecl, void (*bodySynthesizer)(AbstractFunctionDecl *)) { // enum SomeEnum { // case A, B, C // @derived var hashValue: Int { // var result: Int // switch self { // case A: // result = 0 // case B: // result = 1 // case C: // result = 2 // } // return result // } // } // // enum SomeEnumWithAssociatedValues { // case A, B(Int), C(String, Int) // @derived var hashValue: Int { // var result: Int // switch self { // case A: // result = 0 // case B(let a0): // result = 1 // result = _combineHashValues(result, a0.hashValue) // case C(let a0, let a1): // result = 2 // result = _combineHashValues(result, a0.hashValue) // result = _combineHashValues(result, a1.hashValue) // } // return result // } // } // // struct SomeStruct { // var x: Int // var y: String // @derived var hashValue: Int { // var result = 0 // result = _combineHashValues(result, x.hashValue) // result = _combineHashValues(result, y.hashValue) // return result // } // } ASTContext &C = tc.Context; auto parentDC = cast<DeclContext>(parentDecl); Type intType = C.getIntDecl()->getDeclaredType(); // We can't form a Hashable conformance if Int isn't Hashable or // ExpressibleByIntegerLiteral. if (!tc.conformsToProtocol(intType,C.getProtocol(KnownProtocolKind::Hashable), typeDecl, None)) { tc.diagnose(typeDecl->getLoc(), diag::broken_int_hashable_conformance); return nullptr; } ProtocolDecl *intLiteralProto = C.getProtocol(KnownProtocolKind::ExpressibleByIntegerLiteral); if (!tc.conformsToProtocol(intType, intLiteralProto, typeDecl, None)) { tc.diagnose(typeDecl->getLoc(), diag::broken_int_integer_literal_convertible_conformance); return nullptr; } VarDecl *hashValueDecl = new (C) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Var, /*IsCaptureList*/false, SourceLoc(), C.Id_hashValue, intType, parentDC); auto selfDecl = ParamDecl::createSelf(SourceLoc(), parentDC); ParameterList *params[] = { ParameterList::createWithoutLoc(selfDecl), ParameterList::createEmpty(C) }; AccessorDecl *getterDecl = AccessorDecl::create(C, /*FuncLoc=*/SourceLoc(), /*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::IsGetter, AddressorKind::NotAddressor, hashValueDecl, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), /*GenericParams=*/nullptr, params, TypeLoc::withoutLoc(intType), parentDC); getterDecl->setImplicit(); getterDecl->setBodySynthesizer(bodySynthesizer); // Compute the type of hashValue(). Type methodType = FunctionType::get(TupleType::getEmpty(tc.Context), intType); // Compute the interface type of hashValue(). Type interfaceType; auto selfParam = computeSelfParam(getterDecl); if (auto sig = parentDC->getGenericSignatureOfContext()) { getterDecl->setGenericEnvironment(parentDC->getGenericEnvironmentOfContext()); interfaceType = GenericFunctionType::get(sig, {selfParam}, methodType, AnyFunctionType::ExtInfo()); } else interfaceType = FunctionType::get({selfParam}, methodType, AnyFunctionType::ExtInfo()); getterDecl->setInterfaceType(interfaceType); getterDecl->setValidationStarted(); getterDecl->copyFormalAccessFrom(typeDecl, /*sourceIsParentContext*/true); // Finish creating the property. hashValueDecl->setImplicit(); hashValueDecl->setInterfaceType(intType); hashValueDecl->setValidationStarted(); hashValueDecl->makeComputed(SourceLoc(), getterDecl, nullptr, nullptr, SourceLoc()); hashValueDecl->copyFormalAccessFrom(typeDecl, /*sourceIsParentContext*/true); Pattern *hashValuePat = new (C) NamedPattern(hashValueDecl, /*implicit*/true); hashValuePat->setType(intType); hashValuePat = new (C) TypedPattern(hashValuePat, TypeLoc::withoutLoc(intType), /*implicit*/ true); hashValuePat->setType(intType); auto patDecl = PatternBindingDecl::create(C, SourceLoc(), StaticSpellingKind::None, SourceLoc(), hashValuePat, nullptr, parentDC); patDecl->setImplicit(); tc.Context.addSynthesizedDecl(hashValueDecl); tc.Context.addSynthesizedDecl(getterDecl); auto dc = cast<IterableDeclContext>(parentDecl); dc->addMember(getterDecl); dc->addMember(hashValueDecl); dc->addMember(patDecl); return hashValueDecl; }
/// Derive the body for the 'hashValue' getter for a struct. static void deriveBodyHashable_struct_hashValue(AbstractFunctionDecl *hashValueDecl) { auto parentDC = hashValueDecl->getDeclContext(); ASTContext &C = parentDC->getASTContext(); auto structDecl = parentDC->getAsStructOrStructExtensionContext(); SmallVector<ASTNode, 6> statements; auto selfDecl = hashValueDecl->getImplicitSelfDecl(); Type intType = C.getIntDecl()->getDeclaredType(); auto resultVar = new (C) VarDecl(/*IsStatic*/ false, VarDecl::Specifier::Var, /*IsCaptureList*/ false, SourceLoc(), C.getIdentifier("result"), intType, hashValueDecl); resultVar->setInterfaceType(intType); resultVar->setImplicit(); // var result: Int Pattern *resultPat = new (C) NamedPattern(resultVar, /*implicit*/ true); resultPat->setType(intType); resultPat = new (C) TypedPattern(resultPat, TypeLoc::withoutLoc(intType)); resultPat->setType(intType); auto resultBind = PatternBindingDecl::create(C, SourceLoc(), StaticSpellingKind::None, SourceLoc(), resultPat, nullptr, hashValueDecl); statements.push_back(resultBind); // result = 0 { auto resultRef = new (C) DeclRefExpr(resultVar, DeclNameLoc(), /*implicit*/ true); auto assignExpr = new (C) AssignExpr(resultRef, SourceLoc(), integerLiteralExpr(C, 0), /*implicit*/ true); statements.emplace_back(ASTNode(assignExpr)); } auto storedProperties = structDecl->getStoredProperties(/*skipInaccessible=*/true); // For each stored property, generate a statement that combines its hash value // into the result. for (auto propertyDecl : storedProperties) { auto propertyRef = new (C) DeclRefExpr(propertyDecl, DeclNameLoc(), /*implicit*/ true); auto selfRef = new (C) DeclRefExpr(selfDecl, DeclNameLoc(), /*implicit*/ true); auto selfPropertyExpr = new (C) DotSyntaxCallExpr(propertyRef, SourceLoc(), selfRef); // result = _combineHashValues(result, <property>.hashValue) auto combineExpr = combineHashValuesAssignmentExpr(C, resultVar, selfPropertyExpr); statements.emplace_back(ASTNode(combineExpr)); } { // return result auto resultRef = new (C) DeclRefExpr(resultVar, DeclNameLoc(), /*implicit*/ true, AccessSemantics::Ordinary, intType); auto returnStmt = new (C) ReturnStmt(SourceLoc(), resultRef); statements.push_back(returnStmt); } auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); hashValueDecl->setBody(body); }
static void deriveBodyHashable_enum_hashValue(AbstractFunctionDecl *hashValueDecl) { auto parentDC = hashValueDecl->getDeclContext(); ASTContext &C = parentDC->getASTContext(); auto enumDecl = parentDC->getAsEnumOrEnumExtensionContext(); SmallVector<ASTNode, 3> statements; auto selfDecl = hashValueDecl->getImplicitSelfDecl(); Type enumType = selfDecl->getType(); Type intType = C.getIntDecl()->getDeclaredType(); auto resultVar = new (C) VarDecl(/*IsStatic*/ false, VarDecl::Specifier::Var, /*IsCaptureList*/ false, SourceLoc(), C.getIdentifier("result"), intType, hashValueDecl); resultVar->setInterfaceType(intType); resultVar->setImplicit(); // var result Pattern *resultPat = new (C) NamedPattern(resultVar, /*implicit*/ true); resultPat->setType(intType); resultPat = new (C) TypedPattern(resultPat, TypeLoc::withoutLoc(intType)); resultPat->setType(intType); auto resultBind = PatternBindingDecl::create(C, SourceLoc(), StaticSpellingKind::None, SourceLoc(), resultPat, nullptr, hashValueDecl); unsigned index = 0; SmallVector<ASTNode, 4> cases; auto hasNoAssociatedValues = enumDecl->hasOnlyCasesWithoutAssociatedValues(); // For each enum element, generate a case statement that binds the associated // values so that their hash values can be obtained. for (auto elt : enumDecl->getAllElements()) { // case .<elt>(let a0, let a1, ...): SmallVector<VarDecl*, 3> payloadVars; SmallVector<ASTNode, 3> combineExprs; auto payloadPattern = enumElementPayloadSubpattern(elt, 'a', hashValueDecl, payloadVars); auto pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), SourceLoc(), SourceLoc(), elt->getName(), elt, payloadPattern); pat->setImplicit(); auto labelItem = CaseLabelItem(pat); // If the enum has no associated values, we use the ordinal alone as the // hash value, because that is sufficient for a good distribution. If any // case does have associated values, then the ordinal is used as the first // term combined into _combineHashValues, and the final result after // combining the payload is passed to _mixInt to improve the distribution. // result = <ordinal> { auto ordinalExpr = integerLiteralExpr(C, index++); auto resultRef = new (C) DeclRefExpr(resultVar, DeclNameLoc(), /*implicit*/ true); auto assignExpr = new (C) AssignExpr(resultRef, SourceLoc(), ordinalExpr, /*implicit*/ true); combineExprs.emplace_back(ASTNode(assignExpr)); } if (!hasNoAssociatedValues) { // Generate a sequence of expressions that combine the payload's hash // values into result. for (auto payloadVar : payloadVars) { auto payloadVarRef = new (C) DeclRefExpr(payloadVar, DeclNameLoc(), /*implicit*/ true); // result = _combineHashValues(result, <payloadVar>.hashValue) auto combineExpr = combineHashValuesAssignmentExpr(C, resultVar, payloadVarRef); combineExprs.emplace_back(ASTNode(combineExpr)); } } auto hasBoundDecls = !payloadVars.empty(); auto body = BraceStmt::create(C, SourceLoc(), combineExprs, SourceLoc()); cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, hasBoundDecls, SourceLoc(), SourceLoc(), body)); } // generate: switch enumVar { } auto enumRef = new (C) DeclRefExpr(selfDecl, DeclNameLoc(), /*implicit*/true); auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), enumRef, SourceLoc(), cases, SourceLoc(), C); statements.push_back(resultBind); statements.push_back(switchStmt); // generate: return result auto resultRef = new (C) DeclRefExpr(resultVar, DeclNameLoc(), /*implicit*/ true, AccessSemantics::Ordinary, intType); auto returnStmt = new (C) ReturnStmt(SourceLoc(), resultRef); statements.push_back(returnStmt); auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); hashValueDecl->setBody(body); }
/// Derive a 'hashValue' implementation for an enum. static ValueDecl * deriveHashable_enum_hashValue(TypeChecker &tc, Decl *parentDecl, EnumDecl *enumDecl) { // enum SomeEnum { // case A, B, C // @derived var hashValue: Int { // var index: Int // switch self { // case A: // index = 0 // case B: // index = 1 // case C: // index = 2 // } // return index.hashValue // } // } ASTContext &C = tc.Context; auto parentDC = cast<DeclContext>(parentDecl); Type intType = C.getIntDecl()->getDeclaredType(); // We can't form a Hashable conformance if Int isn't Hashable or // IntegerLiteralConvertible. if (!tc.conformsToProtocol(intType,C.getProtocol(KnownProtocolKind::Hashable), enumDecl, None)) { tc.diagnose(enumDecl->getLoc(), diag::broken_int_hashable_conformance); return nullptr; } ProtocolDecl *intLiteralProto = C.getProtocol(KnownProtocolKind::IntegerLiteralConvertible); if (!tc.conformsToProtocol(intType, intLiteralProto, enumDecl, None)) { tc.diagnose(enumDecl->getLoc(), diag::broken_int_integer_literal_convertible_conformance); return nullptr; } auto selfDecl = ParamDecl::createSelf(SourceLoc(), parentDC); ParameterList *params[] = { ParameterList::createWithoutLoc(selfDecl), ParameterList::createEmpty(C) }; FuncDecl *getterDecl = FuncDecl::create(C, SourceLoc(), StaticSpellingKind::None, SourceLoc(), Identifier(), SourceLoc(), SourceLoc(), SourceLoc(), nullptr, Type(), params, TypeLoc::withoutLoc(intType), parentDC); getterDecl->setImplicit(); getterDecl->setBodySynthesizer(deriveBodyHashable_enum_hashValue); // Compute the type of hashValue(). GenericParamList *genericParams = getterDecl->getGenericParamsOfContext(); Type methodType = FunctionType::get(TupleType::getEmpty(tc.Context), intType); Type selfType = getterDecl->computeSelfType(); selfDecl->overwriteType(selfType); Type type; if (genericParams) type = PolymorphicFunctionType::get(selfType, methodType, genericParams); else type = FunctionType::get(selfType, methodType); getterDecl->setType(type); getterDecl->setBodyResultType(intType); // Compute the interface type of hashValue(). Type interfaceType; Type selfIfaceType = getterDecl->computeInterfaceSelfType(false); if (auto sig = parentDC->getGenericSignatureOfContext()) interfaceType = GenericFunctionType::get(sig, selfIfaceType, methodType, AnyFunctionType::ExtInfo()); else interfaceType = FunctionType::get(selfType, methodType); getterDecl->setInterfaceType(interfaceType); getterDecl->setAccessibility(enumDecl->getFormalAccess()); // If the enum was not imported, the derived conformance is either from the // enum itself or an extension, in which case we will emit the declaration // normally. if (enumDecl->hasClangNode()) tc.Context.addExternalDecl(getterDecl); // Create the property. VarDecl *hashValueDecl = new (C) VarDecl(/*static*/ false, /*let*/ false, SourceLoc(), C.Id_hashValue, intType, parentDC); hashValueDecl->setImplicit(); hashValueDecl->makeComputed(SourceLoc(), getterDecl, nullptr, nullptr, SourceLoc()); hashValueDecl->setAccessibility(enumDecl->getFormalAccess()); Pattern *hashValuePat = new (C) NamedPattern(hashValueDecl, /*implicit*/true); hashValuePat->setType(intType); hashValuePat = new (C) TypedPattern(hashValuePat, TypeLoc::withoutLoc(intType), /*implicit*/ true); hashValuePat->setType(intType); auto patDecl = PatternBindingDecl::create(C, SourceLoc(), StaticSpellingKind::None, SourceLoc(), hashValuePat, nullptr, parentDC); patDecl->setImplicit(); auto dc = cast<IterableDeclContext>(parentDecl); dc->addMember(getterDecl); dc->addMember(hashValueDecl); dc->addMember(patDecl); return hashValueDecl; }
static ConstructorDecl *deriveRawRepresentable_init(TypeChecker &tc, Decl *parentDecl, EnumDecl *enumDecl) { ASTContext &C = tc.Context; auto parentDC = cast<DeclContext>(parentDecl); auto rawInterfaceType = enumDecl->getRawType(); auto rawType = ArchetypeBuilder::mapTypeIntoContext(parentDC, rawInterfaceType); // Make sure that the raw type is Equatable. We need it to ensure that we have // a suitable ~= for the switch. auto equatableProto = tc.getProtocol(enumDecl->getLoc(), KnownProtocolKind::Equatable); if (!equatableProto) return nullptr; if (!tc.conformsToProtocol(rawType, equatableProto, enumDecl, None)) { SourceLoc loc = enumDecl->getInherited()[0].getSourceRange().Start; tc.diagnose(loc, diag::enum_raw_type_not_equatable, rawType); return nullptr; } Type enumType = parentDC->getDeclaredTypeInContext(); VarDecl *selfDecl = new (C) ParamDecl(/*IsLet*/false, SourceLoc(), Identifier(), SourceLoc(), C.Id_self, enumType, parentDC); selfDecl->setImplicit(); Pattern *selfParam = new (C) NamedPattern(selfDecl, /*implicit*/ true); selfParam->setType(enumType); selfParam = new (C) TypedPattern(selfParam, TypeLoc::withoutLoc(enumType)); selfParam->setType(enumType); selfParam->setImplicit(); VarDecl *rawDecl = new (C) ParamDecl(/*IsVal*/true, SourceLoc(), C.Id_rawValue, SourceLoc(), C.Id_rawValue, rawType, parentDC); rawDecl->setImplicit(); Pattern *rawParam = new (C) NamedPattern(rawDecl, /*implicit*/ true); rawParam->setType(rawType); rawParam = new (C) TypedPattern(rawParam, TypeLoc::withoutLoc(rawType)); rawParam->setType(rawType); rawParam->setImplicit(); rawParam = new (C) ParenPattern(SourceLoc(), rawParam, SourceLoc()); rawParam->setType(rawType); rawParam->setImplicit(); auto retTy = OptionalType::get(enumType); DeclName name(C, C.Id_init, { C.Id_rawValue }); auto initDecl = new (C) ConstructorDecl(name, SourceLoc(), /*failability*/ OTK_Optional, SourceLoc(), selfParam, rawParam, nullptr, SourceLoc(), parentDC); initDecl->setImplicit(); initDecl->setBodySynthesizer(&deriveBodyRawRepresentable_init); // Compute the type of the initializer. GenericParamList *genericParams = initDecl->getGenericParamsOfContext(); TupleTypeElt element(rawType, C.Id_rawValue); auto argType = TupleType::get(element, C); TupleTypeElt interfaceElement(rawInterfaceType, C.Id_rawValue); auto interfaceArgType = TupleType::get(interfaceElement, C); Type type = FunctionType::get(argType, retTy); Type selfType = initDecl->computeSelfType(); Type selfMetatype = MetatypeType::get(selfType->getInOutObjectType()); Type allocType; Type initType; if (genericParams) { allocType = PolymorphicFunctionType::get(selfMetatype, type, genericParams); initType = PolymorphicFunctionType::get(selfType, type, genericParams); } else { allocType = FunctionType::get(selfMetatype, type); initType = FunctionType::get(selfType, type); } initDecl->setType(allocType); initDecl->setInitializerType(initType); // Compute the interface type of the initializer. Type retInterfaceType = OptionalType::get(parentDC->getDeclaredInterfaceType()); Type interfaceType = FunctionType::get(interfaceArgType, retInterfaceType); Type selfInterfaceType = initDecl->computeInterfaceSelfType(/*init*/ false); Type selfInitializerInterfaceType = initDecl->computeInterfaceSelfType(/*init*/ true); Type allocIfaceType; Type initIfaceType; if (auto sig = parentDC->getGenericSignatureOfContext()) { allocIfaceType = GenericFunctionType::get(sig, selfInterfaceType, interfaceType, FunctionType::ExtInfo()); initIfaceType = GenericFunctionType::get(sig, selfInitializerInterfaceType, interfaceType, FunctionType::ExtInfo()); } else { allocIfaceType = FunctionType::get(selfMetatype, type); initIfaceType = FunctionType::get(selfType, type); } initDecl->setInterfaceType(allocIfaceType); initDecl->setInitializerInterfaceType(initIfaceType); initDecl->setAccessibility(enumDecl->getFormalAccess()); if (enumDecl->hasClangNode()) tc.implicitlyDefinedFunctions.push_back(initDecl); cast<IterableDeclContext>(parentDecl)->addMember(initDecl); return initDecl; }
bool TypeChecker::validateGenericFuncSignature(AbstractFunctionDecl *func) { bool invalid = false; // Create the archetype builder. ArchetypeBuilder builder = createArchetypeBuilder(func->getParentModule()); // Type check the function declaration, treating all generic type // parameters as dependent, unresolved. DependentGenericTypeResolver dependentResolver(builder); if (checkGenericFuncSignature(*this, &builder, func, dependentResolver)) invalid = true; // If this triggered a recursive validation, back out: we're done. // FIXME: This is an awful hack. if (func->hasType()) return !func->isInvalid(); // Finalize the generic requirements. (void)builder.finalize(func->getLoc()); // The archetype builder now has all of the requirements, although there might // still be errors that have not yet been diagnosed. Revert the generic // function signature and type-check it again, completely. revertGenericFuncSignature(func); CompleteGenericTypeResolver completeResolver(*this, builder); if (checkGenericFuncSignature(*this, nullptr, func, completeResolver)) invalid = true; // The generic function signature is complete and well-formed. Determine // the type of the generic function. // Collect the complete set of generic parameter types. SmallVector<GenericTypeParamType *, 4> allGenericParams; collectGenericParamTypes(func->getGenericParams(), func->getDeclContext(), allGenericParams); // Collect the requirements placed on the generic parameter types. SmallVector<Requirement, 4> requirements; collectRequirements(builder, allGenericParams, requirements); auto sig = GenericSignature::get(allGenericParams, requirements); // Debugging of the archetype builder and generic signature generation. if (sig && Context.LangOpts.DebugGenericSignatures) { func->dumpRef(llvm::errs()); llvm::errs() << "\n"; builder.dump(llvm::errs()); llvm::errs() << "Generic signature: "; sig->print(llvm::errs()); llvm::errs() << "\n"; llvm::errs() << "Canonical generic signature: "; sig->getCanonicalSignature()->print(llvm::errs()); llvm::errs() << "\n"; llvm::errs() << "Canonical generic signature for mangling: "; sig->getCanonicalManglingSignature(*func->getParentModule()) ->print(llvm::errs()); llvm::errs() << "\n"; } func->setGenericSignature(sig); if (invalid) { func->overwriteType(ErrorType::get(Context)); return true; } // Compute the function type. Type funcTy; Type initFuncTy; if (auto fn = dyn_cast<FuncDecl>(func)) { funcTy = fn->getBodyResultTypeLoc().getType(); if (!funcTy) { funcTy = TupleType::getEmpty(Context); } else { funcTy = getResultType(*this, fn, funcTy); } } else if (auto ctor = dyn_cast<ConstructorDecl>(func)) { // FIXME: shouldn't this just be // ctor->getDeclContext()->getDeclaredInterfaceType()? if (ctor->getDeclContext()->isProtocolOrProtocolExtensionContext()) { funcTy = ctor->getDeclContext()->getProtocolSelf()->getDeclaredType(); } else { funcTy = ctor->getExtensionType()->getAnyNominal() ->getDeclaredInterfaceType(); } // Adjust result type for failability. if (ctor->getFailability() != OTK_None) funcTy = OptionalType::get(ctor->getFailability(), funcTy); initFuncTy = funcTy; } else { assert(isa<DestructorDecl>(func)); funcTy = TupleType::getEmpty(Context); } auto patterns = func->getBodyParamPatterns(); SmallVector<Pattern *, 4> storedPatterns; // FIXME: Destructors don't have the '()' pattern in their signature, so // paste it here. if (isa<DestructorDecl>(func)) { storedPatterns.append(patterns.begin(), patterns.end()); Pattern *pattern = TuplePattern::create(Context, SourceLoc(), { }, SourceLoc(), /*Implicit=*/true); pattern->setType(TupleType::getEmpty(Context)); storedPatterns.push_back(pattern); patterns = storedPatterns; } bool hasSelf = func->getDeclContext()->isTypeContext(); for (unsigned i = 0, e = patterns.size(); i != e; ++i) { Type argTy; Type initArgTy; Type selfTy; if (i == e-1 && hasSelf) { selfTy = func->computeInterfaceSelfType(/*isInitializingCtor=*/false); // Substitute in our own 'self' parameter. argTy = selfTy; if (initFuncTy) { initArgTy = func->computeInterfaceSelfType(/*isInitializingCtor=*/true); } } else { argTy = patterns[e - i - 1]->getType(); // For an implicit declaration, our argument type will be in terms of // archetypes rather than dependent types. Replace the // archetypes with their corresponding dependent types. if (func->isImplicit()) { argTy = getInterfaceTypeFromInternalType(func, argTy); } if (initFuncTy) initArgTy = argTy; } auto info = applyFunctionTypeAttributes(func, i); // FIXME: We shouldn't even get here if the function isn't locally generic // to begin with, but fixing that requires a lot of reengineering for local // definitions in generic contexts. if (sig && i == e-1) { if (func->getGenericParams()) { // Collect all generic params referenced in parameter types, // return type or requirements. SmallPtrSet<GenericTypeParamDecl *, 4> referencedGenericParams; argTy.visit([&referencedGenericParams](Type t) { if (isa<GenericTypeParamType>(t.getCanonicalTypeOrNull())) { referencedGenericParams.insert( t->castTo<GenericTypeParamType>()->getDecl()); } }); funcTy.visit([&referencedGenericParams](Type t) { if (isa<GenericTypeParamType>(t.getCanonicalTypeOrNull())) { referencedGenericParams.insert( t->castTo<GenericTypeParamType>()->getDecl()); } }); auto requirements = sig->getRequirements(); for (auto req : requirements) { if (req.getKind() == RequirementKind::SameType) { // Same type requirements may allow for generic // inference, even if this generic parameter // is not mentioned in the function signature. // TODO: Make the test more precise. auto left = req.getFirstType(); auto right = req.getSecondType(); // For now consider any references inside requirements // as a possibility to infer the generic type. left.visit([&referencedGenericParams](Type t) { if (isa<GenericTypeParamType>(t.getCanonicalTypeOrNull())) { referencedGenericParams.insert( t->castTo<GenericTypeParamType>()->getDecl()); } }); right.visit([&referencedGenericParams](Type t) { if (isa<GenericTypeParamType>(t.getCanonicalTypeOrNull())) { referencedGenericParams.insert( t->castTo<GenericTypeParamType>()->getDecl()); } }); } } // Find the depth of the function's own generic parameters. unsigned fnGenericParamsDepth = func->getGenericParams()->getDepth(); // Check that every generic parameter type from the signature is // among referencedArchetypes. for (auto *genParam : sig->getGenericParams()) { auto *paramDecl = genParam->getDecl(); if (paramDecl->getDepth() != fnGenericParamsDepth) continue; if (!referencedGenericParams.count(paramDecl)) { // Produce an error that this generic parameter cannot be bound. diagnose(paramDecl->getLoc(), diag::unreferenced_generic_parameter, paramDecl->getNameStr()); func->setInvalid(); } } } funcTy = GenericFunctionType::get(sig, argTy, funcTy, info); if (initFuncTy) initFuncTy = GenericFunctionType::get(sig, initArgTy, initFuncTy, info); } else { funcTy = FunctionType::get(argTy, funcTy, info); if (initFuncTy) initFuncTy = FunctionType::get(initArgTy, initFuncTy, info); } } // Record the interface type. func->setInterfaceType(funcTy); if (initFuncTy) cast<ConstructorDecl>(func)->setInitializerInterfaceType(initFuncTy); return false; }
/// Create AST statements which convert from an enum to an Int with a switch. /// \p stmts The generated statements are appended to this vector. /// \p parentDC Either an extension or the enum itself. /// \p enumDecl The enum declaration. /// \p enumVarDecl The enum input variable. /// \p funcDecl The parent function. /// \p indexName The name of the output variable. /// \return A DeclRefExpr of the output variable (of type Int). static DeclRefExpr *convertEnumToIndex(SmallVectorImpl<ASTNode> &stmts, DeclContext *parentDC, EnumDecl *enumDecl, VarDecl *enumVarDecl, AbstractFunctionDecl *funcDecl, const char *indexName) { ASTContext &C = enumDecl->getASTContext(); Type enumType = enumVarDecl->getType(); Type intType = C.getIntDecl()->getDeclaredType(); auto indexVar = new (C) VarDecl(/*static*/false, /*let*/false, SourceLoc(), C.getIdentifier(indexName), intType, funcDecl); indexVar->setImplicit(); // generate: var indexVar Pattern *indexPat = new (C) NamedPattern(indexVar, /*implicit*/ true); indexPat->setType(intType); indexPat = new (C) TypedPattern(indexPat, TypeLoc::withoutLoc(intType)); indexPat->setType(intType); auto indexBind = PatternBindingDecl::create(C, SourceLoc(), StaticSpellingKind::None, SourceLoc(), indexPat, nullptr, funcDecl); unsigned index = 0; SmallVector<CaseStmt*, 4> cases; for (auto elt : enumDecl->getAllElements()) { // generate: case .<Case>: auto pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), SourceLoc(), SourceLoc(), Identifier(), elt, nullptr); pat->setImplicit(); auto labelItem = CaseLabelItem(/*IsDefault=*/false, pat, SourceLoc(), nullptr); // generate: indexVar = <index> llvm::SmallString<8> indexVal; APInt(32, index++).toString(indexVal, 10, /*signed*/ false); auto indexStr = C.AllocateCopy(indexVal); auto indexExpr = new (C) IntegerLiteralExpr(StringRef(indexStr.data(), indexStr.size()), SourceLoc(), /*implicit*/ true); auto indexRef = new (C) DeclRefExpr(indexVar, SourceLoc(), /*implicit*/true); auto assignExpr = new (C) AssignExpr(indexRef, SourceLoc(), indexExpr, /*implicit*/ true); auto body = BraceStmt::create(C, SourceLoc(), ASTNode(assignExpr), SourceLoc()); cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, /*HasBoundDecls=*/false, SourceLoc(), body)); } // generate: switch enumVar { } auto enumRef = new (C) DeclRefExpr(enumVarDecl, SourceLoc(), /*implicit*/true); auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), enumRef, SourceLoc(), cases, SourceLoc(), C); stmts.push_back(indexBind); stmts.push_back(switchStmt); return new (C) DeclRefExpr(indexVar, SourceLoc(), /*implicit*/ true, AccessSemantics::Ordinary, intType); }