/// Given a call to a function, determine whether it is a call to a constexpr /// function. If so, collect its arguments as constants, fold it and return /// None. If not, mark the results as Unknown, and return an Unknown with /// information about the error. llvm::Optional<SymbolicValue> ConstExprFunctionState::computeCallResult(ApplyInst *apply) { auto conventions = apply->getSubstCalleeConv(); // Determine the callee. auto calleeFn = getConstantValue(apply->getOperand(0)); if (calleeFn.getKind() != SymbolicValue::Function) return evaluator.getUnknown((SILInstruction *)apply, UnknownReason::Default); SILFunction *callee = calleeFn.getFunctionValue(); // Verify that we can fold all of the arguments to the call. SmallVector<SymbolicValue, 4> paramConstants; for (unsigned i = 0, e = apply->getNumOperands() - 1; i != e; ++i) { // If any of the arguments is a non-constant value, then we can't fold this // call. auto op = apply->getOperand(i + 1); SymbolicValue argValue = getConstantValue(op); if (!argValue.isConstant()) return argValue; paramConstants.push_back(argValue); } // TODO(constexpr patch): This is currently unused, so we don't need to // calculate the correct value. Eventually, include code that calculates the // correct value. SubstitutionMap calleeSubMap; // Now that we have successfully folded all of the parameters, we can evaluate // the call. evaluator.pushCallStack(apply->getLoc().getSourceLoc()); SmallVector<SymbolicValue, 4> results; auto callResult = evaluateAndCacheCall(*callee, calleeSubMap, paramConstants, results, numInstEvaluated, evaluator); evaluator.popCallStack(); if (callResult.hasValue()) return callResult.getValue(); unsigned nextResult = 0; // If evaluation was successful, remember the results we captured in our // current function's state. if (unsigned numNormalResults = conventions.getNumDirectSILResults()) { // TODO: unclear when this happens, is this for tuple result values? assert(numNormalResults == 1 && "Multiple results aren't supported?"); setValue(apply->getResults()[0], results[nextResult]); ++nextResult; } assert(nextResult == results.size() && "Unexpected number of results found"); // We have successfully folded this call! return None; }
/// Emit a diagnostic for `poundAssert` builtins whose condition is /// false or whose condition cannot be evaluated. static void diagnosePoundAssert(const SILInstruction *I, SILModule &M, ConstExprEvaluator &constantEvaluator) { auto *builtinInst = dyn_cast<BuiltinInst>(I); if (!builtinInst || builtinInst->getBuiltinKind() != BuiltinValueKind::PoundAssert) return; SmallVector<SymbolicValue, 1> values; constantEvaluator.computeConstantValues({builtinInst->getArguments()[0]}, values); SymbolicValue value = values[0]; if (!value.isConstant()) { diagnose(M.getASTContext(), I->getLoc().getSourceLoc(), diag::pound_assert_condition_not_constant); // If we have more specific information about what went wrong, emit // notes. if (value.getKind() == SymbolicValue::Unknown) value.emitUnknownDiagnosticNotes(builtinInst->getLoc()); return; } assert(value.getKind() == SymbolicValue::Integer && "sema prevents non-integer #assert condition"); APInt intValue = value.getIntegerValue(); assert(intValue.getBitWidth() == 1 && "sema prevents non-int1 #assert condition"); if (intValue.isNullValue()) { auto *message = cast<StringLiteralInst>(builtinInst->getArguments()[1]); StringRef messageValue = message->getValue(); if (messageValue.empty()) messageValue = "assertion failed"; diagnose(M.getASTContext(), I->getLoc().getSourceLoc(), diag::pound_assert_failure, messageValue); return; } }