| //===--- TypeCheckConstraints.cpp - Constraint-based Type Checking --------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file provides high-level entry points that use constraint |
| // systems for type checking, as well as a few miscellaneous helper |
| // functions that support the constraint system. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "MiscDiagnostics.h" |
| #include "TypeChecker.h" |
| #include "swift/AST/ASTVisitor.h" |
| #include "swift/AST/ASTWalker.h" |
| #include "swift/AST/DiagnosticSuppression.h" |
| #include "swift/AST/ExistentialLayout.h" |
| #include "swift/AST/Initializer.h" |
| #include "swift/AST/PrettyStackTrace.h" |
| #include "swift/AST/SubstitutionMap.h" |
| #include "swift/AST/TypeCheckRequests.h" |
| #include "swift/Basic/Statistic.h" |
| #include "swift/Sema/CodeCompletionTypeChecking.h" |
| #include "swift/Sema/ConstraintSystem.h" |
| #include "swift/Sema/SolutionResult.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/SmallPtrSet.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/Support/Allocator.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include "llvm/Support/SaveAndRestore.h" |
| #include "llvm/Support/Format.h" |
| #include <iterator> |
| #include <map> |
| #include <memory> |
| #include <utility> |
| #include <tuple> |
| |
| using namespace swift; |
| using namespace constraints; |
| |
| //===----------------------------------------------------------------------===// |
| // Type variable implementation. |
| //===----------------------------------------------------------------------===// |
| #pragma mark Type variable implementation |
| |
| void TypeVariableType::Implementation::print(llvm::raw_ostream &OS) { |
| getTypeVariable()->print(OS, PrintOptions()); |
| } |
| |
| SavedTypeVariableBinding::SavedTypeVariableBinding(TypeVariableType *typeVar) |
| : TypeVar(typeVar), Options(typeVar->getImpl().getRawOptions()), |
| ParentOrFixed(typeVar->getImpl().ParentOrFixed) { } |
| |
| void SavedTypeVariableBinding::restore() { |
| TypeVar->getImpl().setRawOptions(Options); |
| TypeVar->getImpl().ParentOrFixed = ParentOrFixed; |
| } |
| |
| GenericTypeParamType * |
| TypeVariableType::Implementation::getGenericParameter() const { |
| return locator ? locator->getGenericParameter() : nullptr; |
| } |
| |
| Optional<ExprKind> |
| TypeVariableType::Implementation::getAtomicLiteralKind() const { |
| if (!locator || !locator->directlyAt<LiteralExpr>()) |
| return None; |
| |
| auto kind = getAsExpr(locator->getAnchor())->getKind(); |
| switch (kind) { |
| case ExprKind::IntegerLiteral: |
| case ExprKind::FloatLiteral: |
| case ExprKind::StringLiteral: |
| case ExprKind::BooleanLiteral: |
| case ExprKind::NilLiteral: |
| return kind; |
| default: |
| return None; |
| } |
| } |
| |
| bool TypeVariableType::Implementation::isClosureType() const { |
| if (!(locator && locator->getAnchor())) |
| return false; |
| |
| return isExpr<ClosureExpr>(locator->getAnchor()) && locator->getPath().empty(); |
| } |
| |
| bool TypeVariableType::Implementation::isClosureParameterType() const { |
| if (!(locator && locator->getAnchor())) |
| return false; |
| |
| return isExpr<ClosureExpr>(locator->getAnchor()) && |
| locator->isLastElement<LocatorPathElt::TupleElement>(); |
| } |
| |
| bool TypeVariableType::Implementation::isClosureResultType() const { |
| if (!(locator && locator->getAnchor())) |
| return false; |
| |
| return isExpr<ClosureExpr>(locator->getAnchor()) && |
| locator->isLastElement<LocatorPathElt::ClosureResult>(); |
| } |
| |
| void *operator new(size_t bytes, ConstraintSystem& cs, |
| size_t alignment) { |
| return cs.getAllocator().Allocate(bytes, alignment); |
| } |
| |
| bool constraints::computeTupleShuffle(ArrayRef<TupleTypeElt> fromTuple, |
| ArrayRef<TupleTypeElt> toTuple, |
| SmallVectorImpl<unsigned> &sources) { |
| const unsigned unassigned = -1; |
| |
| SmallVector<bool, 4> consumed(fromTuple.size(), false); |
| sources.clear(); |
| sources.assign(toTuple.size(), unassigned); |
| |
| // Match up any named elements. |
| for (unsigned i = 0, n = toTuple.size(); i != n; ++i) { |
| const auto &toElt = toTuple[i]; |
| |
| // Skip unnamed elements. |
| if (!toElt.hasName()) |
| continue; |
| |
| // Find the corresponding named element. |
| int matched = -1; |
| { |
| int index = 0; |
| for (auto field : fromTuple) { |
| if (field.getName() == toElt.getName() && !consumed[index]) { |
| matched = index; |
| break; |
| } |
| ++index; |
| } |
| } |
| if (matched == -1) |
| continue; |
| |
| // Record this match. |
| sources[i] = matched; |
| consumed[matched] = true; |
| } |
| |
| // Resolve any unmatched elements. |
| unsigned fromNext = 0, fromLast = fromTuple.size(); |
| auto skipToNextAvailableInput = [&] { |
| while (fromNext != fromLast && consumed[fromNext]) |
| ++fromNext; |
| }; |
| skipToNextAvailableInput(); |
| |
| for (unsigned i = 0, n = toTuple.size(); i != n; ++i) { |
| // Check whether we already found a value for this element. |
| if (sources[i] != unassigned) |
| continue; |
| |
| // If there aren't any more inputs, we are done. |
| if (fromNext == fromLast) { |
| return true; |
| } |
| |
| // Otherwise, assign this input to the next output element. |
| const auto &elt2 = toTuple[i]; |
| assert(!elt2.isVararg()); |
| |
| // Fail if the input element is named and we're trying to match it with |
| // something with a different label. |
| if (fromTuple[fromNext].hasName() && elt2.hasName()) |
| return true; |
| |
| sources[i] = fromNext; |
| consumed[fromNext] = true; |
| skipToNextAvailableInput(); |
| } |
| |
| // Complain if we didn't reach the end of the inputs. |
| if (fromNext != fromLast) { |
| return true; |
| } |
| |
| // If we got here, we should have claimed all the arguments. |
| assert(std::find(consumed.begin(), consumed.end(), false) == consumed.end()); |
| return false; |
| } |
| |
| Expr *ConstraintLocatorBuilder::trySimplifyToExpr() const { |
| SmallVector<LocatorPathElt, 4> pathBuffer; |
| auto anchor = getLocatorParts(pathBuffer); |
| // Locators are not guaranteed to have an anchor |
| // if constraint system is used to verify generic |
| // requirements. |
| if (!anchor.is<Expr *>()) |
| return nullptr; |
| |
| ArrayRef<LocatorPathElt> path = pathBuffer; |
| |
| SourceRange range; |
| simplifyLocator(anchor, path, range); |
| return (path.empty() ? getAsExpr(anchor) : nullptr); |
| } |
| |
| void ParentConditionalConformance::diagnoseConformanceStack( |
| DiagnosticEngine &diags, SourceLoc loc, |
| ArrayRef<ParentConditionalConformance> conformances) { |
| for (auto history : llvm::reverse(conformances)) { |
| diags.diagnose(loc, diag::requirement_implied_by_conditional_conformance, |
| history.ConformingType, history.Protocol); |
| } |
| } |
| |
| namespace { |
| /// Produce any additional syntactic diagnostics for the body of a function |
| /// that had a result builder applied. |
| class FunctionSyntacticDiagnosticWalker : public ASTWalker { |
| SmallVector<DeclContext *, 4> dcStack; |
| |
| public: |
| FunctionSyntacticDiagnosticWalker(DeclContext *dc) { dcStack.push_back(dc); } |
| |
| std::pair<bool, Expr *> walkToExprPre(Expr *expr) override { |
| performSyntacticExprDiagnostics(expr, dcStack.back(), /*isExprStmt=*/false); |
| |
| if (auto closure = dyn_cast<ClosureExpr>(expr)) { |
| if (closure->isSeparatelyTypeChecked()) { |
| dcStack.push_back(closure); |
| return {true, expr}; |
| } |
| } |
| |
| return {false, expr}; |
| } |
| |
| Expr *walkToExprPost(Expr *expr) override { |
| if (auto closure = dyn_cast<ClosureExpr>(expr)) { |
| if (closure->isSeparatelyTypeChecked()) { |
| assert(dcStack.back() == closure); |
| dcStack.pop_back(); |
| } |
| } |
| |
| return expr; |
| } |
| |
| std::pair<bool, Stmt *> walkToStmtPre(Stmt *stmt) override { |
| performStmtDiagnostics(stmt, dcStack.back()); |
| return {true, stmt}; |
| } |
| |
| std::pair<bool, Pattern *> walkToPatternPre(Pattern *pattern) override { |
| return {false, pattern}; |
| } |
| |
| bool walkToTypeReprPre(TypeRepr *typeRepr) override { return false; } |
| bool walkToParameterListPre(ParameterList *params) override { return false; } |
| }; |
| } // end anonymous namespace |
| |
| void constraints::performSyntacticDiagnosticsForTarget( |
| const SolutionApplicationTarget &target, bool isExprStmt) { |
| auto *dc = target.getDeclContext(); |
| switch (target.kind) { |
| case SolutionApplicationTarget::Kind::expression: { |
| // First emit diagnostics for the main expression. |
| performSyntacticExprDiagnostics(target.getAsExpr(), dc, isExprStmt); |
| |
| // If this is a for-in statement, we also need to check the where clause if |
| // present. |
| if (target.isForEachStmt()) { |
| if (auto *whereExpr = target.getForEachStmtInfo().whereExpr) |
| performSyntacticExprDiagnostics(whereExpr, dc, /*isExprStmt*/ false); |
| } |
| return; |
| } |
| case SolutionApplicationTarget::Kind::function: { |
| FunctionSyntacticDiagnosticWalker walker(dc); |
| target.getFunctionBody()->walk(walker); |
| return; |
| } |
| case SolutionApplicationTarget::Kind::stmtCondition: |
| case SolutionApplicationTarget::Kind::caseLabelItem: |
| case SolutionApplicationTarget::Kind::patternBinding: |
| case SolutionApplicationTarget::Kind::uninitializedWrappedVar: |
| // Nothing to do for these. |
| return; |
| } |
| llvm_unreachable("Unhandled case in switch!"); |
| } |
| |
| #pragma mark High-level entry points |
| Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, |
| ContextualTypeInfo contextualInfo, |
| TypeCheckExprOptions options) { |
| SolutionApplicationTarget target( |
| expr, dc, contextualInfo.purpose, contextualInfo.getType(), |
| options.contains(TypeCheckExprFlags::IsDiscarded)); |
| auto resultTarget = typeCheckExpression(target, options); |
| if (!resultTarget) { |
| expr = target.getAsExpr(); |
| return Type(); |
| } |
| |
| expr = resultTarget->getAsExpr(); |
| return expr->getType(); |
| } |
| |
| Optional<SolutionApplicationTarget> |
| TypeChecker::typeCheckExpression( |
| SolutionApplicationTarget &target, |
| TypeCheckExprOptions options) { |
| Expr *expr = target.getAsExpr(); |
| DeclContext *dc = target.getDeclContext(); |
| auto &Context = dc->getASTContext(); |
| FrontendStatsTracer StatsTracer(Context.Stats, |
| "typecheck-expr", expr); |
| PrettyStackTraceExpr stackTrace(Context, "type-checking", expr); |
| |
| // First, pre-check the expression, validating any types that occur in the |
| // expression and folding sequence expressions. |
| if (ConstraintSystem::preCheckExpression( |
| expr, dc, /*replaceInvalidRefsWithErrors=*/true)) { |
| target.setExpr(expr); |
| return None; |
| } |
| target.setExpr(expr); |
| |
| // Check whether given expression has a code completion token which requires |
| // special handling. |
| if (Context.CompletionCallback && |
| typeCheckForCodeCompletion(target, /*needsPrecheck*/false, |
| [&](const constraints::Solution &S) { |
| Context.CompletionCallback->sawSolution(S); |
| })) |
| return None; |
| |
| // Construct a constraint system from this expression. |
| ConstraintSystemOptions csOptions = ConstraintSystemFlags::AllowFixes; |
| |
| if (DiagnosticSuppression::isEnabled(Context.Diags)) |
| csOptions |= ConstraintSystemFlags::SuppressDiagnostics; |
| |
| if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables)) |
| csOptions |= ConstraintSystemFlags::AllowUnresolvedTypeVariables; |
| |
| if (options.contains(TypeCheckExprFlags::LeaveClosureBodyUnchecked)) |
| csOptions |= ConstraintSystemFlags::LeaveClosureBodyUnchecked; |
| |
| ConstraintSystem cs(dc, csOptions); |
| |
| // Tell the constraint system what the contextual type is. This informs |
| // diagnostics and is a hint for various performance optimizations. |
| cs.setContextualType( |
| expr, |
| target.getExprContextualTypeLoc(), |
| target.getExprContextualTypePurpose()); |
| |
| // Try to shrink the system by reducing disjunction domains. This |
| // goes through every sub-expression and generate its own sub-system, to |
| // try to reduce the domains of those subexpressions. |
| cs.shrink(expr); |
| target.setExpr(expr); |
| |
| // If the client can handle unresolved type variables, leave them in the |
| // system. |
| auto allowFreeTypeVariables = FreeTypeVariableBinding::Disallow; |
| if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables)) |
| allowFreeTypeVariables = FreeTypeVariableBinding::UnresolvedType; |
| |
| // Attempt to solve the constraint system. |
| auto viable = cs.solve(target, allowFreeTypeVariables); |
| if (!viable) { |
| target.setExpr(expr); |
| return None; |
| } |
| |
| // If the client allows the solution to have unresolved type expressions, |
| // check for them now. We cannot apply the solution with unresolved TypeVars, |
| // because they will leak out into arbitrary places in the resultant AST. |
| if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables) && |
| (viable->size() != 1 || |
| (target.getExprConversionType() && |
| target.getExprConversionType()->hasUnresolvedType()))) { |
| return target; |
| } |
| |
| // Apply this solution to the constraint system. |
| // FIXME: This shouldn't be necessary. |
| auto &solution = (*viable)[0]; |
| cs.applySolution(solution); |
| |
| // Apply the solution to the expression. |
| auto resultTarget = cs.applySolution(solution, target); |
| if (!resultTarget) { |
| // Failure already diagnosed, above, as part of applying the solution. |
| return None; |
| } |
| Expr *result = resultTarget->getAsExpr(); |
| |
| // Unless the client has disabled them, perform syntactic checks on the |
| // expression now. |
| if (!cs.shouldSuppressDiagnostics()) { |
| bool isExprStmt = options.contains(TypeCheckExprFlags::IsExprStmt); |
| performSyntacticDiagnosticsForTarget(*resultTarget, isExprStmt); |
| } |
| |
| resultTarget->setExpr(result); |
| return *resultTarget; |
| } |
| |
| Type TypeChecker::typeCheckParameterDefault(Expr *&defaultValue, |
| DeclContext *DC, Type paramType, |
| bool isAutoClosure) { |
| assert(paramType && !paramType->hasError()); |
| return typeCheckExpression(defaultValue, DC, /*contextualInfo=*/ |
| {paramType, isAutoClosure |
| ? CTP_AutoclosureDefaultParameter |
| : CTP_DefaultParameter}); |
| } |
| |
| bool TypeChecker::typeCheckBinding( |
| Pattern *&pattern, Expr *&initializer, DeclContext *DC, |
| Type patternType, PatternBindingDecl *PBD, unsigned patternNumber) { |
| SolutionApplicationTarget target = |
| PBD ? SolutionApplicationTarget::forInitialization( |
| initializer, DC, patternType, PBD, patternNumber, |
| /*bindPatternVarsOneWay=*/false) |
| : SolutionApplicationTarget::forInitialization( |
| initializer, DC, patternType, pattern, |
| /*bindPatternVarsOneWay=*/false); |
| |
| // Type-check the initializer. |
| auto resultTarget = typeCheckExpression(target); |
| |
| if (resultTarget) { |
| initializer = resultTarget->getAsExpr(); |
| pattern = resultTarget->getInitializationPattern(); |
| return false; |
| } |
| |
| auto &Context = DC->getASTContext(); |
| initializer = target.getAsExpr(); |
| |
| if (!initializer->getType()) |
| initializer->setType(ErrorType::get(Context)); |
| |
| // Assign error types to the pattern and its variables, to prevent it from |
| // being referenced by the constraint system. |
| if (patternType->hasUnresolvedType() || |
| patternType->hasUnboundGenericType()) { |
| pattern->setType(ErrorType::get(Context)); |
| } |
| |
| pattern->forEachVariable([&](VarDecl *var) { |
| // Don't change the type of a variable that we've been able to |
| // compute a type for. |
| if (var->hasInterfaceType() && |
| !var->getType()->hasUnboundGenericType() && |
| !var->isInvalid()) |
| return; |
| |
| var->setInvalid(); |
| }); |
| return true; |
| } |
| |
| bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, |
| unsigned patternNumber, |
| Type patternType) { |
| Pattern *pattern = PBD->getPattern(patternNumber); |
| Expr *init = PBD->getInit(patternNumber); |
| |
| // Enter an initializer context if necessary. |
| PatternBindingInitializer *initContext = nullptr; |
| DeclContext *DC = PBD->getDeclContext(); |
| if (!DC->isLocalContext()) { |
| initContext = cast_or_null<PatternBindingInitializer>( |
| PBD->getInitContext(patternNumber)); |
| if (initContext) |
| DC = initContext; |
| } |
| |
| // If we weren't given a pattern type, compute one now. |
| if (!patternType) { |
| if (pattern->hasType()) |
| patternType = pattern->getType(); |
| else { |
| auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); |
| patternType = typeCheckPattern(contextualPattern); |
| } |
| |
| if (patternType->hasError()) { |
| PBD->setInvalid(); |
| return true; |
| } |
| } |
| |
| bool hadError = TypeChecker::typeCheckBinding( |
| pattern, init, DC, patternType, PBD, patternNumber); |
| if (!init) { |
| PBD->setInvalid(); |
| return true; |
| } |
| |
| PBD->setPattern(patternNumber, pattern, initContext); |
| PBD->setInit(patternNumber, init); |
| |
| // Bind a property with an opaque return type to the underlying type |
| // given by the initializer. |
| if (auto var = pattern->getSingleVar()) { |
| if (auto opaque = var->getOpaqueResultTypeDecl()) { |
| if (auto convertedInit = dyn_cast<UnderlyingToOpaqueExpr>(init)) { |
| auto underlyingType = convertedInit->getSubExpr()->getType() |
| ->mapTypeOutOfContext(); |
| auto underlyingSubs = SubstitutionMap::get( |
| opaque->getOpaqueInterfaceGenericSignature(), |
| [&](SubstitutableType *t) -> Type { |
| if (t->isEqual(opaque->getUnderlyingInterfaceType())) { |
| return underlyingType; |
| } |
| return Type(t); |
| }, |
| LookUpConformanceInModule(opaque->getModuleContext())); |
| |
| opaque->setUnderlyingTypeSubstitutions(underlyingSubs); |
| } else { |
| var->diagnose(diag::opaque_type_var_no_underlying_type); |
| } |
| } |
| } |
| |
| if (hadError) |
| PBD->setInvalid(); |
| |
| PBD->setInitializerChecked(patternNumber); |
| |
| return hadError; |
| } |
| |
| bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { |
| auto &Context = dc->getASTContext(); |
| |
| auto failed = [&]() -> bool { |
| // Invalidate the pattern and the var decl. |
| stmt->getPattern()->setType(ErrorType::get(Context)); |
| stmt->getPattern()->forEachVariable([&](VarDecl *var) { |
| if (var->hasInterfaceType() && !var->isInvalid()) |
| return; |
| var->setInvalid(); |
| }); |
| return true; |
| }; |
| |
| auto sequenceProto = TypeChecker::getProtocol( |
| dc->getASTContext(), stmt->getForLoc(), KnownProtocolKind::Sequence); |
| if (!sequenceProto) |
| return failed(); |
| |
| // Precheck the sequence. |
| Expr *sequence = stmt->getSequence(); |
| if (ConstraintSystem::preCheckExpression( |
| sequence, dc, /*replaceInvalidRefsWithErrors=*/true)) |
| return failed(); |
| stmt->setSequence(sequence); |
| |
| // Precheck the filtering condition. |
| if (Expr *whereExpr = stmt->getWhere()) { |
| if (ConstraintSystem::preCheckExpression( |
| whereExpr, dc, /*replaceInvalidRefsWithErrors=*/true)) |
| return failed(); |
| |
| stmt->setWhere(whereExpr); |
| } |
| |
| auto target = SolutionApplicationTarget::forForEachStmt( |
| stmt, sequenceProto, dc, /*bindPatternVarsOneWay=*/false); |
| if (!typeCheckExpression(target)) |
| return failed(); |
| |
| return false; |
| } |
| |
| bool TypeChecker::typeCheckCondition(Expr *&expr, DeclContext *dc) { |
| // If this expression is already typechecked and has type Bool, then just |
| // re-typecheck it. |
| if (expr->getType() && expr->getType()->isBool()) { |
| auto resultTy = |
| TypeChecker::typeCheckExpression(expr, dc); |
| return !resultTy; |
| } |
| |
| auto *boolDecl = dc->getASTContext().getBoolDecl(); |
| if (!boolDecl) |
| return true; |
| |
| auto resultTy = TypeChecker::typeCheckExpression( |
| expr, dc, |
| /*contextualInfo=*/{boolDecl->getDeclaredInterfaceType(), CTP_Condition}); |
| return !resultTy; |
| } |
| |
| /// Find the '~=` operator that can compare an expression inside a pattern to a |
| /// value of a given type. |
| bool TypeChecker::typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, |
| Type rhsType) { |
| auto &Context = DC->getASTContext(); |
| FrontendStatsTracer StatsTracer(Context.Stats, |
| "typecheck-expr-pattern", EP); |
| PrettyStackTracePattern stackTrace(Context, "type-checking", EP); |
| |
| // Create a 'let' binding to stand in for the RHS value. |
| auto *matchVar = new (Context) VarDecl(/*IsStatic*/false, |
| VarDecl::Introducer::Let, |
| EP->getLoc(), |
| Context.getIdentifier("$match"), |
| DC); |
| matchVar->setInterfaceType(rhsType->mapTypeOutOfContext()); |
| |
| matchVar->setImplicit(); |
| EP->setMatchVar(matchVar); |
| |
| // Find '~=' operators for the match. |
| auto matchLookup = |
| lookupUnqualified(DC->getModuleScopeContext(), |
| DeclNameRef(Context.Id_MatchOperator), |
| SourceLoc(), defaultUnqualifiedLookupOptions); |
| auto &diags = DC->getASTContext().Diags; |
| if (!matchLookup) { |
| diags.diagnose(EP->getLoc(), diag::no_match_operator); |
| return true; |
| } |
| |
| SmallVector<ValueDecl*, 4> choices; |
| for (auto &result : matchLookup) { |
| choices.push_back(result.getValueDecl()); |
| } |
| |
| if (choices.empty()) { |
| diags.diagnose(EP->getLoc(), diag::no_match_operator); |
| return true; |
| } |
| |
| // Build the 'expr ~= var' expression. |
| // FIXME: Compound name locations. |
| auto *matchOp = |
| TypeChecker::buildRefExpr(choices, DC, DeclNameLoc(EP->getLoc()), |
| /*Implicit=*/true, FunctionRefKind::Compound); |
| auto *matchVarRef = new (Context) DeclRefExpr(matchVar, |
| DeclNameLoc(EP->getLoc()), |
| /*Implicit=*/true); |
| |
| Expr *matchArgElts[] = {EP->getSubExpr(), matchVarRef}; |
| auto *matchArgs |
| = TupleExpr::create(Context, EP->getSubExpr()->getSourceRange().Start, |
| matchArgElts, { }, { }, |
| EP->getSubExpr()->getSourceRange().End, |
| /*HasTrailingClosure=*/false, /*Implicit=*/true); |
| |
| Expr *matchCall = new (Context) BinaryExpr(matchOp, matchArgs, |
| /*Implicit=*/true); |
| |
| // Check the expression as a condition. |
| bool hadError = typeCheckCondition(matchCall, DC); |
| // Save the type-checked expression in the pattern. |
| EP->setMatchExpr(matchCall); |
| // Set the type on the pattern. |
| EP->setType(rhsType); |
| return hadError; |
| } |
| |
| static Type replaceArchetypesWithTypeVariables(ConstraintSystem &cs, |
| Type t) { |
| llvm::DenseMap<SubstitutableType *, TypeVariableType *> types; |
| |
| return t.subst( |
| [&](SubstitutableType *origType) -> Type { |
| auto found = types.find(origType); |
| if (found != types.end()) |
| return found->second; |
| |
| if (auto archetypeType = dyn_cast<ArchetypeType>(origType)) { |
| auto root = archetypeType->getRoot(); |
| // We leave opaque types and their nested associated types alone here. |
| // They're globally available. |
| if (isa<OpaqueTypeArchetypeType>(root)) |
| return origType; |
| // For other nested types, fail here so the default logic in subst() |
| // for nested types applies. |
| else if (root != archetypeType) |
| return Type(); |
| |
| auto locator = cs.getConstraintLocator({}); |
| auto replacement = cs.createTypeVariable(locator, |
| TVO_CanBindToNoEscape); |
| |
| if (auto superclass = archetypeType->getSuperclass()) { |
| cs.addConstraint(ConstraintKind::Subtype, replacement, |
| superclass, locator); |
| } |
| for (auto proto : archetypeType->getConformsTo()) { |
| cs.addConstraint(ConstraintKind::ConformsTo, replacement, |
| proto->getDeclaredInterfaceType(), locator); |
| } |
| types[origType] = replacement; |
| return replacement; |
| } |
| |
| // FIXME: Remove this case |
| assert(cast<GenericTypeParamType>(origType)); |
| auto locator = cs.getConstraintLocator({}); |
| auto replacement = cs.createTypeVariable(locator, |
| TVO_CanBindToNoEscape); |
| types[origType] = replacement; |
| return replacement; |
| }, |
| MakeAbstractConformanceForGenericType()); |
| } |
| |
| bool TypeChecker::typesSatisfyConstraint(Type type1, Type type2, |
| bool openArchetypes, |
| ConstraintKind kind, DeclContext *dc, |
| bool *unwrappedIUO) { |
| assert(!type1->hasTypeVariable() && !type2->hasTypeVariable() && |
| "Unexpected type variable in constraint satisfaction testing"); |
| |
| ConstraintSystem cs(dc, ConstraintSystemOptions()); |
| if (openArchetypes) { |
| type1 = replaceArchetypesWithTypeVariables(cs, type1); |
| type2 = replaceArchetypesWithTypeVariables(cs, type2); |
| } |
| |
| cs.addConstraint(kind, type1, type2, cs.getConstraintLocator({})); |
| |
| if (openArchetypes) { |
| assert(!unwrappedIUO && "FIXME"); |
| SmallVector<Solution, 4> solutions; |
| return !cs.solve(solutions, FreeTypeVariableBinding::Allow); |
| } |
| |
| if (auto solution = cs.solveSingle()) { |
| if (unwrappedIUO) |
| *unwrappedIUO = solution->getFixedScore().Data[SK_ForceUnchecked] > 0; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool TypeChecker::isSubtypeOf(Type type1, Type type2, DeclContext *dc) { |
| return typesSatisfyConstraint(type1, type2, |
| /*openArchetypes=*/false, |
| ConstraintKind::Subtype, dc); |
| } |
| |
| bool TypeChecker::isConvertibleTo(Type type1, Type type2, DeclContext *dc, |
| bool *unwrappedIUO) { |
| return typesSatisfyConstraint(type1, type2, |
| /*openArchetypes=*/false, |
| ConstraintKind::Conversion, dc, |
| unwrappedIUO); |
| } |
| |
| bool TypeChecker::isExplicitlyConvertibleTo(Type type1, Type type2, |
| DeclContext *dc) { |
| return (typesSatisfyConstraint(type1, type2, |
| /*openArchetypes=*/false, |
| ConstraintKind::Conversion, dc) || |
| isObjCBridgedTo(type1, type2, dc)); |
| } |
| |
| bool TypeChecker::isObjCBridgedTo(Type type1, Type type2, DeclContext *dc, |
| bool *unwrappedIUO) { |
| return (typesSatisfyConstraint(type1, type2, |
| /*openArchetypes=*/false, |
| ConstraintKind::BridgingConversion, |
| dc, unwrappedIUO)); |
| } |
| |
| bool TypeChecker::checkedCastMaySucceed(Type t1, Type t2, DeclContext *dc) { |
| auto kind = TypeChecker::typeCheckCheckedCast(t1, t2, |
| CheckedCastContextKind::None, dc, |
| SourceLoc(), nullptr, SourceRange()); |
| return (kind != CheckedCastKind::Unresolved); |
| } |
| |
| Expr * |
| TypeChecker::addImplicitLoadExpr(ASTContext &Context, Expr *expr, |
| std::function<Type(Expr *)> getType, |
| std::function<void(Expr *, Type)> setType) { |
| class LoadAdder : public ASTWalker { |
| private: |
| using GetTypeFn = std::function<Type(Expr *)>; |
| using SetTypeFn = std::function<void(Expr *, Type)>; |
| |
| ASTContext &Ctx; |
| GetTypeFn getType; |
| SetTypeFn setType; |
| |
| public: |
| LoadAdder(ASTContext &ctx, GetTypeFn getType, SetTypeFn setType) |
| : Ctx(ctx), getType(getType), setType(setType) {} |
| |
| std::pair<bool, Expr *> walkToExprPre(Expr *E) override { |
| if (isa<ParenExpr>(E) || isa<ForceValueExpr>(E)) |
| return { true, E }; |
| |
| // Since load expression is created by walker, |
| // it's safe to stop as soon as it encounters first one |
| // because it would be the one it just created. |
| if (isa<LoadExpr>(E)) |
| return { false, nullptr }; |
| |
| return { false, createLoadExpr(E) }; |
| } |
| |
| Expr *walkToExprPost(Expr *E) override { |
| if (auto *FVE = dyn_cast<ForceValueExpr>(E)) |
| setType(E, getType(FVE->getSubExpr())->getOptionalObjectType()); |
| |
| if (auto *PE = dyn_cast<ParenExpr>(E)) |
| setType(E, ParenType::get(Ctx, getType(PE->getSubExpr()))); |
| |
| return E; |
| } |
| |
| private: |
| LoadExpr *createLoadExpr(Expr *E) { |
| auto objectType = getType(E)->getRValueType(); |
| auto *LE = new (Ctx) LoadExpr(E, objectType); |
| setType(LE, objectType); |
| return LE; |
| } |
| }; |
| |
| return expr->walk(LoadAdder(Context, getType, setType)); |
| } |
| |
| Expr * |
| TypeChecker::coerceToRValue(ASTContext &Context, Expr *expr, |
| llvm::function_ref<Type(Expr *)> getType, |
| llvm::function_ref<void(Expr *, Type)> setType) { |
| Type exprTy = getType(expr); |
| |
| // If expr has no type, just assume it's the right expr. |
| if (!exprTy) |
| return expr; |
| |
| // If the type is already materializable, then we're already done. |
| if (!exprTy->hasLValueType()) |
| return expr; |
| |
| // Walk into force optionals and coerce the source. |
| if (auto *FVE = dyn_cast<ForceValueExpr>(expr)) { |
| auto sub = coerceToRValue(Context, FVE->getSubExpr(), getType, setType); |
| FVE->setSubExpr(sub); |
| setType(FVE, getType(sub)->getOptionalObjectType()); |
| return FVE; |
| } |
| |
| // Walk into parenthesized expressions to update the subexpression. |
| if (auto paren = dyn_cast<IdentityExpr>(expr)) { |
| auto sub = coerceToRValue(Context, paren->getSubExpr(), getType, setType); |
| paren->setSubExpr(sub); |
| setType(paren, ParenType::get(Context, getType(sub))); |
| return paren; |
| } |
| |
| // Walk into 'try' and 'try!' expressions to update the subexpression. |
| if (auto tryExpr = dyn_cast<AnyTryExpr>(expr)) { |
| auto sub = coerceToRValue(Context, tryExpr->getSubExpr(), getType, setType); |
| tryExpr->setSubExpr(sub); |
| if (isa<OptionalTryExpr>(tryExpr) && !getType(sub)->hasError()) |
| setType(tryExpr, OptionalType::get(getType(sub))); |
| else |
| setType(tryExpr, getType(sub)); |
| return tryExpr; |
| } |
| |
| // Walk into tuples to update the subexpressions. |
| if (auto tuple = dyn_cast<TupleExpr>(expr)) { |
| bool anyChanged = false; |
| for (auto &elt : tuple->getElements()) { |
| // Materialize the element. |
| auto oldType = getType(elt); |
| elt = coerceToRValue(Context, elt, getType, setType); |
| |
| // If the type changed at all, make a note of it. |
| if (getType(elt).getPointer() != oldType.getPointer()) { |
| anyChanged = true; |
| } |
| } |
| |
| // If any of the types changed, rebuild the tuple type. |
| if (anyChanged) { |
| SmallVector<TupleTypeElt, 4> elements; |
| elements.reserve(tuple->getElements().size()); |
| for (unsigned i = 0, n = tuple->getNumElements(); i != n; ++i) { |
| Type type = getType(tuple->getElement(i)); |
| Identifier name = tuple->getElementName(i); |
| elements.push_back(TupleTypeElt(type, name)); |
| } |
| setType(tuple, TupleType::get(elements, Context)); |
| } |
| |
| return tuple; |
| } |
| |
| // Load lvalues. |
| if (exprTy->is<LValueType>()) |
| return addImplicitLoadExpr(Context, expr, getType, setType); |
| |
| // Nothing to do. |
| return expr; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Debugging |
| //===----------------------------------------------------------------------===// |
| #pragma mark Debugging |
| |
| void Solution::dump() const { |
| dump(llvm::errs()); |
| } |
| |
| void Solution::dump(raw_ostream &out) const { |
| PrintOptions PO; |
| PO.PrintTypesForDebugging = true; |
| |
| SourceManager *sm = &getConstraintSystem().getASTContext().SourceMgr; |
| |
| out << "Fixed score: " << FixedScore << "\n"; |
| |
| out << "Type variables:\n"; |
| for (auto binding : typeBindings) { |
| auto &typeVar = binding.first; |
| out.indent(2); |
| Type(typeVar).print(out, PO); |
| out << " as "; |
| binding.second.print(out, PO); |
| if (auto *locator = typeVar->getImpl().getLocator()) { |
| out << " @ "; |
| locator->dump(sm, out); |
| } |
| out << "\n"; |
| } |
| |
| out << "\n"; |
| out << "Overload choices:\n"; |
| for (auto ovl : overloadChoices) { |
| out.indent(2); |
| if (ovl.first) |
| ovl.first->dump(sm, out); |
| out << " with "; |
| |
| auto choice = ovl.second.choice; |
| switch (choice.getKind()) { |
| case OverloadChoiceKind::Decl: |
| case OverloadChoiceKind::DeclViaDynamic: |
| case OverloadChoiceKind::DeclViaBridge: |
| case OverloadChoiceKind::DeclViaUnwrappedOptional: |
| choice.getDecl()->dumpRef(out); |
| out << " as "; |
| if (choice.getBaseType()) |
| out << choice.getBaseType()->getString(PO) << "."; |
| |
| out << choice.getDecl()->getBaseName() << ": " |
| << ovl.second.openedType->getString(PO) << "\n"; |
| break; |
| |
| case OverloadChoiceKind::KeyPathApplication: |
| out << "key path application root " |
| << choice.getBaseType()->getString(PO) << "\n"; |
| break; |
| |
| case OverloadChoiceKind::DynamicMemberLookup: |
| case OverloadChoiceKind::KeyPathDynamicMemberLookup: |
| out << "dynamic member lookup root " |
| << choice.getBaseType()->getString(PO) |
| << " name='" << choice.getName() << "'\n"; |
| break; |
| |
| case OverloadChoiceKind::TupleIndex: |
| out << "tuple " << choice.getBaseType()->getString(PO) << " index " |
| << choice.getTupleIndex() << "\n"; |
| break; |
| } |
| out << "\n"; |
| } |
| |
| out << "\n"; |
| out << "Constraint restrictions:\n"; |
| for (auto &restriction : ConstraintRestrictions) { |
| out.indent(2) << restriction.first.first |
| << " to " << restriction.first.second |
| << " is " << getName(restriction.second) << "\n"; |
| } |
| |
| out << "\n"; |
| out << "Trailing closure matching:\n"; |
| for (auto &trailingClosureMatching : trailingClosureMatchingChoices) { |
| out.indent(2); |
| trailingClosureMatching.first->dump(sm, out); |
| switch (trailingClosureMatching.second) { |
| case TrailingClosureMatching::Forward: |
| out << ": forward\n"; |
| break; |
| |
| case TrailingClosureMatching::Backward: |
| out << ": backward\n"; |
| break; |
| } |
| } |
| |
| out << "\nDisjunction choices:\n"; |
| for (auto &choice : DisjunctionChoices) { |
| out.indent(2); |
| choice.first->dump(sm, out); |
| out << " is #" << choice.second << "\n"; |
| } |
| |
| if (!OpenedTypes.empty()) { |
| out << "\nOpened types:\n"; |
| for (const auto &opened : OpenedTypes) { |
| out.indent(2); |
| opened.first->dump(sm, out); |
| out << " opens "; |
| llvm::interleave( |
| opened.second.begin(), opened.second.end(), |
| [&](OpenedType opened) { |
| Type(opened.first).print(out, PO); |
| out << " -> "; |
| Type(opened.second).print(out, PO); |
| }, |
| [&]() { out << ", "; }); |
| out << "\n"; |
| } |
| } |
| |
| if (!OpenedExistentialTypes.empty()) { |
| out << "\nOpened existential types:\n"; |
| for (const auto &openedExistential : OpenedExistentialTypes) { |
| out.indent(2); |
| openedExistential.first->dump(sm, out); |
| out << " opens to " << openedExistential.second->getString(PO); |
| out << "\n"; |
| } |
| } |
| |
| if (!DefaultedConstraints.empty()) { |
| out << "\nDefaulted constraints: "; |
| interleave(DefaultedConstraints, [&](ConstraintLocator *locator) { |
| locator->dump(sm, out); |
| }, [&] { |
| out << ", "; |
| }); |
| } |
| |
| if (!Fixes.empty()) { |
| out << "\nFixes:\n"; |
| for (auto *fix : Fixes) { |
| out.indent(2); |
| fix->print(out); |
| out << "\n"; |
| } |
| } |
| } |
| |
| void ConstraintSystem::dump() const { |
| print(llvm::errs()); |
| } |
| |
| void ConstraintSystem::dump(Expr *E) const { |
| print(llvm::errs(), E); |
| } |
| |
| void ConstraintSystem::print(raw_ostream &out, Expr *E) const { |
| auto getTypeOfExpr = [&](Expr *E) -> Type { |
| if (hasType(E)) |
| return getType(E); |
| return Type(); |
| }; |
| auto getTypeOfTypeRepr = [&](TypeRepr *TR) -> Type { |
| if (hasType(TR)) |
| return getType(TR); |
| return Type(); |
| }; |
| auto getTypeOfKeyPathComponent = [&](KeyPathExpr *KP, unsigned I) -> Type { |
| if (hasType(KP, I)) |
| return getType(KP, I); |
| return Type(); |
| }; |
| |
| E->dump(out, getTypeOfExpr, getTypeOfTypeRepr, getTypeOfKeyPathComponent); |
| } |
| |
| void ConstraintSystem::print(raw_ostream &out) const { |
| // Print all type variables as $T0 instead of _ here. |
| PrintOptions PO; |
| PO.PrintTypesForDebugging = true; |
| |
| out << "Score: " << CurrentScore << "\n"; |
| |
| for (const auto &contextualType : contextualTypes) { |
| out << "Contextual Type: " << contextualType.second.getType().getString(PO); |
| if (TypeRepr *TR = contextualType.second.typeLoc.getTypeRepr()) { |
| out << " at "; |
| TR->getSourceRange().print(out, getASTContext().SourceMgr, /*text*/false); |
| } |
| out << "\n"; |
| } |
| |
| out << "Type Variables:\n"; |
| for (auto tv : getTypeVariables()) { |
| out.indent(2); |
| Type(tv).print(out, PO); |
| if (tv->getImpl().canBindToLValue()) |
| out << " [lvalue allowed]"; |
| if (tv->getImpl().canBindToInOut()) |
| out << " [inout allowed]"; |
| if (tv->getImpl().canBindToNoEscape()) |
| out << " [noescape allowed]"; |
| auto rep = getRepresentative(tv); |
| if (rep == tv) { |
| if (auto fixed = getFixedType(tv)) { |
| out << " as "; |
| Type(fixed).print(out, PO); |
| } else { |
| const_cast<ConstraintSystem *>(this)->inferBindingsFor(tv).dump(out, 1); |
| } |
| } else { |
| out << " equivalent to "; |
| Type(rep).print(out, PO); |
| } |
| |
| if (auto *locator = tv->getImpl().getLocator()) { |
| out << " @ "; |
| locator->dump(&getASTContext().SourceMgr, out); |
| } |
| |
| out << "\n"; |
| } |
| |
| out << "\nActive Constraints:\n"; |
| for (auto &constraint : ActiveConstraints) { |
| out.indent(2); |
| constraint.print(out, &getASTContext().SourceMgr); |
| out << "\n"; |
| } |
| |
| out << "\nInactive Constraints:\n"; |
| for (auto &constraint : InactiveConstraints) { |
| out.indent(2); |
| constraint.print(out, &getASTContext().SourceMgr); |
| out << "\n"; |
| } |
| |
| if (solverState && solverState->hasRetiredConstraints()) { |
| out << "\nRetired Constraints:\n"; |
| solverState->forEachRetired([&](Constraint &constraint) { |
| out.indent(2); |
| constraint.print(out, &getASTContext().SourceMgr); |
| out << "\n"; |
| }); |
| } |
| |
| if (!ResolvedOverloads.empty()) { |
| out << "Resolved overloads:\n"; |
| |
| // Otherwise, report the resolved overloads. |
| for (auto elt : ResolvedOverloads) { |
| auto resolved = elt.second; |
| auto &choice = resolved.choice; |
| out << " selected overload set choice "; |
| switch (choice.getKind()) { |
| case OverloadChoiceKind::Decl: |
| case OverloadChoiceKind::DeclViaDynamic: |
| case OverloadChoiceKind::DeclViaBridge: |
| case OverloadChoiceKind::DeclViaUnwrappedOptional: |
| if (choice.getBaseType()) |
| out << choice.getBaseType()->getString(PO) << "."; |
| out << choice.getDecl()->getBaseName() << ": " |
| << resolved.boundType->getString(PO) << " == " |
| << resolved.openedType->getString(PO) << "\n"; |
| break; |
| |
| case OverloadChoiceKind::KeyPathApplication: |
| out << "key path application root " |
| << choice.getBaseType()->getString(PO) << "\n"; |
| break; |
| |
| case OverloadChoiceKind::DynamicMemberLookup: |
| case OverloadChoiceKind::KeyPathDynamicMemberLookup: |
| out << "dynamic member lookup:" |
| << choice.getBaseType()->getString(PO) << " name=" |
| << choice.getName() << "\n"; |
| break; |
| |
| case OverloadChoiceKind::TupleIndex: |
| out << "tuple " << choice.getBaseType()->getString(PO) << " index " |
| << choice.getTupleIndex() << "\n"; |
| break; |
| } |
| } |
| out << "\n"; |
| } |
| |
| if (!DisjunctionChoices.empty()) { |
| out << "\nDisjunction choices:\n"; |
| for (auto &choice : DisjunctionChoices) { |
| out.indent(2); |
| choice.first->dump(&getASTContext().SourceMgr, out); |
| out << " is #" << choice.second << "\n"; |
| } |
| } |
| |
| if (!OpenedTypes.empty()) { |
| out << "\nOpened types:\n"; |
| for (const auto &opened : OpenedTypes) { |
| out.indent(2); |
| opened.first->dump(&getASTContext().SourceMgr, out); |
| out << " opens "; |
| llvm::interleave( |
| opened.second.begin(), opened.second.end(), |
| [&](OpenedType opened) { |
| Type(opened.first).print(out, PO); |
| out << " -> "; |
| Type(opened.second).print(out, PO); |
| }, |
| [&]() { out << ", "; }); |
| out << "\n"; |
| } |
| } |
| |
| if (!OpenedExistentialTypes.empty()) { |
| out << "\nOpened existential types:\n"; |
| for (const auto &openedExistential : OpenedExistentialTypes) { |
| out.indent(2); |
| openedExistential.first->dump(&getASTContext().SourceMgr, out); |
| out << " opens to " << openedExistential.second->getString(PO); |
| out << "\n"; |
| } |
| } |
| |
| if (!DefaultedConstraints.empty()) { |
| out << "\nDefaulted constraints: "; |
| interleave(DefaultedConstraints, [&](ConstraintLocator *locator) { |
| locator->dump(&getASTContext().SourceMgr, out); |
| }, [&] { |
| out << ", "; |
| }); |
| out << "\n"; |
| } |
| |
| if (failedConstraint) { |
| out << "\nFailed constraint:\n"; |
| out.indent(2); |
| failedConstraint->print(out, &getASTContext().SourceMgr); |
| out << "\n"; |
| } |
| |
| if (!Fixes.empty()) { |
| out << "\nFixes:\n"; |
| for (auto *fix : Fixes) { |
| out.indent(2); |
| fix->print(out); |
| out << "\n"; |
| } |
| } |
| } |
| |
| /// Determine the semantics of a checked cast operation. |
| CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, |
| Type toType, |
| CheckedCastContextKind contextKind, |
| DeclContext *dc, |
| SourceLoc diagLoc, |
| Expr *fromExpr, |
| SourceRange diagToRange) { |
| // Determine whether we should suppress diagnostics. |
| const bool suppressDiagnostics = |
| contextKind == CheckedCastContextKind::None || |
| contextKind == CheckedCastContextKind::Coercion; |
| assert((suppressDiagnostics || diagLoc.isValid()) && |
| "diagnostics require a valid source location"); |
| |
| SourceRange diagFromRange; |
| if (fromExpr) |
| diagFromRange = fromExpr->getSourceRange(); |
| |
| // If the from/to types are equivalent or convertible, this is a coercion. |
| bool unwrappedIUO = false; |
| if (fromType->isEqual(toType) || |
| (isConvertibleTo(fromType, toType, dc, &unwrappedIUO) && |
| !unwrappedIUO)) { |
| return CheckedCastKind::Coercion; |
| } |
| |
| // Check for a bridging conversion. |
| // Anything bridges to AnyObject. |
| if (toType->isAnyObject()) |
| return CheckedCastKind::BridgingCoercion; |
| |
| if (isObjCBridgedTo(fromType, toType, dc, &unwrappedIUO) && !unwrappedIUO){ |
| return CheckedCastKind::BridgingCoercion; |
| } |
| |
| Type origFromType = fromType; |
| Type origToType = toType; |
| |
| auto &diags = dc->getASTContext().Diags; |
| bool optionalToOptionalCast = false; |
| |
| // Local function to indicate failure. |
| auto failed = [&] { |
| if (suppressDiagnostics) { |
| return CheckedCastKind::Unresolved; |
| } |
| |
| // Explicit optional-to-optional casts always succeed because a nil |
| // value of any optional type can be cast to any other optional type. |
| if (optionalToOptionalCast) |
| return CheckedCastKind::ValueCast; |
| |
| diags.diagnose(diagLoc, diag::downcast_to_unrelated, origFromType, |
| origToType) |
| .highlight(diagFromRange) |
| .highlight(diagToRange); |
| |
| return CheckedCastKind::ValueCast; |
| }; |
| |
| // TODO: Explore optionals using the same strategy used by the |
| // runtime. |
| // For now, if the target is more optional than the source, |
| // just defer it out for the runtime to handle. |
| while (auto toValueType = toType->getOptionalObjectType()) { |
| auto fromValueType = fromType->getOptionalObjectType(); |
| if (!fromValueType) { |
| return CheckedCastKind::ValueCast; |
| } |
| |
| toType = toValueType; |
| fromType = fromValueType; |
| optionalToOptionalCast = true; |
| } |
| |
| // On the other hand, casts can decrease optionality monadically. |
| unsigned extraFromOptionals = 0; |
| while (auto fromValueType = fromType->getOptionalObjectType()) { |
| fromType = fromValueType; |
| ++extraFromOptionals; |
| } |
| |
| // If the unwrapped from/to types are equivalent or bridged, this isn't a real |
| // downcast. Complain. |
| auto &Context = dc->getASTContext(); |
| if (extraFromOptionals > 0) { |
| switch (typeCheckCheckedCast(fromType, toType, |
| CheckedCastContextKind::None, dc, |
| SourceLoc(), nullptr, SourceRange())) { |
| case CheckedCastKind::Coercion: |
| case CheckedCastKind::BridgingCoercion: { |
| // FIXME: Add a Fix-It, when the caller provides us with enough |
| // information. |
| if (!suppressDiagnostics) { |
| bool isBridged = |
| !fromType->isEqual(toType) && !isConvertibleTo(fromType, toType, dc); |
| |
| switch (contextKind) { |
| case CheckedCastContextKind::None: |
| case CheckedCastContextKind::Coercion: |
| llvm_unreachable("suppressing diagnostics"); |
| |
| case CheckedCastContextKind::ForcedCast: { |
| std::string extraFromOptionalsStr(extraFromOptionals, '!'); |
| auto diag = diags.diagnose(diagLoc, diag::downcast_same_type, |
| origFromType, origToType, |
| extraFromOptionalsStr, |
| isBridged); |
| diag.highlight(diagFromRange); |
| diag.highlight(diagToRange); |
| |
| /// Add the '!''s needed to adjust the type. |
| diag.fixItInsertAfter(diagFromRange.End, |
| std::string(extraFromOptionals, '!')); |
| if (isBridged) { |
| // If it's bridged, we still need the 'as' to perform the bridging. |
| diag.fixItReplaceChars(diagLoc, diagLoc.getAdvancedLocOrInvalid(3), |
| "as"); |
| } else { |
| // Otherwise, implicit conversions will handle it in most cases. |
| SourceLoc afterExprLoc = Lexer::getLocForEndOfToken(Context.SourceMgr, |
| diagFromRange.End); |
| |
| diag.fixItRemove(SourceRange(afterExprLoc, diagToRange.End)); |
| } |
| break; |
| } |
| |
| case CheckedCastContextKind::ConditionalCast: |
| // If we're only unwrapping a single optional, that optional value is |
| // effectively carried through to the underlying conversion, making this |
| // the moral equivalent of a map. Complain that one can do this with |
| // 'as' more effectively. |
| if (extraFromOptionals == 1) { |
| // A single optional is carried through. It's better to use 'as' to |
| // the appropriate optional type. |
| auto diag = diags.diagnose(diagLoc, |
| diag::conditional_downcast_same_type, |
| origFromType, origToType, |
| fromType->isEqual(toType) ? 0 |
| : isBridged ? 2 |
| : 1); |
| diag.highlight(diagFromRange); |
| diag.highlight(diagToRange); |
| |
| if (isBridged) { |
| // For a bridged cast, replace the 'as?' with 'as'. |
| diag.fixItReplaceChars(diagLoc, diagLoc.getAdvancedLocOrInvalid(3), |
| "as"); |
| |
| // Make sure we'll cast to the appropriately-optional type by adding |
| // the '?'. |
| // FIXME: Parenthesize! |
| diag.fixItInsertAfter(diagToRange.End, "?"); |
| } else { |
| // Just remove the cast; implicit conversions will handle it. |
| SourceLoc afterExprLoc = |
| Lexer::getLocForEndOfToken(Context.SourceMgr, diagFromRange.End); |
| |
| if (afterExprLoc.isValid() && diagToRange.isValid()) |
| diag.fixItRemove(SourceRange(afterExprLoc, diagToRange.End)); |
| } |
| } |
| |
| // If there is more than one extra optional, don't do anything: this |
| // conditional cast is trying to unwrap some levels of optional; |
| // let the runtime handle it. |
| break; |
| |
| case CheckedCastContextKind::IsExpr: |
| // If we're only unwrapping a single optional, we could have just |
| // checked for 'nil'. |
| if (extraFromOptionals == 1) { |
| auto diag = diags.diagnose(diagLoc, diag::is_expr_same_type, |
| origFromType, origToType); |
| diag.highlight(diagFromRange); |
| diag.highlight(diagToRange); |
| |
| diag.fixItReplace(SourceRange(diagLoc, diagToRange.End), "!= nil"); |
| |
| // Add parentheses if needed. |
| if (!fromExpr->canAppendPostfixExpression()) { |
| diag.fixItInsert(fromExpr->getStartLoc(), "("); |
| diag.fixItInsertAfter(fromExpr->getEndLoc(), ")"); |
| } |
| } |
| |
| // If there is more than one extra optional, don't do anything: this |
| // is performing a deeper check that the runtime will handle. |
| break; |
| |
| case CheckedCastContextKind::IsPattern: |
| case CheckedCastContextKind::EnumElementPattern: |
| // Note: Don't diagnose these, because the code is testing whether |
| // the optionals can be unwrapped. |
| break; |
| } |
| } |
| |
| // Treat this as a value cast so we preserve the semantics. |
| return CheckedCastKind::ValueCast; |
| } |
| |
| case CheckedCastKind::ArrayDowncast: |
| case CheckedCastKind::DictionaryDowncast: |
| case CheckedCastKind::SetDowncast: |
| case CheckedCastKind::ValueCast: |
| break; |
| |
| case CheckedCastKind::Unresolved: |
| return failed(); |
| } |
| } |
| |
| auto checkElementCast = [&](Type fromElt, Type toElt, |
| CheckedCastKind castKind) -> CheckedCastKind { |
| switch (typeCheckCheckedCast(fromElt, toElt, CheckedCastContextKind::None, |
| dc, SourceLoc(), nullptr, SourceRange())) { |
| case CheckedCastKind::Coercion: |
| return CheckedCastKind::Coercion; |
| |
| case CheckedCastKind::BridgingCoercion: |
| return CheckedCastKind::BridgingCoercion; |
| |
| case CheckedCastKind::ArrayDowncast: |
| case CheckedCastKind::DictionaryDowncast: |
| case CheckedCastKind::SetDowncast: |
| case CheckedCastKind::ValueCast: |
| return castKind; |
| |
| case CheckedCastKind::Unresolved: |
| // Even though we know the elements cannot be downcast, we cannot return |
| // failed() here as it's possible for an empty Array, Set or Dictionary to |
| // be cast to any element type at runtime (SR-6192). The one exception to |
| // this is when we're checking whether we can treat a coercion as a checked |
| // cast because we don't want to tell the user to use as!, as it's probably |
| // the wrong suggestion. |
| if (contextKind == CheckedCastContextKind::Coercion) |
| return failed(); |
| return castKind; |
| } |
| llvm_unreachable("invalid cast type"); |
| }; |
| |
| // Check for casts between specific concrete types that cannot succeed. |
| if (auto toElementType = ConstraintSystem::isArrayType(toType)) { |
| if (auto fromElementType = ConstraintSystem::isArrayType(fromType)) { |
| return checkElementCast(*fromElementType, *toElementType, |
| CheckedCastKind::ArrayDowncast); |
| } |
| } |
| |
| if (auto toKeyValue = ConstraintSystem::isDictionaryType(toType)) { |
| if (auto fromKeyValue = ConstraintSystem::isDictionaryType(fromType)) { |
| bool hasCoercion = false; |
| enum { NoBridging, BridgingCoercion } |
| hasBridgingConversion = NoBridging; |
| bool hasCast = false; |
| switch (typeCheckCheckedCast(fromKeyValue->first, toKeyValue->first, |
| CheckedCastContextKind::None, dc, |
| SourceLoc(), nullptr, SourceRange())) { |
| case CheckedCastKind::Coercion: |
| hasCoercion = true; |
| break; |
| |
| case CheckedCastKind::BridgingCoercion: |
| hasBridgingConversion = std::max(hasBridgingConversion, |
| BridgingCoercion); |
| break; |
| |
| case CheckedCastKind::Unresolved: |
| // Handled the same as in checkElementCast; see comment there for |
| // rationale. |
| if (contextKind == CheckedCastContextKind::Coercion) |
| return failed(); |
| LLVM_FALLTHROUGH; |
| |
| case CheckedCastKind::ArrayDowncast: |
| case CheckedCastKind::DictionaryDowncast: |
| case CheckedCastKind::SetDowncast: |
| case CheckedCastKind::ValueCast: |
| hasCast = true; |
| break; |
| } |
| |
| switch (typeCheckCheckedCast(fromKeyValue->second, toKeyValue->second, |
| CheckedCastContextKind::None, dc, |
| SourceLoc(), nullptr, SourceRange())) { |
| case CheckedCastKind::Coercion: |
| hasCoercion = true; |
| break; |
| |
| case CheckedCastKind::BridgingCoercion: |
| hasBridgingConversion = std::max(hasBridgingConversion, |
| BridgingCoercion); |
| break; |
| |
| case CheckedCastKind::Unresolved: |
| // Handled the same as in checkElementCast; see comment there for |
| // rationale. |
| if (contextKind == CheckedCastContextKind::Coercion) |
| return failed(); |
| LLVM_FALLTHROUGH; |
| |
| case CheckedCastKind::ArrayDowncast: |
| case CheckedCastKind::DictionaryDowncast: |
| case CheckedCastKind::SetDowncast: |
| case CheckedCastKind::ValueCast: |
| hasCast = true; |
| break; |
| } |
| |
| if (hasCast) return CheckedCastKind::DictionaryDowncast; |
| switch (hasBridgingConversion) { |
| case NoBridging: |
| break; |
| case BridgingCoercion: |
| return CheckedCastKind::BridgingCoercion; |
| } |
| assert(hasCoercion && "Not a coercion?"); |
| return CheckedCastKind::Coercion; |
| } |
| } |
| |
| if (auto toElementType = ConstraintSystem::isSetType(toType)) { |
| if (auto fromElementType = ConstraintSystem::isSetType(fromType)) { |
| return checkElementCast(*fromElementType, *toElementType, |
| CheckedCastKind::SetDowncast); |
| } |
| } |
| |
| if (auto toTuple = toType->getAs<TupleType>()) { |
| if (auto fromTuple = fromType->getAs<TupleType>()) { |
| if (fromTuple->getNumElements() != toTuple->getNumElements()) |
| return failed(); |
| |
| for (unsigned i = 0, n = toTuple->getNumElements(); i != n; ++i) { |
| const auto &fromElt = fromTuple->getElement(i); |
| const auto &toElt = toTuple->getElement(i); |
| |
| // We should only perform name validation if both elements have a label, |
| // because unlabeled tuple elements can be converted to labeled ones |
| // e.g. |
| // |
| // let tup: (Any, Any) = (1, 1) |
| // _ = tup as! (a: Int, Int) |
| if ((!fromElt.getName().empty() && !toElt.getName().empty()) && |
| fromElt.getName() != toElt.getName()) |
| return failed(); |
| |
| auto result = checkElementCast(fromElt.getType(), toElt.getType(), |
| CheckedCastKind::ValueCast); |
| |
| if (result == CheckedCastKind::Unresolved) |
| return result; |
| } |
| |
| return CheckedCastKind::ValueCast; |
| } |
| } |
| |
| assert(!toType->isAny() && "casts to 'Any' should've been handled above"); |
| assert(!toType->isAnyObject() && |
| "casts to 'AnyObject' should've been handled above"); |
| |
| // A cast from a function type to an existential type (except `Any`) |
| // or an archetype type (with constraints) cannot succeed |
| auto toArchetypeType = toType->is<ArchetypeType>(); |
| auto fromFunctionType = fromType->is<FunctionType>(); |
| auto toExistentialType = toType->isAnyExistentialType(); |
| |
| auto toConstrainedArchetype = false; |
| if (toArchetypeType) { |
| auto archetype = toType->castTo<ArchetypeType>(); |
| toConstrainedArchetype = !archetype->getConformsTo().empty(); |
| } |
| |
| if (fromFunctionType && |
| (toExistentialType || (toArchetypeType && toConstrainedArchetype))) { |
| switch (contextKind) { |
| case CheckedCastContextKind::ConditionalCast: |
| case CheckedCastContextKind::ForcedCast: |
| diags.diagnose(diagLoc, diag::downcast_to_unrelated, origFromType, |
| origToType) |
| .highlight(diagFromRange) |
| .highlight(diagToRange); |
| |
| // If we're referring to a function with a return value (not Void) then |
| // emit a fix-it suggesting to add `()` to call the function |
| if (auto DRE = dyn_cast<DeclRefExpr>(fromExpr)) { |
| if (auto FD = dyn_cast<FuncDecl>(DRE->getDecl())) { |
| if (!FD->getResultInterfaceType()->isVoid()) { |
| diags.diagnose(diagLoc, diag::downcast_to_unrelated_fixit, |
| FD->getBaseIdentifier()) |
| .fixItInsertAfter(fromExpr->getEndLoc(), "()"); |
| } |
| } |
| } |
| |
| return CheckedCastKind::ValueCast; |
| break; |
| |
| case CheckedCastContextKind::IsPattern: |
| case CheckedCastContextKind::EnumElementPattern: |
| case CheckedCastContextKind::IsExpr: |
| case CheckedCastContextKind::None: |
| case CheckedCastContextKind::Coercion: |
| break; |
| } |
| } |
| |
| // If we can bridge through an Objective-C class, do so. |
| if (Type bridgedToClass = getDynamicBridgedThroughObjCClass(dc, fromType, |
| toType)) { |
| switch (typeCheckCheckedCast(bridgedToClass, fromType, |
| CheckedCastContextKind::None, dc, SourceLoc(), |
| nullptr, SourceRange())) { |
| case CheckedCastKind::ArrayDowncast: |
| case CheckedCastKind::BridgingCoercion: |
| case CheckedCastKind::Coercion: |
| case CheckedCastKind::DictionaryDowncast: |
| case CheckedCastKind::SetDowncast: |
| case CheckedCastKind::ValueCast: |
| return CheckedCastKind::ValueCast; |
| |
| case CheckedCastKind::Unresolved: |
| break; |
| } |
| } |
| |
| // If we can bridge through an Objective-C class, do so. |
| if (Type bridgedFromClass = getDynamicBridgedThroughObjCClass(dc, toType, |
| fromType)) { |
| switch (typeCheckCheckedCast(toType, bridgedFromClass, |
| CheckedCastContextKind::None, dc, SourceLoc(), |
| nullptr, SourceRange())) { |
| case CheckedCastKind::ArrayDowncast: |
| case CheckedCastKind::BridgingCoercion: |
| case CheckedCastKind::Coercion: |
| case CheckedCastKind::DictionaryDowncast: |
| case CheckedCastKind::SetDowncast: |
| case CheckedCastKind::ValueCast: |
| return CheckedCastKind::ValueCast; |
| |
| case CheckedCastKind::Unresolved: |
| break; |
| } |
| } |
| |
| // Strip metatypes. If we can cast two types, we can cast their metatypes. |
| bool metatypeCast = false; |
| while (auto toMetatype = toType->getAs<MetatypeType>()) { |
| auto fromMetatype = fromType->getAs<MetatypeType>(); |
| if (!fromMetatype) |
| break; |
| |
| metatypeCast = true; |
| toType = toMetatype->getInstanceType(); |
| fromType = fromMetatype->getInstanceType(); |
| } |
| |
| // Strip an inner layer of potentially existential metatype. |
| bool toExistentialMetatype = false; |
| bool fromExistentialMetatype = false; |
| if (auto toMetatype = toType->getAs<AnyMetatypeType>()) { |
| if (auto fromMetatype = fromType->getAs<AnyMetatypeType>()) { |
| toExistentialMetatype = toType->is<ExistentialMetatypeType>(); |
| fromExistentialMetatype = fromType->is<ExistentialMetatypeType>(); |
| toType = toMetatype->getInstanceType(); |
| fromType = fromMetatype->getInstanceType(); |
| } |
| } |
| |
| bool toArchetype = toType->is<ArchetypeType>(); |
| bool fromArchetype = fromType->is<ArchetypeType>(); |
| bool toExistential = toType->isExistentialType(); |
| bool fromExistential = fromType->isExistentialType(); |
| |
| bool toRequiresClass; |
| if (toType->isExistentialType()) |
| toRequiresClass = toType->getExistentialLayout().requiresClass(); |
| else |
| toRequiresClass = toType->mayHaveSuperclass(); |
| |
| bool fromRequiresClass; |
| if (fromType->isExistentialType()) |
| fromRequiresClass = fromType->getExistentialLayout().requiresClass(); |
| else |
| fromRequiresClass = fromType->mayHaveSuperclass(); |
| |
| // Casts between metatypes only succeed if none of the types are existentials |
| // or if one is an existential and the other is a generic type because there |
| // may be protocol conformances unknown at compile time. |
| if (metatypeCast) { |
| if ((toExistential || fromExistential) && !(fromArchetype || toArchetype)) |
| return failed(); |
| } |
| |
| // Casts from an existential metatype to a protocol metatype always fail, |
| // except when the existential type is 'Any'. |
| if (fromExistentialMetatype && |
| !fromType->isAny() && |
| !toExistentialMetatype && |
| toExistential) |
| return failed(); |
| |
| // Casts to or from generic types can't be statically constrained in most |
| // cases, because there may be protocol conformances we don't statically |
| // know about. |
| if (toExistential || fromExistential || fromArchetype || toArchetype || |
| toRequiresClass || fromRequiresClass) { |
| // Cast to and from AnyObject always succeed. |
| if (!metatypeCast && |
| !fromExistentialMetatype && |
| !toExistentialMetatype && |
| (toType->isAnyObject() || fromType->isAnyObject())) |
| return CheckedCastKind::ValueCast; |
| |
| // If we have a cast from an existential type to a concrete type that we |
| // statically know doesn't conform to the protocol, mark the cast as always |
| // failing. For example: |
| // |
| // struct S {} |
| // enum FooError: Error { case bar } |
| // |
| // func foo() { |
| // do { |
| // throw FooError.bar |
| // } catch is X { /* Will always fail */ |
| // print("Caught bar error") |
| // } |
| // } |
| // |
| if (auto *protocolDecl = |
| dyn_cast_or_null<ProtocolDecl>(fromType->getAnyNominal())) { |
| if (!couldDynamicallyConformToProtocol(toType, protocolDecl, dc)) { |
| return failed(); |
| } |
| } else if (auto protocolComposition = |
| fromType->getAs<ProtocolCompositionType>()) { |
| if (llvm::any_of(protocolComposition->getMembers(), |
| [&](Type protocolType) { |
| if (auto protocolDecl = dyn_cast_or_null<ProtocolDecl>( |
| protocolType->getAnyNominal())) { |
| return !couldDynamicallyConformToProtocol( |
| toType, protocolDecl, dc); |
| } |
| return false; |
| })) { |
| return failed(); |
| } |
| } |
| |
| // If neither type is class-constrained, anything goes. |
| if (!fromRequiresClass && !toRequiresClass) |
| return CheckedCastKind::ValueCast; |
| |
| if (!fromRequiresClass && toRequiresClass) { |
| // If source type is abstract, anything goes. |
| if (fromExistential || fromArchetype) |
| return CheckedCastKind::ValueCast; |
| |
| // Otherwise, we're casting a concrete non-class type to a |
| // class-constrained archetype or existential, which will |
| // probably fail, but we'll try more casts below. |
| } |
| |
| if (fromRequiresClass && !toRequiresClass) { |
| // If destination type is abstract, anything goes. |
| if (toExistential || toArchetype) |
| return CheckedCastKind::ValueCast; |
| |
| // Otherwise, we're casting a class-constrained archetype |
| // or existential to a non-class concrete type, which |
| // will probably fail, but we'll try more casts below. |
| } |
| |
| if (fromRequiresClass && toRequiresClass) { |
| // Ok, we are casting between class-like things. Let's see if we have |
| // explicit superclass bounds. |
| Type toSuperclass; |
| if (toType->getClassOrBoundGenericClass()) |
| toSuperclass = toType; |
| else |
| toSuperclass = toType->getSuperclass(); |
| |
| Type fromSuperclass; |
| if (fromType->getClassOrBoundGenericClass()) |
| fromSuperclass = fromType; |
| else |
| fromSuperclass = fromType->getSuperclass(); |
| |
| // Unless both types have a superclass bound, we have no further |
| // information. |
| if (!toSuperclass || !fromSuperclass) |
| return CheckedCastKind::ValueCast; |
| |
| // Compare superclass bounds. |
| if (fromSuperclass->isBindableToSuperclassOf(toSuperclass)) |
| return CheckedCastKind::ValueCast; |
| |
| // An upcast is also OK. |
| if (toSuperclass->isBindableToSuperclassOf(fromSuperclass)) |
| return CheckedCastKind::ValueCast; |
| } |
| } |
| |
| if (ConstraintSystem::isAnyHashableType(toType) || |
| ConstraintSystem::isAnyHashableType(fromType)) { |
| return CheckedCastKind::ValueCast; |
| } |
| |
| // We perform an upcast while rebinding generic parameters if it's possible |
| // to substitute the generic arguments of the source type with the generic |
| // archetypes of the destination type. Or, if it's possible to substitute |
| // the generic arguments of the destination type with the generic archetypes |
| // of the source type, we perform a downcast instead. |
| if (toType->isBindableTo(fromType) || fromType->isBindableTo(toType)) |
| return CheckedCastKind::ValueCast; |
| |
| // Objective-C metaclasses are subclasses of NSObject in the ObjC runtime, |
| // so casts from NSObject to potentially-class metatypes may succeed. |
| if (auto nsObject = Context.getNSObjectType()) { |
| if (fromType->isEqual(nsObject)) { |
| if (auto toMeta = toType->getAs<MetatypeType>()) { |
| if (toMeta->getInstanceType()->mayHaveSuperclass() |
| || toMeta->getInstanceType()->is<ArchetypeType>()) |
| return CheckedCastKind::ValueCast; |
| } |
| if (toType->is<ExistentialMetatypeType>()) |
| return CheckedCastKind::ValueCast; |
| } |
| } |
| |
| // We can conditionally cast from NSError to an Error-conforming type. |
| // This is handled in the runtime, so it doesn't need a special cast |
| // kind. |
| if (Context.LangOpts.EnableObjCInterop) { |
| auto nsObject = Context.getNSObjectType(); |
| auto nsErrorTy = Context.getNSErrorType(); |
| |
| if (auto errorTypeProto = Context.getProtocol(KnownProtocolKind::Error)) { |
| if (!conformsToProtocol(toType, errorTypeProto, dc).isInvalid()) { |
| if (nsErrorTy) { |
| if (isSubtypeOf(fromType, nsErrorTy, dc) |
| // Don't mask "always true" warnings if NSError is cast to |
| // Error itself. |
| && !isSubtypeOf(fromType, toType, dc)) |
| return CheckedCastKind::ValueCast; |
| } |
| } |
| |
| if (!conformsToProtocol(fromType, errorTypeProto, dc).isInvalid()) { |
| // Cast of an error-conforming type to NSError or NSObject. |
| if ((nsObject && toType->isEqual(nsObject)) || |
| (nsErrorTy && toType->isEqual(nsErrorTy))) |
| return CheckedCastKind::BridgingCoercion; |
| } |
| } |
| |
| // Any class-like type could be dynamically cast to NSObject or NSError |
| // via an Error conformance. |
| if (fromType->mayHaveSuperclass() && |
| ((nsObject && toType->isEqual(nsObject)) || |
| (nsErrorTy && toType->isEqual(nsErrorTy)))) { |
| return CheckedCastKind::ValueCast; |
| } |
| } |
| |
| // The runtime doesn't support casts to CF types and always lets them succeed. |
| // This "always fails" diagnosis makes no sense when paired with the CF |
| // one. |
| auto clas = toType->getClassOrBoundGenericClass(); |
| if (clas && clas->getForeignClassKind() == ClassDecl::ForeignKind::CFType) |
| return CheckedCastKind::ValueCast; |
| |
| // Don't warn on casts that change the generic parameters of ObjC generic |
| // classes. This may be necessary to force-fit ObjC APIs that depend on |
| // covariance, or for APIs where the generic parameter annotations in the |
| // ObjC headers are inaccurate. |
| if (clas && clas->usesObjCGenericsModel()) { |
| if (fromType->getClassOrBoundGenericClass() == clas) |
| return CheckedCastKind::ValueCast; |
| } |
| |
| return failed(); |
| } |
| |
| /// If the expression is an implicit call to _forceBridgeFromObjectiveC or |
| /// _conditionallyBridgeFromObjectiveC, returns the argument of that call. |
| static Expr *lookThroughBridgeFromObjCCall(ASTContext &ctx, Expr *expr) { |
| auto call = dyn_cast<CallExpr>(expr); |
| if (!call || !call->isImplicit()) |
| return nullptr; |
| |
| auto callee = call->getCalledValue(); |
| if (!callee) |
| return nullptr; |
| |
| if (callee == ctx.getForceBridgeFromObjectiveC() || |
| callee == ctx.getConditionallyBridgeFromObjectiveC()) |
| return cast<TupleExpr>(call->getArg())->getElement(0); |
| |
| return nullptr; |
| } |
| |
| /// If the expression has the effect of a forced downcast, find the |
| /// underlying forced downcast expression. |
| ForcedCheckedCastExpr *swift::findForcedDowncast(ASTContext &ctx, Expr *expr) { |
| expr = expr->getSemanticsProvidingExpr(); |
| |
| // Simple case: forced checked cast. |
| if (auto forced = dyn_cast<ForcedCheckedCastExpr>(expr)) { |
| return forced; |
| } |
| |
| // If we have an implicit force, look through it. |
| if (auto forced = dyn_cast<ForceValueExpr>(expr)) { |
| if (forced->isImplicit()) { |
| expr = forced->getSubExpr(); |
| } |
| } |
| |
| // Skip through optional evaluations and binds. |
| auto skipOptionalEvalAndBinds = [](Expr *expr) -> Expr* { |
| do { |
| if (!expr->isImplicit()) |
| break; |
| |
| if (auto optionalEval = dyn_cast<OptionalEvaluationExpr>(expr)) { |
| expr = optionalEval->getSubExpr(); |
| continue; |
| } |
| |
| if (auto bindOptional = dyn_cast<BindOptionalExpr>(expr)) { |
| expr = bindOptional->getSubExpr(); |
| continue; |
| } |
| |
| break; |
| } while (true); |
| |
| return expr; |
| }; |
| |
| auto sub = skipOptionalEvalAndBinds(expr); |
| |
| // If we have an explicit cast, we're done. |
| if (auto *FCE = dyn_cast<ForcedCheckedCastExpr>(sub)) |
| return FCE; |
| |
| // Otherwise, try to look through an implicit _forceBridgeFromObjectiveC() call. |
| if (auto arg = lookThroughBridgeFromObjCCall(ctx, sub)) { |
| sub = skipOptionalEvalAndBinds(arg); |
| if (auto *FCE = dyn_cast<ForcedCheckedCastExpr>(sub)) |
| return FCE; |
| } |
| |
| return nullptr; |
| } |
| |
| bool |
| IsCallableNominalTypeRequest::evaluate(Evaluator &evaluator, CanType ty, |
| DeclContext *dc) const { |
| auto options = defaultMemberLookupOptions; |
| options |= NameLookupFlags::IgnoreAccessControl; |
| |
| // Look for a callAsFunction method. |
| auto &ctx = ty->getASTContext(); |
| auto results = |
| TypeChecker::lookupMember(dc, ty, DeclNameRef(ctx.Id_callAsFunction), |
| options); |
| return llvm::any_of(results, [](LookupResultEntry entry) -> bool { |
| if (auto *fd = dyn_cast<FuncDecl>(entry.getValueDecl())) |
| return fd->isCallAsFunctionMethod(); |
| return false; |
| }); |
| } |
| |
| template <class DynamicAttribute> |
| static bool checkForDynamicAttribute(CanType ty, |
| llvm::function_ref<bool (Type)> hasAttribute) { |
| // If this is an archetype type, check if any types it conforms to |
| // (superclass or protocols) have the attribute. |
| if (auto archetype = dyn_cast<ArchetypeType>(ty)) { |
| for (auto proto : archetype->getConformsTo()) { |
| if (hasAttribute(proto->getDeclaredInterfaceType())) |
| return true; |
| } |
| if (auto superclass = archetype->getSuperclass()) { |
| if (hasAttribute(superclass)) |
| return true; |
| } |
| } |
| |
| // If this is a protocol composition, check if any of its members have the |
| // attribute. |
| if (auto protocolComp = dyn_cast<ProtocolCompositionType>(ty)) { |
| for (auto member : protocolComp->getMembers()) { |
| if (hasAttribute(member)) |
| return true; |
| } |
| } |
| |
| // Otherwise, this must be a nominal type. |
| // Neither Dynamic member lookup nor Dynamic Callable doesn't |
| // work for tuples, etc. |
| auto nominal = ty->getAnyNominal(); |
| if (!nominal) |
| return false; |
| |
| // If this type has the attribute on it, then yes! |
| if (nominal->getAttrs().hasAttribute<DynamicAttribute>()) |
| return true; |
| |
| // Check the protocols the type conforms to. |
| for (auto proto : nominal->getAllProtocols()) { |
| if (hasAttribute(proto->getDeclaredInterfaceType())) |
| return true; |
| } |
| |
| // Check the superclass if present. |
| if (auto classDecl = dyn_cast<ClassDecl>(nominal)) { |
| if (auto superclass = classDecl->getSuperclass()) { |
| if (hasAttribute(superclass)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool |
| HasDynamicMemberLookupAttributeRequest::evaluate(Evaluator &evaluator, |
| CanType ty) const { |
| return checkForDynamicAttribute<DynamicMemberLookupAttr>(ty, [](Type type) { |
| return type->hasDynamicMemberLookupAttribute(); |
| }); |
| } |
| |
| bool |
| HasDynamicCallableAttributeRequest::evaluate(Evaluator &evaluator, |
| CanType ty) const { |
| return checkForDynamicAttribute<DynamicCallableAttr>(ty, [](Type type) { |
| return type->hasDynamicCallableAttribute(); |
| }); |
| } |
| |
| bool swift::shouldTypeCheckInEnclosingExpression(ClosureExpr *expr) { |
| return expr->hasSingleExpressionBody(); |
| } |
| |
| void swift::forEachExprInConstraintSystem( |
| Expr *expr, llvm::function_ref<Expr *(Expr *)> callback) { |
| struct ChildWalker : ASTWalker { |
| llvm::function_ref<Expr *(Expr *)> callback; |
| |
| ChildWalker(llvm::function_ref<Expr *(Expr *)> callback) |
| : callback(callback) {} |
| |
| std::pair<bool, Expr *> walkToExprPre(Expr *E) override { |
| if (auto closure = dyn_cast<ClosureExpr>(E)) { |
| if (!shouldTypeCheckInEnclosingExpression(closure)) |
| return { false, callback(E) }; |
| } |
| return { true, callback(E) }; |
| } |
| |
| std::pair<bool, Pattern*> walkToPatternPre(Pattern *P) override { |
| return { false, P }; |
| } |
| bool walkToDeclPre(Decl *D) override { return false; } |
| bool walkToTypeReprPre(TypeRepr *T) override { return false; } |
| }; |
| |
| expr->walk(ChildWalker(callback)); |
| } |