Skip to content

Commit 7c36bb5

Browse files
committed
lower new() to reference the called object instead of re-creating it with apply_type
addresses #36384
1 parent 1eae65b commit 7c36bb5

File tree

2 files changed

+39
-31
lines changed

2 files changed

+39
-31
lines changed

src/julia-syntax.scm

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -752,12 +752,13 @@
752752
(let* ((field-names (safe-field-names field-names field-types))
753753
(any-ctor
754754
;; definition with Any for all arguments
755-
`(function ,(with-wheres
756-
`(call ,(if (pair? params)
757-
`(curly ,name ,@params)
758-
name)
759-
,@field-names)
760-
(map (lambda (b) (cons 'var-bounds b)) bounds))
755+
`(function (call (|::| |#self#|
756+
,(with-wheres
757+
`(curly (core Type) ,(if (pair? params)
758+
`(curly ,name ,@params)
759+
name))
760+
(map (lambda (b) (cons 'var-bounds b)) bounds)))
761+
,@field-names)
761762
(block
762763
,@locs
763764
(call new ,@field-names)))))
@@ -791,7 +792,9 @@
791792
(define (num-non-varargs args)
792793
(count (lambda (a) (not (vararg? a))) args))
793794

794-
(define (new-call Tname type-params sparams params args field-names field-types)
795+
;; selftype?: tells us whether the called object is the type being constructed,
796+
;; i.e. `new()` and not `new{...}()`.
797+
(define (new-call Tname type-params sparams params args field-names field-types selftype?)
795798
(if (any kwarg? args)
796799
(error "\"new\" does not accept keyword arguments"))
797800
(let ((nnv (num-non-varargs type-params)))
@@ -801,14 +804,16 @@
801804
(error "too many type parameters specified in \"new{...}\"")))
802805
(let* ((Texpr (if (null? type-params)
803806
`(outerref ,Tname)
804-
`(curly (outerref ,Tname)
805-
,@type-params)))
806-
(tn (make-ssavalue))
807+
(if selftype?
808+
'|#self#|
809+
`(curly (outerref ,Tname)
810+
,@type-params))))
811+
(tn (if (symbol? Texpr) Texpr (make-ssavalue)))
807812
(field-convert (lambda (fld fty val)
808813
(if (equal? fty '(core Any))
809814
val
810815
`(call (top convert)
811-
,(if (and (equal? type-params params) (memq fty params) (memq fty sparams))
816+
,(if (and (not selftype?) (equal? type-params params) (memq fty params) (memq fty sparams))
812817
fty ; the field type is a simple parameter, the usage here is of a
813818
; local variable (currently just handles sparam) for the bijection of params to type-params
814819
`(call (core fieldtype) ,tn ,(+ fld 1)))
@@ -823,7 +828,7 @@
823828
(let ((argt (make-ssavalue))
824829
(nf (make-ssavalue)))
825830
`(block
826-
(= ,tn ,Texpr)
831+
,@(if (symbol? tn) '() `((= ,tn ,Texpr)))
827832
(= ,argt (call (core tuple) ,@args))
828833
(= ,nf (call (core nfields) ,argt))
829834
(if (call (top ult_int) ,nf ,(length field-names))
@@ -835,9 +840,9 @@
835840
(new ,tn ,@(map (lambda (fld fty) (field-convert fld fty `(call (core getfield) ,argt ,(+ fld 1) (false))))
836841
(iota (length field-names)) (list-head field-types (length field-names))))))))
837842
(else
838-
`(block
839-
(= ,tn ,Texpr)
840-
(new ,tn ,@(map field-convert (iota (length args)) (list-head field-types (length args)) args)))))))
843+
`(block
844+
,@(if (symbol? tn) '() `((= ,tn ,Texpr)))
845+
(new ,tn ,@(map field-convert (iota (length args)) (list-head field-types (length args)) args)))))))
841846

842847
;; insert item at start of arglist
843848
(define (arglist-unshift sig item)
@@ -856,20 +861,12 @@
856861
(name (if curly? (cadr name) name))
857862
(sparams (map car (map analyze-typevar wheres))))
858863
(cond ((not (eq? name Tname))
859-
`(function ,(with-wheres `(call ,(if curly?
860-
`(curly ,name ,@curlyargs)
861-
name)
862-
,@sig)
863-
wheres)
864+
`(function ,sig
864865
;; pass '() in order to require user-specified parameters with
865866
;; new{...} inside a non-ctor inner definition.
866867
,(ctor-body body '() sparams)))
867868
(else
868-
`(function ,(with-wheres `(call ,(if curly?
869-
`(curly ,name ,@curlyargs)
870-
name)
871-
,@sig)
872-
wheres)
869+
`(function ,sig
873870
,(ctor-body body curlyargs sparams))))))
874871

875872
;; rewrite calls to `new( ... )` to `new` expressions on the appropriate
@@ -881,25 +878,30 @@
881878
(call (-/ new) . args)
882879
(new-call Tname type-params sparams params
883880
(map (lambda (a) (ctor-body a type-params sparams)) args)
884-
field-names field-types))
881+
field-names field-types #t))
885882
(pattern-lambda
886883
(call (curly (-/ new) . p) . args)
887884
(new-call Tname p sparams params
888885
(map (lambda (a) (ctor-body a type-params sparams)) args)
889-
field-names field-types)))
886+
field-names field-types #f)))
890887
body))
891888
(pattern-replace
892889
(pattern-set
890+
;; recognize `(t::(Type{X{T}} where T))(...)` as an inner-style constructor for X
891+
(pattern-lambda (function (-$ (call (|::| self (where (curly (core (-/ Type)) name) . wheres)) . sig)
892+
(|::| (call (|::| self (where (curly (core (-/ Type)) name) . wheres)) . sig) _t))
893+
body)
894+
(ctor-def name Tname ctor-body (cadr __) body wheres))
893895
;; definitions without `where`
894896
(pattern-lambda (function (-$ (call name . sig) (|::| (call name . sig) _t)) body)
895-
(ctor-def name Tname ctor-body sig body #f))
897+
(ctor-def name Tname ctor-body (cadr __) body #f))
896898
(pattern-lambda (= (-$ (call name . sig) (|::| (call name . sig) _t)) body)
897-
(ctor-def name Tname ctor-body sig body #f))
899+
(ctor-def name Tname ctor-body (cadr __) body #f))
898900
;; definitions with `where`
899901
(pattern-lambda (function (where (-$ (call name . sig) (|::| (call name . sig) _t)) . wheres) body)
900-
(ctor-def name Tname ctor-body sig body wheres))
902+
(ctor-def name Tname ctor-body (cadr __) body wheres))
901903
(pattern-lambda (= (where (-$ (call name . sig) (|::| (call name . sig) _t)) . wheres) body)
902-
(ctor-def name Tname ctor-body sig body wheres)))
904+
(ctor-def name Tname ctor-body (cadr __) body wheres)))
903905

904906
;; flatten `where`s first
905907
(pattern-replace

test/syntax.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3276,3 +3276,9 @@ end
32763276
@test m.Foo.bar === 1
32773277
@test Core.get_binding_type(m.Foo, :bar) == Any
32783278
end
3279+
3280+
# rewriting inner constructors with return type decls
3281+
struct InnerCtorRT{T}
3282+
InnerCtorRT()::Int = new{Int}()
3283+
end
3284+
@test_throws MethodError InnerCtorRT()

0 commit comments

Comments
 (0)