diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 2291b733b5893fd168731c45fee6e7c97cbc7c2d..08177c091fa5243e8bb02e1f8bf10878c1044f15 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,11 @@
 2002-12-26  Nathan Sidwell  <nathan@codesourcery.com>
 
+	PR c++/4803
+	* decl2.c (mark_used): Defer inline functions.
+	(finish_file): Merge deferred_fns loops. Check all used
+	inline functions have a definition.
+	* method.c (make_thunk): Thunks are not inline.
+
 	PR c++/5116, c++/764
 	* call.c (build_new_op): Make sure template class operands are
 	instantiated.
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 3d717e8475b94cbea1e70eb03a0ec0be0085d012..1607e379090addb1bf85ca43180451e2aea6cca4 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -2791,12 +2791,13 @@ finish_file ()
 	  reconsider = 1;
 	}
       
-      /* Go through the various inline functions, and see if any need
-	 synthesizing.  */
       for (i = 0; i < deferred_fns_used; ++i)
 	{
 	  tree decl = VARRAY_TREE (deferred_fns, i);
+	  
 	  import_export_decl (decl);
+	  
+	  /* Does it need synthesizing?  */
 	  if (DECL_ARTIFICIAL (decl) && ! DECL_INITIAL (decl)
 	      && TREE_USED (decl)
 	      && (! DECL_REALLY_EXTERN (decl) || DECL_INLINE (decl)))
@@ -2811,30 +2812,21 @@ finish_file ()
 	      pop_from_top_level ();
 	      reconsider = 1;
 	    }
-	}
 
-      /* We lie to the back-end, pretending that some functions are
-	 not defined when they really are.  This keeps these functions
-	 from being put out unnecessarily.  But, we must stop lying
-	 when the functions are referenced, or if they are not comdat
-	 since they need to be put out now.
-	 This is done in a separate for cycle, because if some deferred
-	 function is contained in another deferred function later in
-	 deferred_fns varray, rest_of_compilation would skip this
-	 function and we really cannot expand the same function twice.  */
-      for (i = 0; i < deferred_fns_used; ++i)
-	{
-	  tree decl = VARRAY_TREE (deferred_fns, i);
-      
+	  /* We lie to the back-end, pretending that some functions
+	     are not defined when they really are.  This keeps these
+	     functions from being put out unnecessarily.  But, we must
+	     stop lying when the functions are referenced, or if they
+	     are not comdat since they need to be put out now.  This
+	     is done in a separate for cycle, because if some deferred
+	     function is contained in another deferred function later
+	     in deferred_fns varray, rest_of_compilation would skip
+	     this function and we really cannot expand the same
+	     function twice.  */
 	  if (DECL_NOT_REALLY_EXTERN (decl)
 	      && DECL_INITIAL (decl)
 	      && DECL_NEEDED_P (decl))
 	    DECL_EXTERNAL (decl) = 0;
-	}
-
-      for (i = 0; i < deferred_fns_used; ++i)
-	{
-	  tree decl = VARRAY_TREE (deferred_fns, i);
 
 	  /* If we're going to need to write this function out, and
 	     there's already a body for it, create RTL for it now.
@@ -2890,6 +2882,16 @@ finish_file ()
     } 
   while (reconsider);
 
+  /* All used inline functions must have a definition at this point. */
+  for (i = 0; i < deferred_fns_used; ++i)
+    {
+      tree decl = VARRAY_TREE (deferred_fns, i);
+
+      if (TREE_USED (decl) && DECL_DECLARED_INLINE_P (decl)
+	  && !(TREE_ASM_WRITTEN (decl) || DECL_SAVED_TREE (decl)))
+	cp_warning_at ("inline function `%D' used but never defined", decl);
+    }
+  
   /* We give C linkage to static constructors and destructors.  */
   push_lang_context (lang_name_c);
 
@@ -4665,6 +4667,12 @@ mark_used (decl)
   TREE_USED (decl) = 1;
   if (processing_template_decl)
     return;
+
+  if (TREE_CODE (decl) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (decl)
+      && !TREE_ASM_WRITTEN (decl))
+    /* Remember it, so we can check it was defined.  */
+    defer_fn (decl);
+  
   if (!skip_evaluation)
     assemble_external (decl);
 
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 12a66f6526d6faf5d8a2a0d2406e58aeed5822cd..e5a3bd974fb3008a42f4c4e5df41789fb5411459 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -344,6 +344,8 @@ make_thunk (tree function, bool this_adjusting,
   DECL_NO_STATIC_CHAIN (thunk) = 1;
   /* The THUNK is not a pending inline, even if the FUNCTION is.  */
   DECL_PENDING_INLINE_P (thunk) = 0;
+  DECL_INLINE (thunk) = 0;
+  DECL_DECLARED_INLINE_P (thunk) = 0;
   /* Nor has it been deferred.  */
   DECL_DEFERRED_FN (thunk) = 0;
   /* Add it to the list of thunks associated with FUNCTION.  */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index a68ab21957b7fd06baa06f566d0a06b6750ca151..c54c1c2c1c0baab9295199337dc3ad29cedff226 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,11 @@
 2002-12-26  Nathan Sidwell  <nathan@codesourcery.com>
 
+	* g++.dg/warn/inline1.C: New test.
+	* g++.old-deja/g++.brendan/crash64.C: Remove spurious inlines.
+	* g++.old-deja/g++.jason/synth10.C: Likewise.
+	* g++.old-deja/g++.mike/net31.C: Likewise.
+	* g++.old-deja/g++.mike/p8786.C: Likewise.
+
 	* g++.dg/template/friend10.C: New test.
 	* g++.dg/template/conv5.C: New test.
 
diff --git a/gcc/testsuite/g++.dg/warn/inline1.C b/gcc/testsuite/g++.dg/warn/inline1.C
new file mode 100644
index 0000000000000000000000000000000000000000..24836e744ebbff96e77af0f8720cc8096a0c03b1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/inline1.C
@@ -0,0 +1,23 @@
+// { dg-do compile }
+
+// Copyright (C) 2002 Free Software Foundation, Inc.
+// Contributed by Nathan Sidwell 26 Dec 2002 <nathan@codesourcery.com>
+
+// PR 4803. Used inline functions must have a definition.
+
+inline void Foo1 ();  // { dg-warning "inline function" "" }
+inline void Bar1 ();
+template <typename T> inline void Foo2(T);   // { dg-warning "inline function" "" }
+template <typename T> inline void Bar2(T);
+
+void Baz ()
+{
+  Foo1 ();
+  Foo2 (1);
+
+  Bar1 ();
+  Bar2 (1);
+}
+
+inline void Bar1 () {}
+template <typename T> inline void Bar2(T) {}
diff --git a/gcc/testsuite/g++.old-deja/g++.brendan/crash64.C b/gcc/testsuite/g++.old-deja/g++.brendan/crash64.C
index ccba22e245a975824f29470012e43495999b2a64..6c2825802f4634cca8575046dfa33c8859e0c697 100644
--- a/gcc/testsuite/g++.old-deja/g++.brendan/crash64.C
+++ b/gcc/testsuite/g++.old-deja/g++.brendan/crash64.C
@@ -4,8 +4,8 @@ typedef long unsigned int size_t;
 typedef void (*RF_Ptr)(void *);
 
 struct _im_pers_mem_spec {
-  inline _im_pers_mem_spec(void );
-  inline _im_pers_mem_spec(auto int of, auto int n);
+  _im_pers_mem_spec(void );
+  _im_pers_mem_spec(auto int of, auto int n);
 };
 
 struct _type_desc {
diff --git a/gcc/testsuite/g++.old-deja/g++.jason/synth10.C b/gcc/testsuite/g++.old-deja/g++.jason/synth10.C
index d94e6bdebe962ae71360c0b78464c1b0f1f624bc..07a372586c01dfffa17d98982e0545ff41abf356 100644
--- a/gcc/testsuite/g++.old-deja/g++.jason/synth10.C
+++ b/gcc/testsuite/g++.old-deja/g++.jason/synth10.C
@@ -7,14 +7,14 @@ class A;
 class AH
 {
   public:
-    inline  AH (   A * p = 0 );
+    AH (   A * p = 0 );
     AH ( const  AH & from )
     : pointer( from.pointer )   { inc(); }
     ~ AH ()  { dec(); }
   private:
     A * pointer;
-    inline void inc() const;
-    inline void dec() const;
+    void inc() const;
+    void dec() const;
 };
 
 class A 
diff --git a/gcc/testsuite/g++.old-deja/g++.mike/net31.C b/gcc/testsuite/g++.old-deja/g++.mike/net31.C
index 3ecf81c734919f5b81a199c4a4b6460b834dc362..05617a8b4ffdf7b097d563472a2cae6163e6f616 100644
--- a/gcc/testsuite/g++.old-deja/g++.mike/net31.C
+++ b/gcc/testsuite/g++.old-deja/g++.mike/net31.C
@@ -13,7 +13,7 @@ class foo_b {
         foo_b();
         ~foo_b();
         foo_b(const foo_b&);
-        inline double& operator()(int);
+        double& operator()(int);
         foo_b& operator=(foo_b&);
         void bar_a(int);
 };
diff --git a/gcc/testsuite/g++.old-deja/g++.mike/p8786.C b/gcc/testsuite/g++.old-deja/g++.mike/p8786.C
index b5443c0fa6ba54ff4fbc00672b3c669cf5107b28..0565fb2558a129f40cd79aa6441356fb3cbc83ee 100644
--- a/gcc/testsuite/g++.old-deja/g++.mike/p8786.C
+++ b/gcc/testsuite/g++.old-deja/g++.mike/p8786.C
@@ -4,7 +4,7 @@
 
 class B {
 public:
-  inline ~B();
+  ~B();
 };
 
 class D : public B {