convert.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #ifndef CARBON_TOOLCHAIN_CHECK_CONVERT_H_
  5. #define CARBON_TOOLCHAIN_CHECK_CONVERT_H_
  6. #include "toolchain/check/context.h"
  7. #include "toolchain/check/pending_block.h"
  8. #include "toolchain/sem_ir/entity_with_params_base.h"
  9. #include "toolchain/sem_ir/ids.h"
  10. namespace Carbon::Check {
  11. // Description of the target of a conversion.
  12. struct ConversionTarget {
  13. enum Kind : int8_t {
  14. // Perform no conversion. The source expression must already have type
  15. // `type_id`.
  16. NoOp,
  17. // Convert to a value of type `type_id`.
  18. Value,
  19. // Convert to either a value or a reference of type `type_id`.
  20. ValueOrRef,
  21. // Convert to a durable reference of type `type_id`.
  22. DurableRef,
  23. // Convert to a reference, suitable for binding to a reference parameter.
  24. // This allows both durable and ephemeral references. The restriction that
  25. // only a `ref self` parameter can bind to an ephemeral reference is
  26. // enforced separately when handling `ref` tags on call arguments.
  27. RefParam,
  28. // Equivalent to RefParam, except that the source expression is not required
  29. // to be marked with a `ref` tag, such as an argument to a `ref self`
  30. // parameter or an operator operand.
  31. UnmarkedRefParam,
  32. // Convert to a reference of type `type_id`, for use as the argument to a
  33. // C++ thunk.
  34. CppThunkRef,
  35. // Convert for an explicit `as` cast. This allows any expression category
  36. // as the result, and uses the `As` interface instead of the `ImplicitAs`
  37. // interface.
  38. ExplicitAs,
  39. // Convert for an explicit `unsafe as` cast. This allows any expression
  40. // category as the result, and uses the `UnsafeAs` interface instead of the
  41. // `As` or `ImplicitAs` interface.
  42. ExplicitUnsafeAs,
  43. // The result of the conversion is discarded. It can't be an initializing
  44. // expression, but can be anything else.
  45. Discarded,
  46. // Convert to an initializing expression that uses `type_id`'s initializing
  47. // representation. The resulting expression will usually be a
  48. // repr-initializing expression, but may be an in-place initializing
  49. // expression if the source expression was. If `storage_id` is present, it
  50. // is used as the storage argument for the converted expression, and it must
  51. // be present if the initializing representation might be in-place.
  52. Initializing,
  53. // Convert to an in-place initializing expression whose storage is
  54. // designated by `storage_id` (which must not be `None`).
  55. InPlaceInitializing,
  56. Last = InPlaceInitializing
  57. };
  58. // The kind of the target for this conversion.
  59. Kind kind;
  60. // The target type for the conversion.
  61. SemIR::TypeId type_id;
  62. // The storage being initialized, if any. It must be valid to reference this
  63. // instruction after splicing in `storage_access_block` (if specified), so it
  64. // must either dominate the initializer or be one of the instructions in
  65. // `storage_access_block`.
  66. SemIR::InstId storage_id = SemIR::InstId::None;
  67. // For an initializer, a block of pending instructions that `storage_id`
  68. // depends on. This block will be spliced or merged before any reference to
  69. // `storage_id`, and may be discarded if `storage_id` is not accessed.
  70. PendingBlock* storage_access_block = nullptr;
  71. // Whether failure of conversion is an error and is diagnosed to the user.
  72. // When looking for a possible conversion but with graceful fallback,
  73. // `diagnose` should be false. If `diagnose` is false, an `ErrorInst` may be
  74. // returned, but it must be discarded.
  75. bool diagnose = true;
  76. // Are we converting this value into an initializer for an object?
  77. auto is_initializer() const -> bool {
  78. return kind == Initializing || kind == InPlaceInitializing;
  79. }
  80. // Is this some kind of explicit `as` conversion?
  81. auto is_explicit_as() const -> bool {
  82. return kind == ExplicitAs || kind == ExplicitUnsafeAs;
  83. }
  84. };
  85. // Convert a value to another type and expression category.
  86. auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
  87. ConversionTarget target) -> SemIR::InstId;
  88. // Performs initialization of `storage_id` from the expression `value_id`, which
  89. // is converted to an initializing expression of the type of `storage_id` if
  90. // necessary, and returns the possibly-converted initializing expression.
  91. //
  92. // `storage_id` is used as the storage argument of the resulting expression
  93. // except as noted below. As a consequence, `storage_id` must dominate
  94. // `value_id` and its subexpressions. This will typically only be the case if
  95. // `storage_id` syntactically precedes `value_id`. Otherwise, some action will
  96. // need to be taken to reorder the code, such as instead calling `Initialize`
  97. // with a pending block containing `storage_id`, or creating a separate
  98. // `InstBlock` to hold either the storage or the initializer.
  99. //
  100. // `for_return` indicates that this conversion is initializing the operand of a
  101. // `return` statement. This means that `storage_id` will be the return slot
  102. // parameter, which isn't valid to access if the type's initializing
  103. // representation is not in-place, so in that case `storage_id` will be used
  104. // solely for its type.
  105. //
  106. // This function does not guarantee to perform an in-place initialization, so
  107. // the caller is responsible for passing the returned `InstId` to an inst that
  108. // is documented as consuming it, such as `Assign`.
  109. auto InitializeExisting(Context& context, SemIR::LocId loc_id,
  110. SemIR::InstId storage_id, SemIR::InstId value_id,
  111. bool for_return = false) -> SemIR::InstId;
  112. // Result of Initialize.
  113. struct InitializeResult {
  114. // The storage location that contains the initialized value. This may be
  115. // different from the `storage_id` that was passed to `Initialize` if the
  116. // storage block was written over existing instructions rather than being
  117. // spliced in.
  118. SemIR::InstId storage_id;
  119. // The converted initializing expression used to initialize the storage.
  120. SemIR::InstId init_id;
  121. };
  122. // Performs initialization of `storage_id` from the expression `value_id`, which
  123. // is converted to an initializing expression of the type of `storage_id` if
  124. // necessary. `storage_access_block` should be used to supply a pending block
  125. // that allocates the storage, and typically contains `storage_id`. The target
  126. // of the initialization will be either `storage_id` itself, or an existing
  127. // storage argument instruction that is overwritten to hold a copy of
  128. // `storage_id` as an optimization for SemIR compactness.
  129. //
  130. // The storage instruction will only be written over an existing instruction if
  131. // it is the sole instruction in the pending block. This is expected to be a
  132. // common case. After this happens, the copy of the instruction in the pending
  133. // block is expected to be unreachable from the SemIR::File. As a result, the
  134. // `storage_id` instruction should not be referenced again after calling this
  135. // function, and this function takes it by rvalue reference to remind the caller
  136. // of this.
  137. //
  138. // If the overwrite optimization is not performed, `storage_access_block` will
  139. // be inserted before any use of the storage by the initializer, and will be
  140. // inserted even if the initializer does not actually use the storage. It must
  141. // be valid to reference `storage_id` after splicing in `storage_access_block`,
  142. // so `storage_id` must either dominate the initializer (but see the TODO below)
  143. // or be one of the instructions in `storage_access_block`. If `storage_id` is
  144. // known to always dominate the initializer, `InitializeExisting` should be used
  145. // instead.
  146. //
  147. // TODO: We don't have an implementation of a proper dominance check, so we
  148. // fake one up by comparing the order in which the insts were created.
  149. //
  150. // This function does not guarantee to perform an in-place initialization, so
  151. // the caller is responsible for passing the returned `inst_id` to an inst that
  152. // is documented as consuming it, such as `Assign`.
  153. auto Initialize(Context& context, SemIR::LocId loc_id,
  154. SemIR::InstId&& storage_id, PendingBlock&& storage_access_block,
  155. SemIR::InstId value_id) -> InitializeResult;
  156. // Convert the given expression to a value expression of the same type.
  157. auto ConvertToValueExpr(Context& context, SemIR::InstId expr_id)
  158. -> SemIR::InstId;
  159. // Convert the given expression to a value or reference expression of the same
  160. // type.
  161. auto ConvertToValueOrRefExpr(Context& context, SemIR::InstId expr_id)
  162. -> SemIR::InstId;
  163. // Converts `expr_id` to a value expression of type `type_id`.
  164. //
  165. // If `diagnose` is true, errors are diagnosed to the user. Set it to false when
  166. // looking to see if a conversion is possible but with graceful fallback. If
  167. // `diagnose` is false, an `ErrorInst` may be returned, but it must be
  168. // discarded.
  169. auto ConvertToValueOfType(Context& context, SemIR::LocId loc_id,
  170. SemIR::InstId expr_id, SemIR::TypeId type_id,
  171. bool diagnose = true) -> SemIR::InstId;
  172. // Convert the given expression to a value or reference expression of the given
  173. // type.
  174. auto ConvertToValueOrRefOfType(Context& context, SemIR::LocId loc_id,
  175. SemIR::InstId expr_id, SemIR::TypeId type_id)
  176. -> SemIR::InstId;
  177. // Attempted to convert `expr_id` to a value expression of type `type_id`, with
  178. // graceful failure, which does not result in diagnostics. An ErrorInst
  179. // instruction is still returned on failure.
  180. auto TryConvertToValueOfType(Context& context, SemIR::LocId loc_id,
  181. SemIR::InstId expr_id, SemIR::TypeId type_id)
  182. -> SemIR::InstId;
  183. // Converts `value_id` to a value expression of type `bool`.
  184. auto ConvertToBoolValue(Context& context, SemIR::LocId loc_id,
  185. SemIR::InstId value_id) -> SemIR::InstId;
  186. // Converts `value_id` to type `type_id` for an `as` expression.
  187. auto ConvertForExplicitAs(Context& context, Parse::NodeId as_node,
  188. SemIR::InstId value_id, SemIR::TypeId type_id,
  189. bool unsafe) -> SemIR::InstId;
  190. // Implicitly converts a set of arguments to match the parameter types in a
  191. // function call. Returns a block containing the converted implicit and explicit
  192. // argument values for runtime parameters. `is_desugared` indicates that this
  193. // call was produced by desugaring, not written as a function call in user code,
  194. // so arguments to `ref` parameters aren't required to have `ref` tags.
  195. auto ConvertCallArgs(Context& context, SemIR::LocId call_loc_id,
  196. SemIR::InstId self_id,
  197. llvm::ArrayRef<SemIR::InstId> arg_refs,
  198. SemIR::InstId return_arg_id, const SemIR::Function& callee,
  199. SemIR::SpecificId callee_specific_id, bool is_desugared)
  200. -> SemIR::InstBlockId;
  201. // A type that has been converted for use as a type expression.
  202. struct TypeExpr {
  203. static const TypeExpr None;
  204. // Returns a TypeExpr describing a type with no associated spelling or type
  205. // sugar.
  206. static auto ForUnsugared(Context& context, SemIR::TypeId type_id) -> TypeExpr;
  207. // The converted expression of type `type`, or `ErrorInst::InstId`.
  208. SemIR::TypeInstId inst_id;
  209. // The corresponding type, or `ErrorInst::TypeId`.
  210. SemIR::TypeId type_id;
  211. };
  212. inline constexpr TypeExpr TypeExpr::None = {.inst_id = SemIR::TypeInstId::None,
  213. .type_id = SemIR::TypeId::None};
  214. // Converts an expression for use as a type.
  215. //
  216. // If `diagnose` is true, errors are diagnosed to the user. Set it to false when
  217. // looking to see if a conversion is possible but with graceful fallback. If
  218. // `diagnose` is false, an `ErrorInst` may be returned, but it must be
  219. // discarded.
  220. //
  221. // TODO: Most of the callers of this function discard the `inst_id` and lose
  222. // track of the conversion. In most cases we should be retaining that as the
  223. // operand of some downstream instruction.
  224. auto ExprAsType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id,
  225. bool diagnose = true) -> TypeExpr;
  226. // Converts an expression in a form position for use as a form.
  227. //
  228. // Note that the right-hand side of a `->` return type declaration is not
  229. // a form position for this purpose, because it uses a special syntax to specify
  230. // forms. `ReturnExprAsForm` should be used instead in that case.
  231. //
  232. // `diagnose` has the same effect as in `ExprAsType`.
  233. auto FormExprAsForm(Context& context, SemIR::LocId loc_id,
  234. SemIR::InstId value_id) -> Context::FormExpr;
  235. // Evaluates an expression in the return-type position (following `->`, not
  236. // `->?`) for use as a form, following the special-case language rules for
  237. // evaluating an expression in that position.
  238. auto ReturnExprAsForm(Context& context, SemIR::LocId loc_id,
  239. SemIR::InstId value_id) -> Context::FormExpr;
  240. // Handles an expression whose result value is unused.
  241. auto DiscardExpr(Context& context, SemIR::InstId expr_id) -> void;
  242. } // namespace Carbon::Check
  243. #endif // CARBON_TOOLCHAIN_CHECK_CONVERT_H_