Opened 9 years ago

Closed 9 years ago

Last modified 9 years ago

#642 closed defect (invalid)

Call to WARN messes with pretty-printer

Reported by: trittweiler Owned by:
Priority: normal Milestone:
Component: ANSI CL Compliance Version: trunk
Keywords: Cc:

Description (last modified by rme)

Calling WARN messes with the pretty-printer.

CL-USER> (lisp-implementation-version)
"Version 1.4-RC1-r13031  (LinuxX8632)"
CL-USER> (setq *print-pretty* t)

CL-USER> (format t "~@<COMPILE-FILE failed while performing ~A on ~A.~@:>"
                 "#<COMPILE-OP (:FORCE T) #x150A26AE>" 
                 "#<IRONCLAD-SOURCE-FILE \"ripemd-160\" #x14D18B46>")
COMPILE-FILE failed while performing #<COMPILE-OP (:FORCE T) #x150A26AE> on
#<IRONCLAD-SOURCE-FILE "ripemd-160" #x14D18B46>.

CL-USER> (warn "~@<COMPILE-FILE failed while performing ~A on ~A.~@:>"
              "#<COMPILE-OP (:FORCE T) #x150A26AE>"
               "#<IRONCLAD-SOURCE-FILE \"ripemd-160\" #x14D18B46>")
; Warning: COMPILE-FILE failed while performing
;                     #<COMPILE-OP (:FORCE T) #x150A26AE> on
;                     #<IRONCLAD-SOURCE-FILE "ripemd-160" #x14D18B46>.
; While executing: SWANK::EVAL-REGION, in process repl-thread(11).

As you can see, WARN somehow messes with pretty-printing.

Change History (7)

comment:1 Changed 9 years ago by rme

  • Description modified (diff)

comment:2 Changed 9 years ago by gb

If we print the condition via FORMAT with *PRINT-PRETTY* true and with some leading text on the line (for simplicity's sake, by adding that leading whitespace to the format string):

? (format t  "; Warning: ~@<COMPILE-FILE failed while performing ~A on ~A.~@:>"
               "#<COMPILE-OP (:FORCE T) #x150A26AE>"
               "#<IRONCLAD-SOURCE-FILE \"ripemd-160\" #x14D18B46>")

we get output that looks like:

; Warning: COMPILE-FILE failed while performing
           #<COMPILE-OP (:FORCE T) #x150A26AE> on
           #<IRONCLAD-SOURCE-FILE "ripemd-160" #x14D18B46>.

e.g., newlines are introduced (because the pretty printer's notion of the output line length has been exceeded) and the use of ~< ... ~:> causes the second and third lines to be indented to the column at which the enclosed output started.

CLHS notes that if a report message contains embedded newlines and code which prints such a multiline message inserts a prefix before the first line, that code should insert appropriate prefixes before printing subsequent lines.

That seems to be what's happening in your example. I'd agree that it certainly looks ugly, but it's not clear that making the output look less ugly (perhaps by suppressing leading whitespace after inserting that prefix) is always desirable. In a case where the pretty printer isn't involved - like:

  (warn "X is negative.~% (this may or may not be a problem.)")

it's not clear that stripping the leading whitespace from the second line constitutes what calls "proper alignment". notes that report message output may be "shifted to the left or right"(presumably by something equivalent to postprocessing), and it's not clear that ~< ... ~:> with *PRINT-PRETTY* true can anticipate the effects of this postprocessing any more or any better than things like #\tab expansion can.

If there's a bug here, I'm not really sure what it is.

comment:3 Changed 9 years ago by trittweiler


~<...~:> supports a feature not supported by pprint-logical-block. If ~:@> is used to terminate the directive (i.e., ~<...~:@>), then a fill-style conditional newline is automatically inserted after each group of blanks immediately contained in the body (except for blanks after a <Newline> directive). This makes it easy to achieve the equivalent of paragraph filling.

The behaviour of fill-style conditional newlines is described in:

CLHS dictionary entry PPRINT-NEWLINE

When a line break is inserted by any type of conditional newline, any blanks that immediately precede the conditional newline are omitted from the output and indentation is introduced at the beginning of the next line. By default, the indentation causes the following line to begin in the same horizontal position as the first character in the immediately containing logical block.

If that doesn't convince from a language lawyering point of view, I'll introduce an argumentum ad usum: :-) It's what I expect as a user, and other implementations seem to get it right. As it stands, you can make fine use of ~<~:> in FORMAT, and ERROR, but not in WARN for no obvious reason.

comment:4 Changed 9 years ago by gb

(In [13368]) Define LINE-LENGTH in terms of STREAM-LINE-LENGTH, which may actually return a non-constant value ...


Hair up %BREAK-MESSAGE, to try to minimize interactions between some types of indentation done by the "condition reporter" and the indentation done by the caller (%BREAK-MESSAGE itself.) See ticket:642.

comment:5 Changed 9 years ago by gb

(In [13387]) Propagate r13368 to 1.4. See ticket:642 .

comment:6 Changed 9 years ago by gb

  • Component changed from IDE to ANSI CL Compliance
  • Resolution set to invalid
  • Status changed from new to closed

The fact that the string "; Warning: " is longer than the string "; Error: " sure seems obvious, as does the fact that since FORMAT doesn't prepend any sort of prefix to its output it isn't really relevant here. (If the first point doesn't seem obvious to you, note that "Warning" is 2 characters longer than "Error", change COMPILE-FILE to COMPILE-FILE.. in the format string in your example, and call ERROR instead of WARN.)

Some other implementations (CLISP, LW, SBCL at least) don't insert a prefix on the first line of output when printing a condition in WARN/BREAK/ERROR/etc. (Those implementations behave like "Implementation A" in the example in CLHS

Allegro CL does insert a prefix on the first line of condition output in those cases, but doesn't seem to follow's requirement that subsequent lines of multiline output be indented/prefixed in some way.

[1] CL-USER(4): (defun test ()
   (error "This is an error message.~%It has two lines."))
[1] CL-USER(5): (test)
Error: This is an error message.
It has two lines.

That seems pretty clearly wrong, but because non-initial lines aren't properly indented someone might likely conclude that your example "works in Allegro." (Accidentally, it would seem.)

CCL inserts prefixes before the first line of condition output in WARN/ERROR/etc. and tries to prefix/indent subsequent lines as well; its behavior is very similar to that of "Implementation B" in the examples in Section advises against the use of #\Tab characters in condition report messages, because indentation might be shifted to the left or right by an arbitrary amount. The spec doesn't enumerate all of the other cases that might be sensitive to this shifting, but it seems likely that other constructs (~T to tab to an absolute position being an obvious example) are also sensitive to whether or not the presented output line is "shifted", and it doesn't seem like it should be hard to understand that ~<~:> when *PRINT-PRETTY* is true and that directive may introduce newlines and do its own shifting/indentation can also interact with indentation introduced later.

You seem to be reporting that multiline condition output - which the spec notes may be subject to shifting/indentation - is subject to shifting/indentation in this case. (Yes. It is.)

Suppose that in two of 3 32-bit CL implementations (A and B) MOST-POSITIVE-FIXNUM was 536870911 and in implementation C it's 1073741823; a user familiar with A and B writes code that somehow makes hardwired assumptions about the value of that constant, finds that the code works in A and B and fails in C, and notes that implementations A and B "get it right" and that C doesn't. They might sincerely believe that, but that doesn't make their (mis)understanding of the issue more useful to them or anyone else. If the misbehavior in this hypothetical case involves inappropriate type declarations, then quoting the spec's description of the behavior of + isn't likely to be productive, either.

That analogy's probably been strained about as much as it can be. If we're in agreement that the issue here is that the "shifting" performed when implementations that print prefixes before conditions do so can interact poorly with indentation performed by other formatting constructs that try to exercise control over indentation, great; I'd add to that that while I think that it's possible to make certain cases generate more aesthetic output (possibly at the expense of other case), I'm very skeptical that any sort of universal "solution" exists.

In other words, yes, WARN does mess with the pretty printer (by "shifting"/indenting output in ways that can't always be visible to the pretty printer.) r13368/r13387 change some details about how that's done and make your example look better, but I wouldn't claim that it "fixes" all cases or that it doesn't make some cases look worse than they had. WARN (and other functions that present conditions to the user) may do that - for reasons that the spec describes - whether the author of a condition reporter understands why it does or not; understanding why this interaction happens and trying to avoid use of formatting constructs that think that they exercise more control over indentation than they in fact do seems like a very small but worthwhile investment.

I don't see a bug here, but r13368/r13387 might convince someone who's unable to make that small effort that a bug has been fixed.

comment:7 Changed 9 years ago by trittweiler

I stand by my point that other implementations get it right. Please notice that I did not claim that CCL is in any way less conformant on the issue -- I assume that's the reason for the patronizing tone in your reply -- it's just that the other implementations get it right that the way they implement WARN does not mess with the pretty-printer. It would allow your users to instantly apply the knowledge they acquired about pretty-printing to use for formatting their warning/error messages.

Note: See TracTickets for help on using tickets.