Wanted: WARN when redefining something with a different current *PACKAGE*

Bug #778760 reported by Jean-Philippe Paradis on 2011-05-06
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
SBCL
Wishlist
Unassigned

Bug Description

Consider the following scenario. I tried to optimize for damage even
if it makes things more unlikely, but the underlying problem can bite
you in much less convoluted scenarios too... I use defclass here but
this applies to everything that defines something. The three
files/systems are written by 3 different programmers.

;;;;; SCENARIO START

;;;; File foo.lisp in system foo.
(defpackage #:foo
  (:use #:cl)
  (:export #:bar
    #:bar-value
    #:make-bar
    ;; Let's pretend tons of other things are exported...
    ))

(in-package #:foo)

(defclass bar ()
  ((value :initarg :value
   :accessor bar-value)))

(defun make-bar (value)
  (make-instance 'bar :value value))

;;; Tons of other functionality here...

;;;; File baz.lisp in system baz. Written by Programmer-Guy.
(defpackage #:baz (:use #:cl #:foo))

(in-package #:baz)

;; Accidental redefinition!
(defclass bar ()
  (; Some slots here, whatever.
   ))

;;; Use the (new) definition of bar here...
;;; Programmer-Guy is unaware that package FOO
;;; defines class BAR and exports the symbol.
;;; He doesn't use the BAR functionality at all, even indirectly.
;;; There's no indication of a problem.

;;;; File quux.lisp in system quux. Written by Programmer-Dude.
;;;; This file just incidentally happens to be loaded
;;;; in an image where system baz was loaded.
(defpackage #:quux (:use #:cl #:foo))

(in-package #:quux)

;;;; Doesn't work! "What the HELL??"
(defun main ()
  (let ((bar (make-bar 3)))
    (incf (bar-value bar) 2)
    bar))

;;;;; SCENARIO END

This whole mess would have been averted if SBCL had thrown a WARNING
when foo:bar was redefined from *PACKAGE* baz while it had originally
been defined in *PACKAGE* foo.

All the definition macros would benefit from this, I think:

defclass
defconstant
defgeneric
define-compiler-macro
define-condition
define-method-combination
define-modify-macro
define-setf-expander
define-symbol-macro
defmacro
defmethod
defpackage
defparameter
defsetf
defstruct
deftype
defun
defvar

Jean-Philippe Paradis <email address hidden> writes:

> Consider the following scenario. I tried to optimize for damage even
> if it makes things more unlikely, but the underlying problem can bite
> you in much less convoluted scenarios too... I use defclass here but
> this applies to everything that defines something. The three
> files/systems are written by 3 different programmers.
>
> ;;;;; SCENARIO START

This scenario is exactly what package locks are designed for.

Christophe

Nikodemus Siivola (nikodemus) wrote :

I agree with Christophe, though the point stands that if you use a library that doesn't lock its packages ... things are currently harder than necessary to protect against.

Might be we could extend package locking in the inverse direction:

(defpackage :foo
   (:use :cl :some-big-unlocked-package)
   (:restrict t))

(in-package :foo)

(defun from-big-unlocked-package ...)

=> error "Restriction on package :FOO violated while redefining BIG:FROM-BIG..."

Ie. a "restricted package" works as if all other packages were locked, while in it.

Package locking is a feature that each individual library must explicitly activate for their users to benefit from it. I think it's safe to say most libraries out there (at least 51%!) don't lock their package(s) (not that I made a formal survey, just a hunch).

The feature I'm proposing would grant a nice level of additional protection by default, with zero involvement from libraries. I think that's significant and worth considering.

Jean-Philippe Paradis <email address hidden> writes:

> Package locking is a feature that each individual library must
> explicitly activate for their users to benefit from it.

I don't think that's true: users can lock packages for themselves. (In
order to anticipate your next objection, yes, it's possible that a
library with a locked package in some way doesn't quite function; my
hunch, like yours not backed by a formal survey, is that most would
continue to function just fine).

> The feature I'm proposing would grant a nice level of additional
> protection by default, with zero involvement from libraries. I think
> that's significant and worth considering.

I'm generally against adding additional protection against ordinary,
conforming behaviour with no convenient way for the advanced user to say
"trust me, I know what I'm doing". (People following along with a long
historical view of sbcl development may laugh hollowly at this, but
we've only comparatively recently managed to remove a huge heap of
redefinition warnings that were fantastically annoying; we should think
long and hard before reintroducing them.

Christophe

Nikodemus Siivola (nikodemus) wrote :

I do think that we would like to provide a way for users to protect themselves against

  (defpackage :foo
     (:use :cl :some-big-unlocked-package))

  (in-package :foo)

  (defun from-big-unlocked-package ...) ; oops

Maybe locked packages should also provide protection in the other direction. Maybe it should be a separate option like :RESTRICT.

Changed in sbcl:
importance: Undecided → Wishlist
status: New → Confirmed
To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers