Patrick Kelley 8fd444092b initial
2025-05-07 15:35:15 -04:00

286 lines
11 KiB
C++

// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
#pragma once
#include <string>
#include <utility>
#include <hilti/rt/3rdparty/ArticleEnumClass-v2/EnumClass.h>
#include <hilti/ast/ctor.h>
#include <hilti/ast/expression.h>
#include <hilti/ast/operator.h>
#include <hilti/ast/type.h>
#include <hilti/base/util.h>
namespace hilti {
/** Tunes the specifics of a type coercion operation. */
enum class CoercionStyle {
/**
* Specifies that coercion is taking place in the context of a assignment
* of the source expression to a variable of the destination type.
*/
Assignment = (1U << 0U),
/**
* Specifies that coercion is taking place in the context of passing the
* source expression to a function parameter of the target type.
*/
FunctionCall = (1U << 2U),
/**
* Let coercion succeed if the types fully match. (You probably always
* want this).
*/
TryExactMatch = (1U << 3U),
/**
* Let coercion succeed if the source type can be converted into the
* destination type by a legal constness change.
*/
TryConstPromotion = (1U << 4U),
/**
* Let coercion succeed if the source type can be converted into the
* destination type by any of the plugins' provided type coercions. (This
* is the main path to performing actual coercions that change types.)
*/
TryCoercion = (1U << 5U),
/**
* Same as `TryCoercion` but limits the search to coercions where the base
* type doesn't change. In other words, limits coercions to changing the
* type's parameters. This is usually tried before general coercions across
* types.
*/
TryCoercionWithinSameType = (1U << 6U),
/** Never allow any substantial type changes. */
DisallowTypeChanges = (1U << 7U),
/**
* Signal that the coercion takes place in a semantic language context
* expecting the given destination type. This can be used to support
* coercions at locations where normally it wouldn't take place, such as
* conversion to bool in conditional statements.
*/
ContextualConversion = (1U << 8U),
/** Deref any references to try getting coercion to succeed. */
TryDeref = (1U << 9U),
/**
* Shortcut style activating all possible coercions in the context of an
* assignment.
*/
TryAllForAssignment = Assignment | TryExactMatch | TryConstPromotion | TryCoercion | TryDeref,
/**
* Shortcut style allowing for direct matches only in the context of
* operator resolution.
*/
TryDirectForMatching = TryExactMatch | TryConstPromotion,
/**
* Shortcut style activating all possible coercions in the context of
* operator resolution.
*/
TryAllForMatching = TryDirectForMatching | TryCoercion,
/**
* Shortcut style activating possible coercions in the context of
* function parameter passing, however without allowing any type changes.
*/
TryDirectMatchForFunctionCall = FunctionCall | TryExactMatch | TryConstPromotion,
/**
* Shortcut style activating all possible coercions in the context of
* function parameter passing.
*/
TryAllForFunctionCall = TryDirectMatchForFunctionCall | TryCoercion | TryDeref,
};
/**
* Returns a readable representation of a coercion style setting for debugging
* purposes.
*/
extern std::string to_string(bitmask<CoercionStyle> style);
} // namespace hilti
enableEnumClassBitmask(hilti::CoercionStyle); // Must be in global scope
namespace hilti {
/** Return type for the functions doing expression coercion. */
struct CoercedExpression {
/** Returns true if coercion was successful. */
operator bool() const { return coerced.hasValue(); }
/**
* Coerced expression if successful, an error if not. This will be set
* even if the coerced expression ends up being identical to the source
* expression.
*/
Result<Expression*> coerced;
/**
* Coerced expression if successful and the coerced expression is not
* identical to original one; unset otherwise.
*/
Expression* nexpr = {};
/**
* If coerced is set, true if type of new expression's type is to be
* considered changed compared to source expression's type for overload
* resolution
*/
bool consider_type_changed = false;
/**
*
* Represents a successful coercion that led the source expression not
* changing, which will be assigned to the `coerced` field.
*
* @note The expression not changing doesn't necessarily mean that
* the expression's type is *exactly* matching the coercion's destination
* type. However, even if not, the caller should proceed by using the
* `coerced` field value for anywhere where the coerced expression is
* expected.
*
* @param src the original source expression
*/
CoercedExpression(Expression* src) : coerced(src) {}
/**
* Represents a successful coercion that led to a new expression
* different from the source expression.
*
* @param src the original source expression's type
* @param coerced the resulting expression that *src* was coerced to
*/
CoercedExpression(QualifiedType* src, Expression* coerced)
: coerced(coerced),
nexpr(coerced),
consider_type_changed(src->type()->typeClass() != coerced->type()->type()->typeClass()) {}
/** Represents an unsuccessful coercion. */
CoercedExpression() = default;
/**
* Represents an unsuccessful coercion, carrying an error message along
* explaining why it failed.
*/
CoercedExpression(const result::Error& error) : coerced(error) {}
};
/**
* Coerces an expression to a given target type. This returns a struct with
* fields that provide result of the coercion, along with additional meta
* information. Depending on the coercion style, a coerced expression may be
* the exact same expression as passed in if types match sufficiently.
*
* @note This function does not actually *perform* the coercion, it just
* returns an AST of the specified target type that will let the compiler
* later carry out the coercion (usually that'll be a `expression::Coerced``
* node).
*
* @param builder builder to use
* @param e expression to coerce
* @param dst target type
* @param style coercion style to use, given as a bitmask of any style
* specifiers that apply
* @return the *result* will evaluate to true if coercion was successful; if
* so, the contained fields will provide more information
*/
CoercedExpression coerceExpression(Builder* builder, Expression* e, QualifiedType* dst,
bitmask<CoercionStyle> style = CoercionStyle::TryAllForAssignment, bool lhs = false);
/**
* Coerces an expression to a given target type. This returns a struct with
* fields that provide result of the coercion, along with additional meta
* information. Depending on the coercion style, a coerced expression may be
* the exact same expression as passed in if types match sufficiently.
*
* @note This function does not actually *perform* the coercion, it just
* returns an AST of the specified target type that will let the compiler
* later carry out the coercion (usually that'll be a `expression::Coerced``
* node).
*
* @param builder builder to use
* @param e expression to coerce
* @param src explicitly specified source type; this can be different from
* the type of *e* and will be used instead of that
* @param dst target type
* @param style coercion style to use, given as a bitmask of any style
* specifiers that apply
* @return the *result* will evaluate to true if coercion was successful; if
* so, the contained fields will provide more information
*/
CoercedExpression coerceExpression(Builder* builder, Expression* e, QualifiedType* src_, QualifiedType* dst_,
bitmask<CoercionStyle> style = CoercionStyle::TryAllForAssignment, bool lhs = false);
/**
* Matches a set of expressions against a set of operands, coercing them as
* needed. This takes into account specifics of the operands, such as them
* being optional or having defaults.
*
* @param builder builder to use
* @param kind kind of operator the operands are for
* @param exprs source expressions to match against the operands
* @param operands operands to match against
* @param style coercion style to use for each expression's coercion to its
* operand, given as a bitmask of any style
* specifiers that apply
* @return If successful, a pair with a boolean as its 1st argument that
* indicates whether any of the expressions was changed; and as its 2nd
* element, the coerced expressions now matching the operands. The returned
* vector will have defaults filled in for missing expressions where
* available (missing expressions for optional operands without defaults will
* remain left out). If unsuccessful, an error.
*/
Result<std::pair<bool, Expressions>> coerceOperands(Builder* builder, operator_::Kind kind, const Expressions& exprs,
const operator_::Operands& operands, bitmask<CoercionStyle> style);
/**
* Coerces a constructor to a given target type. This returns the coerced
* constructor, now of the new type. If the constructor is already of the
* right type, it will just be returned back.
*
* @param builder builder to use
* @param c ctor to coerce
* @param dst target type
* @param style coercion style to use
* @return if the coercion was successful, the returned new value (which may be the same as the old)
*/
Result<Ctor*> coerceCtor(Builder* builder, Ctor* c, QualifiedType* dst,
bitmask<CoercionStyle> style = CoercionStyle::TryAllForAssignment);
/**
* Coerces a source type to a given target type. This returns the coerced
* type. If the type is already of the right type, it will just be returned
* back.
*
* @param builder builder to use
* @param c ctor to coerce
* @param dst target type
* @param style coercion style to use
* @return if the coercion was successful, the returned new value (which may be the same as the old)
*/
Result<QualifiedType*> coerceType(Builder* builder, QualifiedType* src_, QualifiedType* dst_,
bitmask<CoercionStyle> style = CoercionStyle::TryAllForAssignment);
namespace coercer::detail {
/** Implements the corresponding functionality for the default HILTI compiler plugin. */
Ctor* coerceCtor(Builder* builder, Ctor* c, QualifiedType* dst, bitmask<CoercionStyle> style);
/** Implements the corresponding functionality for the default HILTI compiler plugin. */
QualifiedType* coerceType(Builder* builder, QualifiedType* t, QualifiedType* dst, bitmask<CoercionStyle> style);
} // namespace coercer::detail
} // namespace hilti