The id-subset[1] of Objective-C is also memory safe, just like the Smalltalk it copied.
And Objective-C is a credible C competitor, partly because it is a true superset of C, partly because you can get it to any performance level you want (frequently faster than equivalent practical C code [2]) and it was even used in the OS kernel in NeXTStep.
Now obviously it's not done, as it is a true superset and thus inherits all of C's non-safety, and if you were to just use the id-subset that is memory safe, you wouldn't be fully competitive.
However, it does show a fairly clear path forward: restrict the C part of Objective-C so that it remains safe, let all the tricky parts that would otherwise cause non-safety be handled by the id-subset.
That is the approach I am taking with the procedural part of Objective-S[3]: let the procedural part be like Smalltalk, with type-declarations allowing you to optimize that away to something like Pascal or Oberon. Use reference counting to keep references safe, but potentially leaky in the face of cycles. Optional lifetime annotations such as weak can be used to eliminate those leaks and to eliminate reference counting operations. Just like optional type declarations can reduce boxing and dynamic dispatch.
And Objective-C is a credible C competitor, partly because it is a true superset of C, partly because you can get it to any performance level you want (frequently faster than equivalent practical C code [2]) and it was even used in the OS kernel in NeXTStep.
Now obviously it's not done, as it is a true superset and thus inherits all of C's non-safety, and if you were to just use the id-subset that is memory safe, you wouldn't be fully competitive.
However, it does show a fairly clear path forward: restrict the C part of Objective-C so that it remains safe, let all the tricky parts that would otherwise cause non-safety be handled by the id-subset.
That is the approach I am taking with the procedural part of Objective-S[3]: let the procedural part be like Smalltalk, with type-declarations allowing you to optimize that away to something like Pascal or Oberon. Use reference counting to keep references safe, but potentially leaky in the face of cycles. Optional lifetime annotations such as weak can be used to eliminate those leaks and to eliminate reference counting operations. Just like optional type declarations can reduce boxing and dynamic dispatch.
[1] https://blog.metaobject.com/2014/05/the-spidy-subset-or-avoi...
[2] https://www.amazon.com/gp/product/0321842847/ref=as_li_tl?ie...
[3] https://objective.st/