286 lines
11 KiB
C++
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
|