Skip to content

Commit 7ad0e3d

Browse files
authored
lower new() to reference the called object instead of re-creating it with apply_type (#44664)
addresses #36384
1 parent f410c62 commit 7ad0e3d

File tree

2 files changed

+62
-36
lines changed

2 files changed

+62
-36
lines changed

src/julia-syntax.scm

Lines changed: 54 additions & 36 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 (|::| |#ctor-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+
'|#ctor-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)
@@ -850,56 +855,69 @@
850855
((length= lno 3) (string " around " (caddr lno) ":" (cadr lno)))
851856
(else "")))
852857

858+
;; convert constructor signature from X(...) to (|#ctor-self#|::Type{X})(...),
859+
;; or return #f if we can't
860+
(define (ctor-sig sig)
861+
(cond ((or (eq? (car sig) '|::|) (eq? (car sig) 'where))
862+
(let ((s2 (ctor-sig (cadr sig))))
863+
(and s2 `(,(car sig) ,s2 ,@(cddr sig)))))
864+
((eq? (car sig) 'call)
865+
(let ((head (cadr sig)))
866+
(if (decl? head)
867+
(if (eq? (cadr head) '|#ctor-self#|)
868+
sig ;; already in the required form
869+
#f)
870+
`(call (|::| |#ctor-self#| (curly (core Type) ,head)) ,@(cddr sig)))))
871+
(else #f)))
872+
853873
(define (ctor-def name Tname ctor-body sig body wheres)
854874
(let* ((curly? (and (pair? name) (eq? (car name) 'curly)))
855875
(curlyargs (if curly? (cddr name) '()))
856876
(name (if curly? (cadr name) name))
857877
(sparams (map car (map analyze-typevar wheres))))
858878
(cond ((not (eq? name Tname))
859-
`(function ,(with-wheres `(call ,(if curly?
860-
`(curly ,name ,@curlyargs)
861-
name)
862-
,@sig)
863-
wheres)
879+
`(function ,sig
864880
;; pass '() in order to require user-specified parameters with
865881
;; new{...} inside a non-ctor inner definition.
866-
,(ctor-body body '() sparams)))
882+
,(ctor-body body '() sparams #f)))
867883
(else
868-
`(function ,(with-wheres `(call ,(if curly?
869-
`(curly ,name ,@curlyargs)
870-
name)
871-
,@sig)
872-
wheres)
873-
,(ctor-body body curlyargs sparams))))))
884+
(let ((newsig (ctor-sig sig)))
885+
`(function ,(or newsig sig)
886+
,(ctor-body body curlyargs sparams (not (not newsig)))))))))
874887

875888
;; rewrite calls to `new( ... )` to `new` expressions on the appropriate
876889
;; type, determined by the containing constructor definition.
877890
(define (rewrite-ctor ctor Tname params field-names field-types)
878-
(define (ctor-body body type-params sparams)
891+
(define (ctor-body body type-params sparams selftype?)
879892
(pattern-replace (pattern-set
880893
(pattern-lambda
881894
(call (-/ new) . args)
882895
(new-call Tname type-params sparams params
883-
(map (lambda (a) (ctor-body a type-params sparams)) args)
884-
field-names field-types))
896+
(map (lambda (a) (ctor-body a type-params sparams selftype?)) args)
897+
field-names field-types selftype?))
885898
(pattern-lambda
886899
(call (curly (-/ new) . p) . args)
887900
(new-call Tname p sparams params
888-
(map (lambda (a) (ctor-body a type-params sparams)) args)
889-
field-names field-types)))
901+
(map (lambda (a) (ctor-body a type-params sparams selftype?)) args)
902+
field-names field-types #f)))
890903
body))
891904
(pattern-replace
892905
(pattern-set
906+
;; recognize `(t::(Type{X{T}} where T))(...)` as an inner-style constructor for X
907+
(pattern-lambda (function (-$ (call (|::| self (where (curly (core (-/ Type)) name) . wheres)) . sig)
908+
(|::| (call (|::| self (where (curly (core (-/ Type)) name) . wheres)) . sig) _t))
909+
body)
910+
(ctor-def name Tname ctor-body (cadr __) body wheres))
893911
;; definitions without `where`
894912
(pattern-lambda (function (-$ (call name . sig) (|::| (call name . sig) _t)) body)
895-
(ctor-def name Tname ctor-body sig body #f))
913+
(ctor-def name Tname ctor-body (cadr __) body #f))
896914
(pattern-lambda (= (-$ (call name . sig) (|::| (call name . sig) _t)) body)
897-
(ctor-def name Tname ctor-body sig body #f))
915+
(ctor-def name Tname ctor-body (cadr __) body #f))
898916
;; definitions with `where`
899917
(pattern-lambda (function (where (-$ (call name . sig) (|::| (call name . sig) _t)) . wheres) body)
900-
(ctor-def name Tname ctor-body sig body wheres))
918+
(ctor-def name Tname ctor-body (cadr __) body wheres))
901919
(pattern-lambda (= (where (-$ (call name . sig) (|::| (call name . sig) _t)) . wheres) body)
902-
(ctor-def name Tname ctor-body sig body wheres)))
920+
(ctor-def name Tname ctor-body (cadr __) body wheres)))
903921

904922
;; flatten `where`s first
905923
(pattern-replace

test/syntax.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3357,3 +3357,11 @@ demo44723()::Any = Base.Experimental.@opaque () -> true ? 1 : 2
33573357
@test y == Core.svec(2, 3)
33583358
@test z == 4
33593359
end
3360+
3361+
# rewriting inner constructors with return type decls
3362+
struct InnerCtorRT{T}
3363+
InnerCtorRT()::Int = new{Int}()
3364+
InnerCtorRT{T}() where {T} = ()->new()
3365+
end
3366+
@test_throws MethodError InnerCtorRT()
3367+
@test InnerCtorRT{Int}()() isa InnerCtorRT{Int}

0 commit comments

Comments
 (0)