| Age | Commit message (Collapse) | Author | Files | Lines |
|
Inlining in sparse works slightly differently than what my
mental model is: the body is only evaluated after the inline
expansion. IOW, an inline function is not evaluated until it
is effectively inlined. That's fine but it means that generic
expressions also need to be handled during the inlining.
However, since the body of inline functions is evaluated just
after inline expansion, so (recursively) copying the expression
and its type - expression map is quite useless here.
So, just copy the expression itself and its control expression
to 'isolate' them from evaluation, evaluate it and then just
copy the selected expression.
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Currently, the tree inlining is done very early, during the
evaluation phase. This means that the inlining is done even
if the corresponding call belong to a sub-expression that
will be discarded during the expansion phase.
Usually this is not a problem but in some pathological
cases it can lead to a huge waste of memory and CPU time.
So, move this inline expansion to ... the expansion phase.
Also, re-expand the resulting expression since constant
arguments may create new opportunities for simplification.
Note: the motivation for thsi is a pathological case in the
kernel where a combination of max_t() + const_ilog2() +
roundup_pow_of_two() + cpumask_weight() + __const_hweight*()
caused Sparse to use 2.3Gb of memory. With this patch
the memory consumption is down to 247Mb.
Link: https://marc.info/?l=linux-sparse&m=158098958501220
Link: https://lore.kernel.org/netdev/CAHk-=whvS9x5NKtOqcUgJeTY7dfdAHc
Reported-by: Randy Dunlap <rdunlap@infradead.org>
Originally-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Before commit 756731e9 ("use a specific struct for asm operands")
ASM operands where stored as a list of n times 3 expressions.
After this commit, the triplets where stored inside a single
expression of type EXPR_ASM_OPERAND.
However, while this improved the parsing and use of ASM operands
it needlessly reuse 'struct expression' for something that is not
an expression at all.
Fix this by really using a specific struct for ASM operands.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The idea being, of course, to be able for some functions to return
a bool, making clear what's their possible returned values.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
These functions are clearly meant to be used locally.
Furthermore, some have no prototypes.
Make these functions static and remove the prototype
when one was present.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
ASM operands have the following syntax:
[<ident>] "<constraint>" '(' <expr> ')'
For some reasons, during parsing this is stored
as a sequence of 3 expressions. This has some serious
disadvantages though:
- <ident> has not the type of an expression
- it complicates processing when compared to having a specific
struct for it (need to loop & maintain some state).
- <ident> is optional and stored as a null pointer when not present
which is annoying, for example, if null pointers are used internally
in ptr-lists to mark removed pointers.
Fix this by using a specific structure to store the 3 elements
of ASM operands.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
|
|
The old code was relicensed by Novafora Corporation, successor in interest to
Transmeta Corporation, in 2009. Other authors were also asked about the change
of their contributions to the MIT license and all with copyrightable changes
agreed to it.
Signed-off-by: Franz Schrober <franzschrober@yahoo.de>
Acked-by: Adam DiCarlo <adam@bikko.org>
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Acked-by: Alberto Bertogli <albertito@blitiri.com.ar>
Acked-by: Alecs King <alecs@perlchina.org>
Acked-by: Alexander Shishkin <alexander.shishckin@gmail.com>
Acked-by: Alexey Dobriyan <adobriyan@gmail.com>
Acked-by: Alexey Zaytsev <alexey.zaytsev@gmail.com>
Acked-by: Andries E. Brouwer <Andries.Brouwer@cwi.nl>
Acked-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Acked-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Acked-by: Ben Pfaff <blp@nicira.com>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Bernd Petrovitsch <bernd@petrovitsch.priv.at>
Acked-by: Bernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Acked-by: Blue Swirl <blauwirbel@gmail.com>
Acked-by: Chris Forbes <chrisf@ijw.co.nz>
Acked-by: Chris Wedgwood <cw@f00f.org>
Acked-by: Christopher Li <sparse@chrisli.org>
Acked-by: Damien Lespiau <damien.lespiau@gmail.com>
Acked-by: Dan Carpenter <error27@gmail.com>
Acked-by: Dan McGee <dan@archlinux.org>
Acked-by: Daniel De Graaf <danieldegraaf@gmail.com>
Acked-by: Daniel Sheridan <dan.sheridan@postman.org.uk>
Acked-by: Dave Jones <davej@redhat.com>
Acked-by: David Given <dg@cowlark.com>
Acked-by: David Miller <davem@redhat.com>
Acked-by: David Mosberger-Tang <dmosberger@gmail.com>
Acked-by: David Olien <David.Olien@lsi.com>
Acked-by: Diego Elio Pettenò <flameeyes@flameeyes.eu>
Acked-by: Emil Medve <Emilian.Medve@Freescale.com>
Acked-by: Ethan Jackson <jacksone@nicira.com>
Acked-by: Florian Fainelli <f.fainelli@gmail.com>
Acked-by: Frank Zago <fzago@systemfabricworks.com>
Acked-by: Frederic Crozat <fcrozat@suse.com>
Acked-by: Geoff Johnstone <geoff.johnstone@gmail.com>
Acked-by: Hannes Eder <hannes@hanneseder.net>
Acked-by: Jan Pokorný <pokorny_jan@seznam.cz>
Acked-by: Jeff Garzik <jgarzik@redhat.com>
Acked-by: Jiri Slaby <jslaby@suse.cz>
Acked-by: Joe Perches <joe@perches.com>
Acked-by: Joel Soete <rubisher@scarlet.be>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net>
Acked-by: Josh Triplett <josh@kernel.org>
Acked-by: Kamil Dudka <kdudka@redhat.com>
Acked-by: Kim Phillips <kim.phillips@linaro.org>
Acked-by: KOSAKI Motohiro <kosaki.motohiro@gmail.com>
Acked-by: Kovarththanan Rajaratnam <kovarththanan.rajaratnam@gmail.com>
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Acked-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
Acked-by: Martin Nagy <nagy.martin@gmail.com>
Acked-by: Masatake YAMATO <yamato@redhat.com>
Acked-by: Mauro Dreissig <mukadr@gmail.com>
Acked-by: Michael Büsch <m@bues.ch>
Acked-by: Michael Stefaniuc <mstefani@redhat.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Mika Kukkonen <mikukkon@iki.fi>
Acked-by: Mike Frysinger <vapier@gentoo.org>
Acked-by: Mitesh Shah <Mitesh.Shah@synopsys.com>
Acked-by: Morten Welinder <mortenw@gnome.org>
Acked-by: Namhyung Kim <namhyung@gmail.com>
Acked-by: Nicolas Kaiser <nikai@nikai.net>
Acked-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Pavel Roskin <proski@gnu.org>
Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>
Acked-by: Peter Jones <pjones@redhat.com>
Acked-by: Peter A Jonsson <pj@sics.se>
Acked-by: Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
Acked-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>
Acked-by: Randy Dunlap <rdunlap@xenotime.net>
Acked-by: Reinhard Tartler <siretart@tauware.de>
Ached-by: Richard Knutsson <richard.knutsson@gmail.com>
Acked-by: Rob Taylor <rob.taylor@codethink.co.uk>
Acked-by: Rui Saraiva <rmpsaraiva@gmail.com>
Acked-by: Ryan Anderson <ryan@michonline.com>
Acked-by: Sam Ravnborg <sam@ravnborg.org>
Acked-by: Samuel Bronson <naesten@gmail.com>
Acked-by: Santtu Hyrkkö <santtu.hyrkko@gmail.com>
Acked-by: Shakthi Kannan <shakthimaan@gmail.com>
Acked-by: Stephen Hemminger <shemminger@linux-foundation.org>
Acked-by: Thomas Schmid <Thomas.Schmid@br-automation.com>
Acked-by: Tilman Sauerbeck <tilman@code-monkey.de>
Acked-by: Vegard Nossum <vegardno@ifi.uio.no>
Acked-by: Xi Wang <xi.wang@gmail.com>
Acked-by: Yura Pakhuchiy <pakhuchiy@gmail.com>
Signed-off-by: Christopher Li <sparse@chrisli.org>
|
|
In sparse, inline is replacing the function call
expression with a compound statment of inline
function body. During the process, all reference
to the function arguments need to replaced with
calling arguments.
When inlining the case statement. Sparse forgets
to replace the case_label->stmt to the new version.
If it has inner inline function call, it will cause
the orignal copy of the function definition get
modified. That should never happen.
It cause warning error when the inline
function call the second time.
With this change, validations/bug_inline_switch.c
no longer generate warning.
Signed-off-by: Christopher Li <sparse@chrisli.org>
|
|
> Do you want to resend your change which revert the context changes?
> Make it base on Josh's git's tree and I will merge your changes in my
> branch.
Below. Or I can give it to you in git if you prefer. I still think we
should redo this in some form so that annotations with different
contexts can work properly, but I don't have time to take care of it
right now.
johannes
>From ca95b62edf1600a2b55ed9ca0515d049807a84fc Mon Sep 17 00:00:00 2001
From: Johannes Berg <johannes@sipsolutions.net>
Date: Tue, 23 Dec 2008 10:53:19 +0100
Subject: [PATCH] Revert context tracking code
|
|
The sparse man page promises that it will check this:
Functions with the extended attribute
__attribute__((context(expression,in_context,out_context))
require the context expression (for instance, a lock) to have the
value in_context (a constant nonnegative integer) when called,
and return with the value out_context (a constant nonnegative
integer).
It doesn't keep that promise though, nor can it, especially with
contexts that can be acquired recursively (like RCU in the kernel.)
This patch makes sparse track different contexts, and also follows
up on that promise, but with slightly different semantics:
* the "require the context to have the value" is changed to require
it to have /at least/ the value if 'in_context',
* an exact_context(...) attribute is introduced with the previously
described semantics (to be used for non-recursive contexts),
* the __context__ statement is extended to also include a required
context argument (same at least semantics),
Unfortunately, I wasn't able to keep the same output, so now you'll
see different messages from sparse, especially when trying to unlock
a lock that isn't locked you'll see a message pointing to the unlock
function rather than complaining about the basic block, you can see
that in the test suite changes.
This patch also contains test updates and a lot of new tests for the
new functionality. Except for the changed messages, old functionality
should not be affected.
However, the kernel use of __attribute__((context(...)) is actually
wrong, the kernel often does things like:
static void *dev_mc_seq_start(struct seq_file *seq, loff_t * pos)
__acquires(dev_base_lock)
{
[...]
read_lock(&dev_base_lock);
[...]
}
rather than
static void *dev_mc_seq_start(struct seq_file *seq, loff_t * pos)
__acquires(dev_base_lock)
{
[...]
__acquire__(dev_base_lock);
read_lock(&dev_base_lock);
[...]
}
(and possibly more when read_lock() is annotated appropriately, such
as dropping whatever context read_lock() returns to convert the context
to the dev_base_lock one.)
Currently, sparse doesn't care, but if it's going to check the context
of functions contained within another function then we need to put the
actual __acquire__ together with acquiring the context.
The great benefit of this patch is that you can now document at least
some locking assumptions in a machine-readable way:
before:
/* requires mylock held */
static void myfunc(void)
{...}
after:
static void myfunc(void)
__requires(mylock)
{...}
where, for sparse,
#define __requires(x) __attribute__((context(x,1,1)))
Doing so may result in lots of other functions that need to be annoated
along with it because they also have the same locking requirements, but
ultimately sparse can check a lot of locking assumptions that way.
I have already used this patch and identify a number of kernel bugs by
marking things to require certain locks or RCU-protection and checking
sparse output. To do that, you need a few kernel patches which I'll
send separately.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
|
|
Turn FORCE_MOD into storage class specifier (that's how it's
actually used and that makes for much simpler logics).
Introduce explicit EXPR_FORCE_CAST for forced casts; handle it
properly.
Kill the idiocy in get_as() (we end up picking the oddest things
for address space - e.g. if we have int __attribute__((address_space(1))) *p,
we'll get warnings about removal of address space when we do things like
(unsigned short)*p. Fixed. BTW, that had caught a bunch of very odd
bogosities in the kernel and eliminated several false positives in there.
As the result, get_as() is gone now and evaluate_cast() got simpler.
Kill the similar idiocy in handling pointer assignments; while we are at it,
fix the qualifiers check for assignments to/from void * (you can't assign
const int * to void * - qualifiers on the left side should be no less than
on the right one; for normal codepath we get that checked, but the special
case of void * skips these checks).
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
|
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
|
preparation to initializer fixes
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
|
For inline functions, Sparse inlines the function body at evaluation. It is
very hard to find out the original function call. This change preserves the
original call as an annotation.
Signed-Off-By: Christopher Li <sparse@chrisli.org>
|
|
Signed-off-by: Pavel Roskin <proski@gnu.org>
Signed-off-by: Josh Triplett <josh@freedesktop.org>
|
|
With the introduction of __builtin_types_compatible_p, types can now appear as
expressions (underneath an EXPR_COMPARE), so copy_expression needs to handle
EXPR_TYPE; just pass type expressions through without copying, since they
won't change when inlining.
Signed-off-by: Josh Triplett <josh@freedesktop.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
|
|
This removes the list of symbols for block statements, and instead makes
a declaration be a statement of its own.
This is necessary to correctly handle the case of mixed statements and
declarations correctly, since the order of declarations and statements
is meaningful.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
|
|
We need to create a copy of cast_type when we are copying a combination
of EXPR_CAST and EXPR_INITIALIZER (i.e. a compound literal).
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
|
|
Mitesh Shah (and others) report that broken libc's will have their own
"error()" that the sparse naming clashes with.
So use a sed-script to rewrite all the occurrences.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
|
|
This replaceq calls to warning() with error() at places where (I think)
the gcc reports an error. Also added a global variable die_if_error
which is set if there is one or more errors. If someone wants to stop
processing further, can check for the variable.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
|
|
This patch add static declare to make sparse happy of checking itself.
|
|
This test-case shows a bug in initializer expressions in inlining:
struct s {
unsigned a;
unsigned b:1;
};
static inline void bar(void)
{
struct s x = {
.a = 0,
.b = 0,
};
}
void foo(void)
{
bar();
bar();
}
gives:
b.c:9:8: warning: invalid access past the end of 'x' (8 8)
The reason is that we start with
EXPR_INITIALIZER:
EXPR_IDENTIFIER[a]: ea
EXPR_IDENTIFIER[b]: eb
That's what we have in inline tree. First call give us new EXPR_INITIALIZER
containing the same EXPR_IDENTIFIER nodes. Evaluating that gives list with
two expressions - ea and EXPR_POS[4]: eb. EXPR_POS is actually cannibalized
second EXPR_IDENTIFIER. Expanding the results doesn't do anything interesting
(yet).
Second call: that's where the things get ugly. New EXPR_INITIALIZER again,
but now it contains EXPR_POS. And that gives us EXPR_POS[4]: EXPR_POS[4]: eb.
Oops... Expanding that will collapse to single offset of 8. That will be
in the node we'd allocated for the second EXPR_POS, so corruption of the
inline tree won't go any further. It's more than enough already, though...
Obvious solution would be to always copy EXPR_POS and EXPR_IDENTIFIER when
uninlining. Trivial patch follows; unless you see a way to do it smarter...
|
|
|
|
didn't get copied.
Test-case by Sam Ravnborg (inline function with static symbol with
initializer).
|
|
The asm_inputs/outputs "expression list" is not really an
expression list any more: it is a list of "triples", where
the first entry is the identifier name, the second one is
the constraint string, and the third one is the expression.
|
|
Now that we actually save, evaluate and use the asm arguments,
we need to make sure to copy them properly too.
|
|
It's disgusting how intimate lib.c is with all the types,
and this is slowly trying to split things up a bit. Now
the intimate part is in allocate.c, but maybe we can get
to the point where each allocation user just declares its
own allocation strategy, and just uses the generic routines
in allocate.c
|
|
|
|
Duh. We did a "dup_expression()" instead of a "copy_exprssion"
for the sub-expressions of EXPR_INDEX and EXPR_IDENTIFIER. As
a result, we never actually replaced any symbols in there..
|
|
We add a per-symbol pseudo, and just track them that way.
|
|
This also makes our evaluation simplification only happen
for the implied ones. If you put an explicit cast somewhere,
it does _not_ get combined with an implied one.
|
|
Oops. It never mattered before, because the linearized end
result wasn't really used. Now that we check for context,
it does matter. Big way.
|
|
(In practice, it should always be a constant, so it did the
right thing. But maybe it becomes that only after expansion
at the inlining site..)
|
|
returns the size of the underlying object.
This is different from "sizeof(*expr)" for arrays, where
the array would degenerate to a pointer to one member, and
thus "sizeof(*expr)" gives the size of one entry in the
array.
Why do this? It's useful for things like
#define memset(a,b,c) ({ \
(void) __builtin_warning(__sizeof_ptr__(a) > 1, __sizeof_ptr__(a) != (c), "check memset size"); \
memset(a, b, c); })
where we really want to check the size of the object we're
doing the "memset()" on, but the regular sizeof() just doesn't
cut it.
|
|
positional markers be hierarchical rather than a flat list.
This makes the data structure a bit more complex, but it simplifies
some of the code, and makes it possible to evaluate complex initializers
without going insane.
In particular, we how support
struct xxxx var = {
.a.b[10] = 1;
};
which we couldn't handle at all before (it had to
be written as
struct xxxx var = {
.a = {
.b = { [10] = 1; }
}
}
or similar.
The new code changes all array indexes and structure members
to EXPR_POS expressions offset from the "outer" scope (either
start of the symbol, or an outer EXPR_POS).
|
|
warn->warning
error->error_die
new error
lib.h:
warn->warning
error->error_die
new error
Add gcc format checking to warning/error/...
|
|
..and switch us entirely over to the new naming scheme.
All the nasty work of going through the users thanks to Chris Li.
|
|
When we take the address of an inline function or otherwise refusing to
inline it, we need to output the now non-inline function properly.
What we do is
a) keeping body and symbol list of inlined function in new fields
b) when expanding inlined call use these fields
c) when evaluating the function itself (which happens if sparse
decides that it can't be [always] inlined) uninline the sucker. I.e.
create ->stmt and ->symbol_list by copying the ->inline_stmt and
->inline_symbol_list same as we would do while expanding a call.
That guarantees that we won't run into trouble with inlined calls coming
afterwards - evaluation doesn't mangle ->inline_stmt anymore.
|
|
When we copy the body of inlined function, we leave more nodes shared
than we should - they might be mangled by evaluation of copy. Fixed.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
|
|
Handling of non-lvalue compound objects:
We introduce a new primitive - EXPR_SLICE. Meaning is "that many bits
from that offset in that non-lvalue struct or union". It is used when
we try to get a member out of a non-lvalue struct or union (subsequent .<field>
just narrow the slice). And as far as scalar, struct and union fields count,
that's it. The only subtle point is handling of array fields. And there
I'm doing what C99 requires - they *do* decay to real, honest pointers,
causing a copy of object to memory if needed. We get an anonymous object
that lives until the next sequence point; optimizer is certainly free to
get rid of it completely if it can make do with the value we'd copied there.
Note that you _are_ allowed to say
foo().a[1] = 0;
It doesn't make sense, since the value you've assigned will be immediately
lost (and any optimizer will turn that into f()), but it is legitimate and
it avoids a *lot* of PITA in describing semantics.
It covers only array decay - any other member of non-lvalue struct or union
is *not* an lvalue and in
struct foo {int x; int y[2];};
struct foo a(void);
...
a().x = 0; /* not allowed, non-lvalue */
a().y[0] = 1; /* allowed, but pointless */
you will get an error from the first assignment, but not from the second
one.
Signed-off-by: Al Viro <viro@parcelfarce.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
|
|
Ideally we should probably allocate an error code for recursive expand,
so the caller can know that the inline did not expand at all.
But the evaluate_symbol_call don't care about that so I just make it
reutrn 0.
|
|
It's the same as a regular C conditional, except you could
evaluate both sides first. Right now we treat it exactly
the same as an EXPR_CONDITIONAL.
|
|
I really don't know why we didn't do this before. Not
copying all fields is a bug, but maybe there was a reason
that isn't immediately obvious. If so, maybe we'd comment
on it next time.
|
|
Stupid stupid bugs. This makes inlining work a lot better.
|
|
Associate them with each computed goto, and copy them properly
when inlining.
|
|
FP handling added, everything straightforward by now.
|
|
This teaches sparse what __alignof__ really means, instead of just using
the same code as "__sizeof__"
It gets rid of the warnings in ebtables that does:
struct ebt_entries {
...
char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace)));
};
Which caused warning because sparse was evaluating __alignof__ as the same as sizeof,
and sizeof was 57 (ie non-power of 2).
This is just based on the existing code and a peek at the data
structures in expression and symbol.
|
|
Easy enough to do, since these things don't change: just
return the original one.
|
|
Add a few cases for expressions, that showed the things we didn't
handle right.
|
|
isn't the sole copyright owner these days.
|
|
because we didn't add them to the list of statement symbols.
|
|
to get all the symbol replacement stuff right, we just build a
per-function list of all the symbols associated with a function
(except for arguments, which are special anyway due to the
site-specific initializers).
When we inline, we then create the replacement symbols up front,
which makes it trivial to replace them as needed. This means that
we get all the special symbols (return targets etc) automatically
right.
Also fix a few problems with builtin symbols that were uncovered
by the inliner when running over the kernel sources.
|
|
|
|
We still don't copy all symbols correctly, in particular branch
target symbols (ie labels, case targets etc).l
|
|
argument symbols that got corrupted by the incorrect
partial symbol copy when creating the new argument symbols.
|
|
Much still not implemented.
|
|
Warn about internal "not implemented yet" kinds of things only
when verbose. These warnings have nothing to do with the sources
we're checking, they're only good for pointing out limitations
of sparse itself.
|
|
it _should_ be
Make beginnings of a statement copier, to start testing trival
inline function replacement.
The return symbol replacement doesn't work, but the theory is
there.
NOTE! I'm getting more and more convinced that the symbol replacement
is a bad idea, and is should be doable some other way (ie by looking
up symbols at _evaluation_ time rather than having to switch them around
when inlining).
|
|
replace the symbols encounterd durign an expression copy, and
this is the first step showing how.
This doesn't get the multi-level inlining right, we'll get there
some day. Also, it currently complains about subexpressions it can't
copy yet, causing way too much verbosity for the kernel.
|
|
used for inlining. Right now the copying is totally broken, since
we don't actually do any symbol replacement, but hey, we'll get there
some day.
Make "inline_function()" call the new copy_statement() function.
|