Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions spec/primitives/slice_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ describe "Primitives: Slice" do
slice.to_a.should eq([0, 1, 4, 9, 16, 25] of {{ num }})
slice.read_only?.should be_true
end

# TODO: these should probably return the same pointers
pending_interpreted "creates multiple literals" do
slice1 = Slice({{ num }}).literal(1, 2, 3)
slice2 = Slice({{ num }}).literal(1, 2, 3)
slice1.should eq(slice2)
end
{% end %}
end
end
4 changes: 3 additions & 1 deletion src/compiler/crystal/program.cr
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ module Crystal
types["Regex"] = @regex = NonGenericClassType.new self, self, "Regex", reference
types["Range"] = range = @range = GenericClassType.new self, self, "Range", struct_t, ["B", "E"]
range.struct = true
types["Slice"] = slice = @slice = GenericClassType.new self, self, "Slice", struct_t, ["T"]
slice.struct = true

types["Exception"] = @exception = NonGenericClassType.new self, self, "Exception", reference

Expand Down Expand Up @@ -528,7 +530,7 @@ module Crystal

{% for name in %w(object no_return value number reference void nil bool char int int8 int16 int32 int64 int128
uint8 uint16 uint32 uint64 uint128 float float32 float64 string symbol pointer enumerable indexable
array static_array exception tuple named_tuple proc union enum range regex crystal
array static_array exception tuple named_tuple proc union enum range slice regex crystal
packed_annotation thread_local_annotation no_inline_annotation
always_inline_annotation naked_annotation returns_twice_annotation
raises_annotation primitive_annotation call_convention_annotation
Expand Down
105 changes: 59 additions & 46 deletions src/compiler/crystal/semantic/main_visitor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,10 @@ module Crystal
if check_special_new_call(node, obj.type?)
return false
end

if check_slice_literal_call(node, obj.type?)
return false
end
end

args.each &.accept(self)
Expand Down Expand Up @@ -1567,6 +1571,60 @@ module Crystal
false
end

def check_slice_literal_call(node, obj_type)
return false unless obj_type
return false unless obj_type.metaclass?

instance_type = obj_type.instance_type.remove_typedef

if node.name == "literal"
case instance_type
when GenericClassType # Slice
return false unless instance_type == @program.slice
node.raise "TODO: implement slice_literal primitive for Slice without generic arguments"
when GenericClassInstanceType # Slice(T)
return false unless instance_type.generic_type == @program.slice

element_type = instance_type.type_vars["T"].type
kind = case element_type
when IntegerType
element_type.kind
when FloatType
element_type.kind
else
node.raise "Only slice literals of primitive integer or float types can be created"
end

node.args.each do |arg|
arg.raise "Expected NumberLiteral, got #{arg.class_desc}" unless arg.is_a?(NumberLiteral)
arg.accept self
arg.raise "Argument out of range for a Slice(#{element_type})" unless arg.representable_in?(element_type)
end

# create the internal constant `$Slice:n` to hold the slice contents
const_name = "$Slice:#{@program.const_slices.size}"
const_value = Nop.new
const_value.type = @program.static_array_of(element_type, node.args.size)
const = Const.new(@program, @program, const_name, const_value)
@program.types[const_name] = const
@program.const_slices << Program::ConstSliceInfo.new(const_name, kind, node.args)

# ::Slice.new(pointerof($Slice:n.@buffer), {{ args.size }}, read_only: true)
pointer_node = PointerOf.new(ReadInstanceVar.new(Path.new(const_name).at(node), "@buffer").at(node)).at(node)
size_node = NumberLiteral.new(node.args.size.to_s, :i32).at(node)
read_only_node = NamedArgument.new("read_only", BoolLiteral.new(true).at(node)).at(node)
expanded = Call.new(Path.global("Slice").at(node), "new", [pointer_node, size_node], named_args: [read_only_node]).at(node)

expanded.accept self
node.bind_to expanded
node.expanded = expanded
return true
end
end

false
end

# Rewrite:
#
# LibFoo::Struct.new arg0: value0, argN: value0
Expand Down Expand Up @@ -2308,7 +2366,7 @@ module Crystal
when "pointer_new"
visit_pointer_new node
when "slice_literal"
visit_slice_literal node
node.raise "BUG: Slice literal should have been expanded"
when "argc"
# Already typed
when "argv"
Expand Down Expand Up @@ -2466,51 +2524,6 @@ module Crystal
node.type = scope.instance_type
end

def visit_slice_literal(node)
call = self.call.not_nil!

case slice_type = scope.instance_type
when GenericClassType # Slice
call.raise "TODO: implement slice_literal primitive for Slice without generic arguments"
when GenericClassInstanceType # Slice(T)
element_type = slice_type.type_vars["T"].type
kind = case element_type
when IntegerType
element_type.kind
when FloatType
element_type.kind
else
call.raise "Only slice literals of primitive integer or float types can be created"
end

call.args.each do |arg|
arg.raise "Expected NumberLiteral, got #{arg.class_desc}" unless arg.is_a?(NumberLiteral)
arg.raise "Argument out of range for a Slice(#{element_type})" unless arg.representable_in?(element_type)
end

# create the internal constant `$Slice:n` to hold the slice contents
const_name = "$Slice:#{@program.const_slices.size}"
const_value = Nop.new
const_value.type = @program.static_array_of(element_type, call.args.size)
const = Const.new(@program, @program, const_name, const_value)
@program.types[const_name] = const
@program.const_slices << Program::ConstSliceInfo.new(const_name, kind, call.args)

# ::Slice.new(pointerof($Slice:n.@buffer), {{ args.size }}, read_only: true)
pointer_node = PointerOf.new(ReadInstanceVar.new(Path.new(const_name).at(node), "@buffer").at(node)).at(node)
size_node = NumberLiteral.new(call.args.size.to_s, :i32).at(node)
read_only_node = NamedArgument.new("read_only", BoolLiteral.new(true).at(node)).at(node)
extra = Call.new(Path.global("Slice").at(node), "new", [pointer_node, size_node], named_args: [read_only_node]).at(node)

extra.accept self
node.extra = extra
node.type = slice_type
call.expanded = extra
else
node.raise "BUG: Unknown scope for slice_literal primitive"
end
end

def visit_struct_or_union_set(node)
scope = @scope.as(NonGenericClassType)

Expand Down