using CoalesceFn = double (*)(double, double, double); using FinalizeFn = double (*)(double);
static std::unique_ptr<Expression> coalesce_n_way_vector(const Expression* arg0, const Expression* arg1, double startingState, const Type& returnType,
CoalesceFn coalesce,
FinalizeFn finalize) { // Takes up to two vector or scalar arguments and coalesces them in sequence: // scalar = startingState; // scalar = coalesce(scalar, arg0.x, arg1.x); // scalar = coalesce(scalar, arg0.y, arg1.y); // scalar = coalesce(scalar, arg0.z, arg1.z); // scalar = coalesce(scalar, arg0.w, arg1.w); // scalar = finalize(scalar); // // If an argument is null, zero is passed to the coalesce function. If the arguments are a mix // of scalars and vectors, the scalars are interpreted as a vector containing the same value for // every component.
if (value >= minimumValue && value <= maximumValue) { // This result will fit inside the return type.
} else { // The value is outside the float range or is NaN (all if-checks fail); do not optimize. return nullptr;
}
}
using EvaluateFn = double (*)(double, double, double);
static std::unique_ptr<Expression> evaluate_n_way_intrinsic(const Context& context, const Expression* arg0, const Expression* arg1, const Expression* arg2, const Type& returnType,
EvaluateFn eval) { // Takes up to three arguments and evaluates all of them, left-to-right, in tandem. // Equivalent to constructing a new compound value containing the results from: // eval(arg0.x, arg1.x, arg2.x), // eval(arg0.y, arg1.y, arg2.y), // eval(arg0.z, arg1.z, arg2.z), // eval(arg0.w, arg1.w, arg2.w) // // If an argument is null, zero is passed to the evaluation function. If the arguments are a mix // of scalars and compounds, scalars are interpreted as a compound containing the same value for // every component.
if (array[index] >= minimumValue && array[index] <= maximumValue) { // This result will fit inside the return type.
} else { // The value is outside the float range or is NaN (all if-checks fail); do not optimize. return nullptr;
}
}
template <typename T1, typename T2> staticdouble pun_value(double val) { // Interpret `val` as a value of type T1.
static_assert(sizeof(T1) == sizeof(T2));
T1 inputValue = (T1)val; // Reinterpret those bits as a value of type T2.
T2 outputValue;
memcpy(&outputValue, &inputValue, sizeof(T2)); // Return the value-of-type-T2 as a double. (Non-finite values will prohibit optimization.) return (double)outputValue;
}
// Helper functions for optimizing all of our intrinsics.
namespace Intrinsics {
namespace {
double coalesce_length(double a, double b, double) { return a + (b * b); } double finalize_length(double a) { return std::sqrt(a); }
double coalesce_distance(double a, double b, double c) { b -= c; return a + (b * b); } double finalize_distance(double a) { return std::sqrt(a); }
double coalesce_dot(double a, double b, double c) { return a + (b * c); } double coalesce_any(double a, double b, double) { return a || b; } double coalesce_all(double a, double b, double) { return a && b; }
bool compare_lessThan(double a, double b) { return a < b; } bool compare_lessThanEqual(double a, double b) { return a <= b; } bool compare_greaterThan(double a, double b) { return a > b; } bool compare_greaterThanEqual(double a, double b) { return a >= b; } bool compare_equal(double a, double b) { return a == b; } bool compare_notEqual(double a, double b) { return a != b; }
double evaluate_radians(double a, double, double) { return a * 0.0174532925; } double evaluate_degrees(double a, double, double) { return a * 57.2957795; } double evaluate_sin(double a, double, double) { return std::sin(a); } double evaluate_cos(double a, double, double) { return std::cos(a); } double evaluate_tan(double a, double, double) { return std::tan(a); } double evaluate_asin(double a, double, double) { return std::asin(a); } double evaluate_acos(double a, double, double) { return std::acos(a); } double evaluate_atan(double a, double, double) { return std::atan(a); } double evaluate_atan2(double a, double b, double) { return std::atan2(a, b); } double evaluate_asinh(double a, double, double) { return std::asinh(a); } double evaluate_acosh(double a, double, double) { return std::acosh(a); } double evaluate_atanh(double a, double, double) { return std::atanh(a); }
double evaluate_pow(double a, double b, double) { return std::pow(a, b); } double evaluate_exp(double a, double, double) { return std::exp(a); } double evaluate_log(double a, double, double) { return std::log(a); } double evaluate_exp2(double a, double, double) { return std::exp2(a); } double evaluate_log2(double a, double, double) { return std::log2(a); } double evaluate_sqrt(double a, double, double) { return std::sqrt(a); } double evaluate_inversesqrt(double a, double, double) { return sk_ieee_double_divide(1.0, std::sqrt(a));
}
double evaluate_add(double a, double b, double) { return a + b; } double evaluate_sub(double a, double b, double) { return a - b; } double evaluate_mul(double a, double b, double) { return a * b; } double evaluate_div(double a, double b, double) { return sk_ieee_double_divide(a, b); } double evaluate_abs(double a, double, double) { return std::abs(a); } double evaluate_sign(double a, double, double) { return (a > 0) - (a < 0); } double evaluate_opposite_sign(double a,double, double) { return (a < 0) - (a > 0); } double evaluate_floor(double a, double, double) { return std::floor(a); } double evaluate_ceil(double a, double, double) { return std::ceil(a); } double evaluate_fract(double a, double, double) { return a - std::floor(a); } double evaluate_min(double a, double b, double) { return (a < b) ? a : b; } double evaluate_max(double a, double b, double) { return (a > b) ? a : b; } double evaluate_clamp(double x, double l, double h) { return (x < l) ? l : (x > h) ? h : x; } double evaluate_fma(double a, double b, double c) { return a * b + c; } double evaluate_saturate(double a, double, double) { return (a < 0) ? 0 : (a > 1) ? 1 : a; } double evaluate_mix(double x, double y, double a) { return x * (1 - a) + y * a; } double evaluate_step(double e, double x, double) { return (x < e) ? 0 : 1; } double evaluate_mod(double a, double b, double) { return a - b * std::floor(sk_ieee_double_divide(a, b));
} double evaluate_smoothstep(double edge0, double edge1, double x) { double t = sk_ieee_double_divide(x - edge0, edge1 - edge0);
t = (t < 0) ? 0 : (t > 1) ? 1 : t; return t * t * (3.0 - 2.0 * t);
}
double evaluate_matrixCompMult(double x, double y, double) { return x * y; }
double evaluate_not(double a, double, double) { return !a; } double evaluate_sinh(double a, double, double) { return std::sinh(a); } double evaluate_cosh(double a, double, double) { return std::cosh(a); } double evaluate_tanh(double a, double, double) { return std::tanh(a); } double evaluate_trunc(double a, double, double) { return std::trunc(a); } double evaluate_round(double a, double, double) { // The semantics of std::remainder guarantee a rounded-to-even result here, regardless of the // current float-rounding mode. return a - std::remainder(a, 1.0);
} double evaluate_floatBitsToInt(double a, double, double) { return pun_value<float, int32_t> (a); } double evaluate_floatBitsToUint(double a, double, double) { return pun_value<float, uint32_t>(a); } double evaluate_intBitsToFloat(double a, double, double) { return pun_value<int32_t, float>(a); } double evaluate_uintBitsToFloat(double a, double, double) { return pun_value<uint32_t, float>(a); }
staticvoid extract_matrix(const Expression* expr, float mat[16]) {
size_t numSlots = expr->type().slotCount();
for (size_t index = 0; index < numSlots; ++index) {
mat[index] = *expr->getConstantValue(index);
}
}
static std::unique_ptr<Expression> optimize_intrinsic_call(const Context& context,
Position pos,
IntrinsicKind intrinsic, const ExpressionArray& argArray, const Type& returnType) { // Replace constant variables with their literal values.
IntrinsicArguments arguments = {};
SkASSERT(SkToSizeT(argArray.size()) <= arguments.size());
for (int index = 0; index < argArray.size(); ++index) {
arguments[index] = ConstantFolder::GetConstantValueForVariable(*argArray[index]);
}
auto Get = [&](int idx, int col) -> float { return *arguments[idx]->getConstantValue(col);
};
switch (intrinsic) { // 8.1 : Angle and Trigonometry Functions case k_radians_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_radians); case k_degrees_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_degrees); case k_sin_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_sin); case k_cos_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_cos); case k_tan_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_tan); case k_sinh_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_sinh); case k_cosh_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_cosh); case k_tanh_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_tanh); case k_asin_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_asin); case k_acos_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_acos); case k_atan_IntrinsicKind:
if (argArray.size() == 1) { return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_atan);
} else { return evaluate_pairwise_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_atan2);
} case k_asinh_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_asinh);
case k_acosh_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_acosh); case k_atanh_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_atanh); // 8.2 : Exponential Functions case k_pow_IntrinsicKind: return evaluate_pairwise_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_pow); case k_exp_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_exp); case k_log_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_log); case k_exp2_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_exp2); case k_log2_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_log2); case k_sqrt_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_sqrt); case k_inversesqrt_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_inversesqrt); // 8.3 : Common Functions case k_abs_IntrinsicKind: return evaluate_intrinsic_numeric(context, arguments, returnType,
Intrinsics::evaluate_abs); case k_sign_IntrinsicKind: return Intrinsics::evaluate_sign(context, arguments);
case k_floor_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_floor); case k_ceil_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_ceil); case k_fract_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_fract); case k_mod_IntrinsicKind: return evaluate_pairwise_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_mod); case k_min_IntrinsicKind: return evaluate_pairwise_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_min); case k_max_IntrinsicKind: return evaluate_pairwise_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_max); case k_clamp_IntrinsicKind: return evaluate_3_way_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_clamp); case k_fma_IntrinsicKind: return evaluate_3_way_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_fma); case k_saturate_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_saturate); case k_mix_IntrinsicKind:
if (arguments[2]->type().componentType().isBoolean()) { const SkSL::Type& numericType = arguments[0]->type().componentType();
if (numericType.isFloat()) {
type_check_expression<float>(*arguments[0]);
type_check_expression<float>(*arguments[1]);
} else if (numericType.isInteger()) {
type_check_expression<SKSL_INT>(*arguments[0]);
type_check_expression<SKSL_INT>(*arguments[1]);
} else if (numericType.isBoolean()) {
type_check_expression<bool>(*arguments[0]);
type_check_expression<bool>(*arguments[1]);
} else {
SkDEBUGFAILF("unsupported type %s", numericType.description().c_str()); return nullptr;
} return evaluate_n_way_intrinsic(context, arguments[0], arguments[1], arguments[2],
returnType, Intrinsics::evaluate_mix);
} else { return evaluate_3_way_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_mix);
} case k_step_IntrinsicKind: return evaluate_pairwise_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_step); case k_smoothstep_IntrinsicKind: return evaluate_3_way_intrinsic(context, arguments, returnType,
Intrinsics::evaluate_smoothstep); case k_trunc_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_trunc); case k_round_IntrinsicKind: // GLSL `round` documents its rounding mode as unspecified case k_roundEven_IntrinsicKind: // and is allowed to behave identically to `roundEven`. return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_round); case k_floatBitsToInt_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_floatBitsToInt); case k_floatBitsToUint_IntrinsicKind: return evaluate_intrinsic<float>(context, arguments, returnType,
Intrinsics::evaluate_floatBitsToUint); case k_intBitsToFloat_IntrinsicKind: return evaluate_intrinsic<SKSL_INT>(context, arguments, returnType,
Intrinsics::evaluate_intBitsToFloat); case k_uintBitsToFloat_IntrinsicKind: return evaluate_intrinsic<SKSL_INT>(context, arguments, returnType,
Intrinsics::evaluate_uintBitsToFloat); // 8.4 : Floating-Point Pack and Unpack Functions case k_packUnorm2x16_IntrinsicKind: { auto Pack = [&](int n) -> unsigned int {
float x = Get(0, n); return (int)std::round(Intrinsics::evaluate_clamp(x, 0.0, 1.0) * 65535.0);
}; constdouble packed = ((Pack(0) << 0) & 0x0000FFFF) |
((Pack(1) << 16) & 0xFFFF0000); return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
*context.fTypes.fUInt, &packed);
} case k_packSnorm2x16_IntrinsicKind: { auto Pack = [&](int n) -> unsigned int {
float x = Get(0, n); return (int)std::round(Intrinsics::evaluate_clamp(x, -1.0, 1.0) * 32767.0);
}; constdouble packed = ((Pack(0) << 0) & 0x0000FFFF) |
((Pack(1) << 16) & 0xFFFF0000); return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
*context.fTypes.fUInt, &packed);
} case k_packHalf2x16_IntrinsicKind: { auto Pack = [&](int n) -> unsigned int { return SkFloatToHalf(Get(0, n));
}; constdouble packed = ((Pack(0) << 0) & 0x0000FFFF) |
((Pack(1) << 16) & 0xFFFF0000); return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
*context.fTypes.fUInt, &packed);
} case k_unpackUnorm2x16_IntrinsicKind: {
SKSL_INT x = *arguments[0]->getConstantValue(0);
uint16_t a = ((x >> 0) & 0x0000FFFF);
uint16_t b = ((x >> 16) & 0x0000FFFF); constdouble unpacked[2] = {double(a) / 65535.0, double(b) / 65535.0}; return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
*context.fTypes.fFloat2, unpacked);
} case k_unpackSnorm2x16_IntrinsicKind: {
SKSL_INT x = *arguments[0]->getConstantValue(0);
int16_t a = ((x >> 0) & 0x0000FFFF);
int16_t b = ((x >> 16) & 0x0000FFFF); constdouble unpacked[2] = {Intrinsics::evaluate_clamp(double(a) / 32767.0, -1.0, 1.0),
Intrinsics::evaluate_clamp(double(b) / 32767.0, -1.0, 1.0)}; return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
*context.fTypes.fFloat2, unpacked);
} case k_unpackHalf2x16_IntrinsicKind: {
SKSL_INT x = *arguments[0]->getConstantValue(0);
uint16_t a = ((x >> 0) & 0x0000FFFF);
uint16_t b = ((x >> 16) & 0x0000FFFF); constdouble unpacked[2] = {SkHalfToFloat(a),
SkHalfToFloat(b)}; return ConstructorCompound::MakeFromConstants(context, arguments[0]->fPosition,
*context.fTypes.fFloat2, unpacked);
} // 8.5 : Geometric Functions case k_length_IntrinsicKind: return Intrinsics::evaluate_length(arguments);
case k_distance_IntrinsicKind: return Intrinsics::evaluate_distance(arguments);
case k_dot_IntrinsicKind: return Intrinsics::evaluate_dot(arguments);
case k_cross_IntrinsicKind: { auto X = [&](int n) -> float { return Get(0, n); }; auto Y = [&](int n) -> float { return Get(1, n); };
SkASSERT(arguments[0]->type().columns() == 3); // the vec2 form is not a real intrinsic
std::string FunctionCall::description(OperatorPrecedence) const {
std::string result = std::string(this->function().name()) + "("; auto separator = SkSL::String::Separator();
for (const std::unique_ptr<Expression>& arg : this->arguments()) {
result += separator();
result += arg->description(OperatorPrecedence::kSequence);
}
result += ")"; return result;
}
staticbool argument_and_parameter_flags_match(const Expression& argument, const Variable& parameter) { // If the function parameter has a pixel format, the argument being passed in must have a // matching pixel format.
LayoutFlags paramPixelFormat = parameter.layout().fFlags & LayoutFlag::kAllPixelFormats;
if (paramPixelFormat != LayoutFlag::kNone) { // The only SkSL type that supports pixel-format qualifiers is a storage texture.
if (parameter.type().isStorageTexture()) { // Storage textures are opaquely typed, so there's no way to specify one other than by // directly accessing a variable.
if (!argument.is<VariableReference>()) { return false;
}
// The variable's pixel-format flags must match. (Only one pixel-format bit can be set.) const Variable& var = *argument.as<VariableReference>().variable();
if ((var.layout().fFlags & LayoutFlag::kAllPixelFormats) != paramPixelFormat) { return false;
}
}
}
// The only other supported parameter flags are `const` and `in/out`, which do not allow // multiple overloads. returntrue;
}
/** *Usedtodeterminethebestoverloadforafunctioncallbycalculatingthecostofcoercingthe *argumentsofthefunctiontotherequiredtypes.Costhasnoparticularmeaningotherthan"lower *costsarepreferred".ReturnsCoercionCost::Impossible()ifthecallisnotvalid.Thisisnever *calledforfunctionswithonlyonedefinition.
*/ static CoercionCost call_cost(const Context& context, const FunctionDeclaration& function, const ExpressionArray& arguments) { // Strict-ES2 programs can never call an `$es3` function.
if (context.fConfig->strictES2Mode() && function.modifierFlags().isES3()) { return CoercionCost::Impossible();
} // Functions with the wrong number of parameters are never a match.
if (function.parameters().size() != SkToSizeT(arguments.size())) { return CoercionCost::Impossible();
} // If the arguments cannot be coerced to the parameter types, the function is never a match.
FunctionDeclaration::ParamTypes types; const Type* ignored;
if (!function.determineFinalTypes(arguments, &types, &ignored)) { return CoercionCost::Impossible();
} // If the arguments do not match the parameter types due to mismatched modifiers, the function // is never a match.
for (int i = 0; i < arguments.size(); i++) { const Expression& arg = *arguments[i]; const Variable& param = *function.parameters()[i];
if (!argument_and_parameter_flags_match(arg, param)) { return CoercionCost::Impossible();
}
} // Return the sum of coercion costs of each argument.
CoercionCost total = CoercionCost::Free();
for (int i = 0; i < arguments.size(); i++) {
total = total + arguments[i]->coercionCost(*types[i]);
} return total;
}
const FunctionDeclaration* FunctionCall::FindBestFunctionForCall( const Context& context, const FunctionDeclaration* overloadChain, const ExpressionArray& arguments) {
if (!overloadChain->nextOverload()) { return overloadChain;
}
CoercionCost bestCost = CoercionCost::Impossible(); const FunctionDeclaration* best = nullptr;
for (const FunctionDeclaration* f = overloadChain; f != nullptr; f = f->nextOverload()) {
CoercionCost cost = call_cost(context, *f, arguments);
if (cost <= bestCost) {
bestCost = cost;
best = f;
}
} return bestCost.fImpossible ? nullptr : best;
}
static std::string build_argument_type_list(SkSpan<const std::unique_ptr<Expression>> arguments) {
std::string result = "("; auto separator = SkSL::String::Separator();
for (const std::unique_ptr<Expression>& arg : arguments) {
result += separator();
result += arg->type().displayName();
} return result + ")";
}
std::unique_ptr<Expression> FunctionCall::Convert(const Context& context,
Position pos,
std::unique_ptr<Expression> functionValue,
ExpressionArray arguments) { switch (functionValue->kind()) { case Expression::Kind::kTypeReference: return Constructor::Convert(context,
pos,
functionValue->as<TypeReference>().value(),
std::move(arguments)); case Expression::Kind::kFunctionReference: { const FunctionReference& ref = functionValue->as<FunctionReference>(); const FunctionDeclaration* best = FindBestFunctionForCall(context, ref.overloadChain(),
arguments);
if (best) { return FunctionCall::Convert(context, pos, *best, std::move(arguments));
}
std::string msg = "no match for " + std::string(ref.overloadChain()->name()) +
build_argument_type_list(arguments);
context.fErrors->error(pos, msg); return nullptr;
} case Expression::Kind::kMethodReference: {
MethodReference& ref = functionValue->as<MethodReference>();
arguments.push_back(std::move(ref.self()));
const FunctionDeclaration* best = FindBestFunctionForCall(context, ref.overloadChain(),
arguments);
if (best) { return FunctionCall::Convert(context, pos, *best, std::move(arguments));
}
std::string msg = "no match for " + arguments.back()->type().displayName() + "::" + std::string(ref.overloadChain()->name().substr(1)) +
build_argument_type_list(SkSpan(arguments).first(arguments.size() - 1));
context.fErrors->error(pos, msg); return nullptr;
} case Expression::Kind::kPoison:
functionValue->fPosition = pos; return functionValue; default:
context.fErrors->error(pos, "not a function"); return nullptr;
}
}
std::unique_ptr<Expression> FunctionCall::Convert(const Context& context,
Position pos, const FunctionDeclaration& function,
ExpressionArray arguments) { // Reject ES3 function calls in strict ES2 mode.
if (context.fConfig->strictES2Mode() && function.modifierFlags().isES3()) {
context.fErrors->error(pos, "call to '" + function.description() + "' is not supported"); return nullptr;
}
// Reject function calls with the wrong number of arguments.
if (function.parameters().size() != SkToSizeT(arguments.size())) {
std::string msg = "call to '" + std::string(function.name()) + "' expected " +
std::to_string(function.parameters().size()) + " argument";
if (function.parameters().size() != 1) {
msg += "s";
}
msg += ", but found " + std::to_string(arguments.size());
context.fErrors->error(pos, msg); return nullptr;
}
// If the arguments do not match the parameter types due to mismatched modifiers, reject the // function call.
for (int i = 0; i < arguments.size(); i++) { const Expression& arg = *arguments[i]; const Variable& param = *function.parameters()[i];
if (!argument_and_parameter_flags_match(arg, param)) {
context.fErrors->error(arg.position(), "expected argument of type '" +
param.layout().paddedDescription() +
param.modifierFlags().paddedDescription() +
param.type().description() + "'"); return nullptr;
}
}
// Resolve generic types.
FunctionDeclaration::ParamTypes types; const Type* returnType;
if (!function.determineFinalTypes(arguments, &types, &returnType)) {
std::string msg = "no match for " + std::string(function.name()) +
build_argument_type_list(arguments);
context.fErrors->error(pos, msg); return nullptr;
}
for (int i = 0; i < arguments.size(); i++) { // Coerce each argument to the proper type.
arguments[i] = types[i]->coerceExpression(std::move(arguments[i]), context);
if (!arguments[i]) { return nullptr;
} // Update the refKind on out-parameters, and ensure that they are actually assignable.
ModifierFlags paramFlags = function.parameters()[i]->modifierFlags();
if (paramFlags & ModifierFlag::kOut) { const VariableRefKind refKind = (paramFlags & ModifierFlag::kIn)
? VariableReference::RefKind::kReadWrite
: VariableReference::RefKind::kPointer;
if (!Analysis::UpdateVariableRefKind(arguments[i].get(), refKind, context.fErrors)) { return nullptr;
}
}
}
if (function.isMain()) {
context.fErrors->error(pos, "call to 'main' is not allowed"); return nullptr;
}
if (function.intrinsicKind() == k_eval_IntrinsicKind) { // This is a method call on an effect child. Translate it into a ChildCall, which simplifies // handling in the generators and analysis code. const Variable& child = *arguments.back()->as<VariableReference>().variable();
arguments.pop_back(); return ChildCall::Make(context, pos, returnType, child, std::move(arguments));
}
// We might be able to optimize built-in intrinsics.
if (function.isIntrinsic() && has_compile_time_constant_arguments(arguments)) { // The function is an intrinsic and all inputs are compile-time constants. Optimize it.
if (std::unique_ptr<Expression> expr = optimize_intrinsic_call(context,
pos,
function.intrinsicKind(),
arguments,
*returnType)) {
expr->fPosition = pos; return expr;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.