Switch the prelude to use ref instead of addr (#6359)

This commit is contained in:
Geoff Romer
2025-11-14 00:40:26 +00:00
committed by GitHub
parent 55e5675373
commit 2b8fdf3417
199 changed files with 1757 additions and 2576 deletions
+122 -73
View File
@@ -14,7 +14,7 @@
namespace Carbon::SemIR {
// A function that validates that a builtin was declared properly.
using ValidateFn = auto(const File& sem_ir, llvm::ArrayRef<TypeId> arg_types,
using ValidateFn = auto(const File& sem_ir, llvm::ArrayRef<InstId> call_params,
TypeId return_type) -> bool;
namespace {
@@ -35,21 +35,33 @@ struct ValidateState {
TypeId type_params[MaxTypeParams] = {TypeId::None, TypeId::None};
};
template <typename TypeConstraint>
auto Check(const File& sem_ir, ValidateState& state, TypeId type_id) -> bool;
// A constraint that applies to types.
template <typename T>
concept TypeConstraint =
requires(const File& sem_ir, ValidateState& state, TypeId type_id) {
{ T::CheckType(sem_ir, state, type_id) } -> std::convertible_to<bool>;
};
// A constraint that applies to parameters.
template <typename T>
concept ParamConstraint =
requires(const File& sem_ir, ValidateState& state, InstId param_id) {
{ T::CheckParam(sem_ir, state, param_id) } -> std::convertible_to<bool>;
};
// Constraint that a type is generic type parameter `I` of the builtin,
// satisfying `TypeConstraint`. See ValidateSignature for details.
template <int I, typename TypeConstraint>
// satisfying `Constraint`. See ValidateSignature for details.
template <int I, typename Constraint>
requires TypeConstraint<Constraint>
struct TypeParam {
static_assert(I >= 0 && I < MaxTypeParams);
static auto Check(const File& sem_ir, ValidateState& state, TypeId type_id)
-> bool {
static auto CheckType(const File& sem_ir, ValidateState& state,
TypeId type_id) -> bool {
if (state.type_params[I].has_value() && type_id != state.type_params[I]) {
return false;
}
if (!TypeConstraint::Check(sem_ir, state, type_id)) {
if (!Constraint::CheckType(sem_ir, state, type_id)) {
return false;
}
state.type_params[I] = type_id;
@@ -61,8 +73,8 @@ struct TypeParam {
// details.
template <const TypeInstId& BuiltinId>
struct BuiltinType {
static auto Check(const File& sem_ir, ValidateState& /*state*/,
TypeId type_id) -> bool {
static auto CheckType(const File& sem_ir, ValidateState& /*state*/,
TypeId type_id) -> bool {
return sem_ir.types().GetInstId(type_id) == BuiltinId;
}
};
@@ -70,27 +82,29 @@ struct BuiltinType {
// Constraint that a type is a pointer to another type. See ValidateSignature
// for details.
template <typename PointeeT>
requires TypeConstraint<PointeeT>
struct PointerTo {
static auto Check(const File& sem_ir, ValidateState& state, TypeId type_id)
-> bool {
static auto CheckType(const File& sem_ir, ValidateState& state,
TypeId type_id) -> bool {
if (!sem_ir.types().Is<PointerType>(type_id)) {
return false;
}
return Check<PointeeT>(sem_ir, state, sem_ir.GetPointeeType(type_id));
return CheckType<PointeeT>(sem_ir, state, sem_ir.GetPointeeType(type_id));
}
};
// Constraint that a type is MaybeUnformed<T>.
template <typename T>
requires TypeConstraint<T>
struct MaybeUnformed {
static auto Check(const File& sem_ir, ValidateState& state, TypeId type_id)
-> bool {
static auto CheckType(const File& sem_ir, ValidateState& state,
TypeId type_id) -> bool {
auto maybe_unformed =
sem_ir.types().TryGetAs<SemIR::MaybeUnformedType>(type_id);
if (!maybe_unformed) {
return false;
}
return Check<T>(
return CheckType<T>(
sem_ir, state,
sem_ir.types().GetTypeIdForTypeInstId(maybe_unformed->inner_id));
}
@@ -99,8 +113,8 @@ struct MaybeUnformed {
// Constraint that a type is `()`, used as the return type of builtin functions
// with no return value.
struct NoReturn {
static auto Check(const File& sem_ir, ValidateState& /*state*/,
TypeId type_id) -> bool {
static auto CheckType(const File& sem_ir, ValidateState& /*state*/,
TypeId type_id) -> bool {
auto tuple = sem_ir.types().TryGetAs<TupleType>(type_id);
if (!tuple) {
return false;
@@ -117,8 +131,8 @@ using CharLiteral = BuiltinType<CharLiteralType::TypeInstId>;
// Constraint that a type is `u8` or an adapted type, including `Core.Char`.
struct CharCompatible {
static auto Check(const File& sem_ir, ValidateState& /*state*/,
TypeId type_id) -> bool {
static auto CheckType(const File& sem_ir, ValidateState& /*state*/,
TypeId type_id) -> bool {
auto int_info = sem_ir.types().TryGetIntTypeInfo(type_id);
if (!int_info) {
// Not an integer.
@@ -134,8 +148,8 @@ struct CharCompatible {
// Constraint that requires the type to be a sized integer type.
struct AnySizedInt {
static auto Check(const File& sem_ir, ValidateState& /*state*/,
TypeId type_id) -> bool {
static auto CheckType(const File& sem_ir, ValidateState& /*state*/,
TypeId type_id) -> bool {
return sem_ir.types().Is<IntType>(type_id);
}
};
@@ -143,18 +157,18 @@ struct AnySizedInt {
// Constraint that requires the type to be an integer type: either a sized
// integer type or a literal.
struct AnyInt {
static auto Check(const File& sem_ir, ValidateState& state, TypeId type_id)
-> bool {
return AnySizedInt::Check(sem_ir, state, type_id) ||
BuiltinType<IntLiteralType::TypeInstId>::Check(sem_ir, state,
type_id);
static auto CheckType(const File& sem_ir, ValidateState& state,
TypeId type_id) -> bool {
return AnySizedInt::CheckType(sem_ir, state, type_id) ||
BuiltinType<IntLiteralType::TypeInstId>::CheckType(sem_ir, state,
type_id);
}
};
// Constraint that requires the type to be a sized floating-point type.
struct AnySizedFloat {
static auto Check(const File& sem_ir, ValidateState& /*state*/,
TypeId type_id) -> bool {
static auto CheckType(const File& sem_ir, ValidateState& /*state*/,
TypeId type_id) -> bool {
return sem_ir.types().Is<FloatType>(type_id);
}
};
@@ -162,18 +176,18 @@ struct AnySizedFloat {
// Constraint that requires the type to be a float type: either a sized float
// type or a literal.
struct AnyFloat {
static auto Check(const File& sem_ir, ValidateState& state, TypeId type_id)
-> bool {
return AnySizedFloat::Check(sem_ir, state, type_id) ||
BuiltinType<FloatLiteralType::TypeInstId>::Check(sem_ir, state,
type_id);
static auto CheckType(const File& sem_ir, ValidateState& state,
TypeId type_id) -> bool {
return AnySizedFloat::CheckType(sem_ir, state, type_id) ||
BuiltinType<FloatLiteralType::TypeInstId>::CheckType(sem_ir, state,
type_id);
}
};
// Constraint that allows an arbitrary type.
struct AnyType {
static auto Check(const File& /*sem_ir*/, ValidateState& /*state*/,
TypeId /*type_id*/) -> bool {
static auto CheckType(const File& /*sem_ir*/, ValidateState& /*state*/,
TypeId /*type_id*/) -> bool {
return true;
}
};
@@ -184,20 +198,35 @@ using Type = BuiltinType<TypeType::TypeInstId>;
// Constraint that a type supports a primitive copy. This happens if its
// initializing representation is a copy of its value representation.
struct PrimitiveCopyable {
static auto Check(const File& sem_ir, ValidateState& /*state*/,
TypeId type_id) -> bool {
static auto CheckType(const File& sem_ir, ValidateState& /*state*/,
TypeId type_id) -> bool {
return InitRepr::ForType(sem_ir, type_id).IsCopyOfObjectRepr() &&
ValueRepr::ForType(sem_ir, type_id)
.IsCopyOfObjectRepr(sem_ir, type_id);
}
};
// Constraint that a parameter is passed by reference.
template <typename Constraint>
requires TypeConstraint<Constraint>
struct ByRef {
static auto CheckParam(const File& sem_ir, ValidateState& state,
InstId param_id) -> bool {
if (auto ref_param = sem_ir.insts().TryGetAs<RefParam>(param_id)) {
return CheckType<Constraint>(sem_ir, state, ref_param->type_id);
}
return false;
}
};
// Checks that the specified type matches the given type constraint.
template <typename TypeConstraint>
auto Check(const File& sem_ir, ValidateState& state, TypeId type_id) -> bool {
template <typename Constraint>
requires TypeConstraint<Constraint>
auto CheckType(const File& sem_ir, ValidateState& state, TypeId type_id)
-> bool {
while (type_id.has_value()) {
// Allow a type that satisfies the constraint.
if (TypeConstraint::Check(sem_ir, state, type_id)) {
if (Constraint::CheckType(sem_ir, state, type_id)) {
return true;
}
@@ -207,6 +236,27 @@ auto Check(const File& sem_ir, ValidateState& state, TypeId type_id) -> bool {
return false;
}
// CheckParam<Constraint> checks that param_id (which must belong to
// the AnyParam category) matches the given parameter constraint. A type
// constraint is interpreted as a parameter constraint that requires param_id to
// be a ValueParam whose type matches the type constraint.
template <typename Constraint>
requires ParamConstraint<Constraint>
auto CheckParam(const File& sem_ir, ValidateState& state, InstId param_id)
-> bool {
return Constraint::CheckParam(sem_ir, state, param_id);
}
template <typename Constraint>
requires TypeConstraint<Constraint>
auto CheckParam(const File& sem_ir, ValidateState& state, InstId param_id)
-> bool {
if (auto param_pattern = sem_ir.insts().TryGetAs<ValueParam>(param_id)) {
return CheckType<Constraint>(sem_ir, state, param_pattern->type_id);
}
return false;
}
} // namespace
// Validates that this builtin has a signature matching the specified signature.
@@ -217,7 +267,7 @@ auto Check(const File& sem_ir, ValidateState& state, TypeId type_id) -> bool {
// value of a third integer type. Types used within the signature should provide
// a `Check` function that validates that the Carbon type is expected:
//
// auto Check(const File&, ValidateState&, TypeId) -> bool;
// auto CheckType(const File&, ValidateState&, TypeId) -> bool;
//
// To constrain that the same type is used in multiple places in the signature,
// `TypeParam<I, T>` can be used. For example:
@@ -229,27 +279,28 @@ auto Check(const File& sem_ir, ValidateState& state, TypeId type_id) -> bool {
// are used in the descriptions of the builtins.
template <typename SignatureFnType>
static auto ValidateSignature(const File& sem_ir,
llvm::ArrayRef<TypeId> arg_types,
llvm::ArrayRef<InstId> call_params,
TypeId return_type) -> bool {
using SignatureTraits = llvm::function_traits<SignatureFnType*>;
ValidateState state;
// Must have expected number of arguments.
if (arg_types.size() != SignatureTraits::num_args) {
if (call_params.size() != SignatureTraits::num_args) {
return false;
}
// Argument types must match.
if (![&]<size_t... Indexes>(std::index_sequence<Indexes...>) {
return ((Check<typename SignatureTraits::template arg_t<Indexes>>(
sem_ir, state, arg_types[Indexes])) &&
return ((CheckParam<typename SignatureTraits::template arg_t<Indexes>>(
sem_ir, state, call_params[Indexes])) &&
...);
}(std::make_index_sequence<SignatureTraits::num_args>())) {
return false;
}
// Result type must match.
if (!Check<typename SignatureTraits::result_t>(sem_ir, state, return_type)) {
if (!CheckType<typename SignatureTraits::result_t>(sem_ir, state,
return_type)) {
return false;
}
@@ -259,10 +310,10 @@ static auto ValidateSignature(const File& sem_ir,
// Validates the signature for NoOp. This ignores all arguments, only validating
// that the return type is compatible.
static auto ValidateNoOpSignature(const File& sem_ir,
llvm::ArrayRef<TypeId> /*arg_types*/,
llvm::ArrayRef<InstId> /*call_params*/,
TypeId return_type) -> bool {
ValidateState state;
return Check<NoReturn>(sem_ir, state, return_type);
return CheckType<NoReturn>(sem_ir, state, return_type);
}
// Descriptions of builtin functions follow. For each builtin, a corresponding
@@ -443,77 +494,77 @@ constexpr BuiltinInfo IntRightShift = {
// "int.sadd_assign": integer in-place addition.
constexpr BuiltinInfo IntSAddAssign = {
"int.sadd_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntT)->NoReturn>};
// "int.ssub_assign": integer in-place subtraction.
constexpr BuiltinInfo IntSSubAssign = {
"int.ssub_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntT)->NoReturn>};
// "int.smul_assign": integer in-place multiplication.
constexpr BuiltinInfo IntSMulAssign = {
"int.smul_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntT)->NoReturn>};
// "int.sdiv_assign": integer in-place division.
constexpr BuiltinInfo IntSDivAssign = {
"int.sdiv_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntT)->NoReturn>};
// "int.smod_assign": integer in-place modulo.
constexpr BuiltinInfo IntSModAssign = {
"int.smod_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntT)->NoReturn>};
// "int.uadd_assign": unsigned integer in-place addition.
constexpr BuiltinInfo IntUAddAssign = {
"int.uadd_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntT)->NoReturn>};
// "int.usub_assign": unsigned integer in-place subtraction.
constexpr BuiltinInfo IntUSubAssign = {
"int.usub_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntT)->NoReturn>};
// "int.umul_assign": unsigned integer in-place multiplication.
constexpr BuiltinInfo IntUMulAssign = {
"int.umul_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntT)->NoReturn>};
// "int.udiv_assign": unsigned integer in-place division.
constexpr BuiltinInfo IntUDivAssign = {
"int.udiv_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntT)->NoReturn>};
// "int.mod_assign": integer in-place modulo.
constexpr BuiltinInfo IntUModAssign = {
"int.umod_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntT)->NoReturn>};
// "int.and_assign": integer in-place bitwise and.
constexpr BuiltinInfo IntAndAssign = {
"int.and_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntT)->NoReturn>};
// "int.or_assign": integer in-place bitwise or.
constexpr BuiltinInfo IntOrAssign = {
"int.or_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntT)->NoReturn>};
// "int.xor_assign": integer in-place bitwise xor.
constexpr BuiltinInfo IntXorAssign = {
"int.xor_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntT)->NoReturn>};
// "int.left_shift_assign": integer in-place left shift.
constexpr BuiltinInfo IntLeftShiftAssign = {
"int.left_shift_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntU)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntU)->NoReturn>};
// "int.right_shift_assign": integer in-place right shift.
constexpr BuiltinInfo IntRightShiftAssign = {
"int.right_shift_assign",
ValidateSignature<auto(PointerTo<SizedIntT>, SizedIntU)->NoReturn>};
ValidateSignature<auto(ByRef<SizedIntT>, SizedIntU)->NoReturn>};
// "int.eq": integer equality comparison.
constexpr BuiltinInfo IntEq = {"int.eq",
@@ -566,22 +617,22 @@ constexpr BuiltinInfo FloatDiv = {
// "float.add_assign": float in-place addition.
constexpr BuiltinInfo FloatAddAssign = {
"float.add_assign",
ValidateSignature<auto(PointerTo<SizedFloatT>, SizedFloatT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedFloatT>, SizedFloatT)->NoReturn>};
// "float.sub_assign": float in-place subtraction.
constexpr BuiltinInfo FloatSubAssign = {
"float.sub_assign",
ValidateSignature<auto(PointerTo<SizedFloatT>, SizedFloatT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedFloatT>, SizedFloatT)->NoReturn>};
// "float.mul_assign": float in-place multiplication.
constexpr BuiltinInfo FloatMulAssign = {
"float.mul_assign",
ValidateSignature<auto(PointerTo<SizedFloatT>, SizedFloatT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedFloatT>, SizedFloatT)->NoReturn>};
// "float.div_assign": float in-place division.
constexpr BuiltinInfo FloatDivAssign = {
"float.div_assign",
ValidateSignature<auto(PointerTo<SizedFloatT>, SizedFloatT)->NoReturn>};
ValidateSignature<auto(ByRef<SizedFloatT>, SizedFloatT)->NoReturn>};
// Converts between floating-point types, with a diagnostic if the value doesn't
// fit.
@@ -639,10 +690,8 @@ constexpr BuiltinInfo TypeAnd = {"type.and",
// The implementation of `Destroy` for a type. The argument must be checked with
// `type.can_destroy` first.
// TODO: The argument should be `addr self: Self*`. Consider modifying
// `ValidateSignature` to more fully enforce the structure.
constexpr BuiltinInfo TypeDestroy = {
"type.destroy", ValidateSignature<auto(PointerTo<AnyType>)->NoReturn>};
"type.destroy", ValidateSignature<auto(ByRef<AnyType>)->NoReturn>};
// Returns a facet type that's used to determine whether a type can use
// `type.destroy`.
@@ -670,14 +719,14 @@ auto BuiltinFunctionKind::ForBuiltinName(llvm::StringRef name)
}
auto BuiltinFunctionKind::IsValidType(const File& sem_ir,
llvm::ArrayRef<TypeId> arg_types,
llvm::ArrayRef<InstId> call_params,
TypeId return_type) const -> bool {
static constexpr ValidateFn* ValidateFns[] = {
#define CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(Name) \
BuiltinFunctionInfo::Name.validate,
#include "toolchain/sem_ir/builtin_function_kind.def"
};
return ValidateFns[AsInt()](sem_ir, arg_types, return_type);
return ValidateFns[AsInt()](sem_ir, call_params, return_type);
}
static auto IsLiteralType(const File& sem_ir, TypeId type_id) -> bool {