Beispiel #1
0
/// 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);
}
Beispiel #2
0
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};
}
Beispiel #3
0
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};
}
Beispiel #4
0
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};
}
Beispiel #5
0
/// 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;
}
Beispiel #13
0
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);
}