public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
From: Martin Sebor <msebor@gmail.com>
To: Florian Weimer <fweimer@redhat.com>
Cc: Martin Sebor via Libc-alpha <libc-alpha@sourceware.org>,
	Joseph Myers <joseph@codesourcery.com>
Subject: Re: [PATCH] add support for -Wmismatched-dealloc
Date: Mon, 4 Jan 2021 16:18:07 -0700	[thread overview]
Message-ID: <ee04d104-e46e-1501-ce96-ee7f9b3fd322@gmail.com> (raw)
In-Reply-To: <87turwiqqw.fsf@oldenburg2.str.redhat.com>

[-- Attachment #1: Type: text/plain, Size: 4267 bytes --]

On 1/4/21 9:57 AM, Florian Weimer wrote:
> * Martin Sebor:
> 
>> On 1/4/21 9:07 AM, Florian Weimer wrote:
>>> * Martin Sebor via Libc-alpha:
>>>
>>>> diff --git a/wcsmbs/wchar.h b/wcsmbs/wchar.h
>>>> index 9cf8b05a87..4c1c7f1119 100644
>>>> --- a/wcsmbs/wchar.h
>>>> +++ b/wcsmbs/wchar.h
>>>> @@ -151,7 +151,8 @@ extern size_t wcsxfrm_l (wchar_t *__s1, const wchar_t *__s2,
>>>>    			 size_t __n, locale_t __loc) __THROW;
>>>>      /* Duplicate S, returning an identical malloc'd string.  */
>>>> -extern wchar_t *wcsdup (const wchar_t *__s) __THROW __attribute_malloc__;
>>>> +extern wchar_t *wcsdup (const wchar_t *__s) __THROW
>>>> +  __attribute_malloc__ __attr_dealloc_free;
>>>>    #endif
>>>>      /* Find the first occurrence of WC in WCS.  */
>>>> @@ -562,9 +563,18 @@ extern wchar_t *wcpncpy (wchar_t *__restrict __dest,
>>>>    /* Wide character I/O functions.  */
>>>>      #if defined __USE_XOPEN2K8 || __GLIBC_USE (LIB_EXT2)
>>>> +#  ifdef __REDIRECT
>>>> +/* Declare the __fclose alias and associate it as a deallocator
>>>> +   with open_wmemstream below.  */
>>>> +extern int __REDIRECT (__fclose, (FILE *), fclose);
>>>> +#    define __attr_dealloc_fclose __attr_dealloc (__fclose, 1)
>>>> +#  else
>>>> +#    define __attr_dealloc_fclose /* empty */
>>>> +#  endif
>>>>    /* Like OPEN_MEMSTREAM, but the stream is wide oriented and produces
>>>>       a wide character string.  */
>>>> -extern __FILE *open_wmemstream (wchar_t **__bufloc, size_t *__sizeloc) __THROW;
>>>> +extern __FILE *open_wmemstream (wchar_t **__bufloc, size_t *__sizeloc) __THROW
>>>> +  __attribute_malloc__ __attr_dealloc_fclose;
>>>>    #endif
>>>>      #if defined __USE_ISOC95 || defined __USE_UNIX98
>>> Why is an alias for fclose needed here, but not for free?
>>
>> Because fclose is not a built-in so there's no __builtin_fclose
>> to associate open_wmemstream with.  free is a built-in and so
>> __attr_dealloc_free just references __builtin_free and doesn't
>> need an explicit declaration.
> 
> Ahh, that explains the discrepancy.
> 
> I'm a bit worried that the __fclose alias causes problems.  Would it be
> possible to add __builtin_fclose to GCC instead?

I don't like the alias hack either.  Adding a built-in is possible
but it's late in GCC stage 3 and I'm doubtful the change would be
accepted before the Glibc deadline (that's this week, right?)

The alias isn't necessary in <stdio.h> where fclose is declared
so I've removed it from there.  It would have been only marginally
useful in <wchar.h>, and only when fclose isn't declared, but it's
probably best avoided.  So one possibility is to prepare the header
for __builtin_fclose if/when it's added, fall back on fclose when
it's declared (i.e., when <stdio.h> is included), and do nothing
otherwise (and accept that calling, say free, on a pointer returned
from open_wmemstteam, will not be diagnosed in those translation
units).

Attached is a patch that does that.  If you want to change it
to something else (e.g, forget about open_wmemstream altogether
for now or conditionally declare it in <stdio.h> when <wchar.h>
is included, or any other viable alternative) please let me know.

> Based on how this patch appears to make both __fclose and fclose
> acceptable as a deallocator, GCC resolves redirects as part of the
> matching check.  I wonder if this constrains the usefulness of the
> attribute in some way.  I can imagine situations where at the source
> level, different deallocators should be used (say to support debugging
> builds), but release builds redirect different deallocators to the same
> implementation.

The attribute doesn't do anything special with aliases (it was
just a way to get around the problem with functions not being
declared in some headers).

As for different configurations, the attribute is designed for
standard C/POSIX APIs and others like those.  A user-defined API
with different deallocators in one configuration than in another
has to create the associations conditionally, based on those
configurations.  But there's no way to change these associations
for GCC built-ins, just like there's no way to change their
semantics.  They're hardwired into GCC and the only way to
affect them is to disable the built-ins.

Martin

> 
> Thanks,
> Florian
> 


[-- Attachment #2: glibc-attr-malloc.diff --]
[-- Type: text/x-patch, Size: 16904 bytes --]

diff --git a/include/programs/xmalloc.h b/include/programs/xmalloc.h
index fd6cf61730..2a8d6ff23c 100644
--- a/include/programs/xmalloc.h
+++ b/include/programs/xmalloc.h
@@ -23,11 +23,11 @@
 
 /* Prototypes for a few program-wide used functions.  */
 extern void *xmalloc (size_t n)
-  __attribute_malloc__ __attribute_alloc_size__ ((1));
+  __attribute_malloc__ __attribute_alloc_size__ ((1)) __attr_dealloc_free;
 extern void *xcalloc (size_t n, size_t s)
-  __attribute_malloc__ __attribute_alloc_size__ ((1, 2));
+  __attribute_malloc__ __attribute_alloc_size__ ((1, 2)) __attr_dealloc_free;
 extern void *xrealloc (void *o, size_t n)
-  __attribute_malloc__ __attribute_alloc_size__ ((2));
-extern char *xstrdup (const char *) __attribute_malloc__;
+  __attribute_malloc__ __attribute_alloc_size__ ((2)) __attr_dealloc_free;
+extern char *xstrdup (const char *) __attribute_malloc__ __attr_dealloc_free;
 
 #endif /* xmalloc.h */
diff --git a/libio/Makefile b/libio/Makefile
index 276baf58ac..0f1f536f7a 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -61,7 +61,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \
 	bug-ungetc2 bug-ftell bug-ungetc3 bug-ungetc4 tst-fopenloc2 \
 	tst-memstream1 tst-memstream2 tst-memstream3 tst-memstream4 \
 	tst-wmemstream1 tst-wmemstream2 tst-wmemstream3 tst-wmemstream4 \
-	bug-memstream1 bug-wmemstream1 \
+	tst-wmemstream5 bug-memstream1 bug-wmemstream1 \
 	tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \
 	tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \
 	tst-ftell-append tst-fputws tst-bz22415 tst-fgetc-after-eof \
diff --git a/libio/stdio.h b/libio/stdio.h
index 998470943e..f27d526f44 100644
--- a/libio/stdio.h
+++ b/libio/stdio.h
@@ -165,22 +165,34 @@ extern int renameat2 (int __oldfd, const char *__old, int __newfd,
 		      const char *__new, unsigned int __flags) __THROW;
 #endif
 
+/* Close STREAM.
+
+   This function is a possible cancellation point and therefore not
+   marked with __THROW.  */
+extern int fclose (FILE *__stream);
+
+#undef __attr_dealloc_fclose
+#define __attr_dealloc_fclose __attr_dealloc (fclose, 1)
+
 /* Create a temporary file and open it read/write.
 
    This function is a possible cancellation point and therefore not
    marked with __THROW.  */
 #ifndef __USE_FILE_OFFSET64
-extern FILE *tmpfile (void) __wur;
+extern FILE *tmpfile (void)
+  __attribute_malloc__ __attr_dealloc_fclose __wur;
 #else
 # ifdef __REDIRECT
-extern FILE *__REDIRECT (tmpfile, (void), tmpfile64) __wur;
+extern FILE *__REDIRECT (tmpfile, (void), tmpfile64)
+  __attribute_malloc__ __attr_dealloc_fclose __wur;
 # else
 #  define tmpfile tmpfile64
 # endif
 #endif
 
 #ifdef __USE_LARGEFILE64
-extern FILE *tmpfile64 (void) __wur;
+extern FILE *tmpfile64 (void)
+   __attribute_malloc__ __attr_dealloc_fclose __wur;
 #endif
 
 /* Generate a temporary filename.  */
@@ -202,15 +214,9 @@ extern char *tmpnam_r (char *__s) __THROW __wur;
    P_tmpdir is tried and finally "/tmp".  The storage for the filename
    is allocated by `malloc'.  */
 extern char *tempnam (const char *__dir, const char *__pfx)
-     __THROW __attribute_malloc__ __wur;
+  __attribute_malloc__ __wur __attr_dealloc_free __THROW;
 #endif
 
-
-/* Close STREAM.
-
-   This function is a possible cancellation point and therefore not
-   marked with __THROW.  */
-extern int fclose (FILE *__stream);
 /* Flush STREAM, or all streams if STREAM is NULL.
 
    This function is a possible cancellation point and therefore not
@@ -244,7 +250,8 @@ extern int fcloseall (void);
    This function is a possible cancellation point and therefore not
    marked with __THROW.  */
 extern FILE *fopen (const char *__restrict __filename,
-		    const char *__restrict __modes) __wur;
+		    const char *__restrict __modes)
+  __attribute_malloc__ __attr_dealloc_fclose __wur;
 /* Open a file, replacing an existing stream with it.
 
    This function is a possible cancellation point and therefore not
@@ -256,7 +263,7 @@ extern FILE *freopen (const char *__restrict __filename,
 # ifdef __REDIRECT
 extern FILE *__REDIRECT (fopen, (const char *__restrict __filename,
 				 const char *__restrict __modes), fopen64)
-  __wur;
+  __attribute_malloc__ __attr_dealloc_fclose __wur;
 extern FILE *__REDIRECT (freopen, (const char *__restrict __filename,
 				   const char *__restrict __modes,
 				   FILE *__restrict __stream), freopen64)
@@ -268,7 +275,8 @@ extern FILE *__REDIRECT (freopen, (const char *__restrict __filename,
 #endif
 #ifdef __USE_LARGEFILE64
 extern FILE *fopen64 (const char *__restrict __filename,
-		      const char *__restrict __modes) __wur;
+		      const char *__restrict __modes)
+  __attribute_malloc__ __attr_dealloc_fclose __wur;
 extern FILE *freopen64 (const char *__restrict __filename,
 			const char *__restrict __modes,
 			FILE *__restrict __stream) __wur;
@@ -276,7 +284,8 @@ extern FILE *freopen64 (const char *__restrict __filename,
 
 #ifdef	__USE_POSIX
 /* Create a new stream that refers to an existing system file descriptor.  */
-extern FILE *fdopen (int __fd, const char *__modes) __THROW __wur;
+extern FILE *fdopen (int __fd, const char *__modes) __THROW
+  __attribute_malloc__ __attr_dealloc_fclose __wur;
 #endif
 
 #ifdef	__USE_GNU
@@ -284,18 +293,20 @@ extern FILE *fdopen (int __fd, const char *__modes) __THROW __wur;
    and uses the given functions for input and output.  */
 extern FILE *fopencookie (void *__restrict __magic_cookie,
 			  const char *__restrict __modes,
-			  cookie_io_functions_t __io_funcs) __THROW __wur;
+			  cookie_io_functions_t __io_funcs) __THROW
+  __attribute_malloc__ __attr_dealloc_fclose __wur;
 #endif
 
 #if defined __USE_XOPEN2K8 || __GLIBC_USE (LIB_EXT2)
 /* Create a new stream that refers to a memory buffer.  */
 extern FILE *fmemopen (void *__s, size_t __len, const char *__modes)
-  __THROW __wur;
+  __THROW __attribute_malloc__ __attr_dealloc_fclose __wur;
 
 /* Open a stream that writes into a malloc'd buffer that is expanded as
    necessary.  *BUFLOC and *SIZELOC are updated with the buffer's location
    and the number of characters written on fflush or fclose.  */
-extern FILE *open_memstream (char **__bufloc, size_t *__sizeloc) __THROW __wur;
+extern FILE *open_memstream (char **__bufloc, size_t *__sizeloc) __THROW
+  __attribute_malloc__ __attr_dealloc_fclose __wur;
 #endif
 
 
@@ -792,17 +803,19 @@ extern int fileno_unlocked (FILE *__stream) __THROW __wur;
 
 
 #ifdef __USE_POSIX2
-/* Create a new stream connected to a pipe running the given command.
+/* Close a stream opened by popen and return the status of its child.
 
    This function is a possible cancellation point and therefore not
    marked with __THROW.  */
-extern FILE *popen (const char *__command, const char *__modes) __wur;
+extern int pclose (FILE *__stream);
 
-/* Close a stream opened by popen and return the status of its child.
+/* Create a new stream connected to a pipe running the given command.
 
    This function is a possible cancellation point and therefore not
    marked with __THROW.  */
-extern int pclose (FILE *__stream);
+extern FILE *popen (const char *__command, const char *__modes)
+  __attribute_malloc__ __attr_dealloc (pclose, 1) __wur;
+
 #endif
 
 
diff --git a/libio/tst-freopen.c b/libio/tst-freopen.c
index baa13166fe..c4df29d171 100644
--- a/libio/tst-freopen.c
+++ b/libio/tst-freopen.c
@@ -81,6 +81,36 @@ do_test_basic (void)
   fclose (f);
 }
 
+#if defined __GNUC__ && __GNUC__ >= 11
+#pragma GCC diagnostic push
+#pragma GCC diagnostic error "-Wmismatched-dealloc"
+#endif
+
+/* Verify that freopen returns stream.  */
+static void
+do_test_return_stream (void)
+{
+  FILE *f1 = fopen (name, "r");
+  if (f1 == NULL)
+    FAIL_EXIT1 ("fopen: %m");
+
+  FILE *f2 = freopen (name, "r+", f1);
+  if (f2 == NULL)
+    FAIL_EXIT1 ("freopen: %m");
+
+  /* Verify that freopen isn't declared with attribute malloc
+     (which would let GCC fold the inequality to false).  */
+  if (f1 != f2)
+    FAIL_EXIT1 ("freopen returned a different stream");
+
+  /* This shouldn't trigger -Wmismatched-dealloc.  */
+  fclose (f1);
+}
+
+#if defined __GNUC__ && __GNUC__ >= 11
+# pragma GCC diagnostic pop
+#endif
+
 /* Test for BZ#21398, where it tries to freopen stdio after the close
    of its file descriptor.  */
 static void
@@ -105,6 +135,7 @@ do_test (void)
 {
   do_test_basic ();
   do_test_bz21398 ();
+  do_test_return_stream ();
 
   return 0;
 }
diff --git a/libio/tst-popen1.c b/libio/tst-popen1.c
index bae6615b9b..9f36b20090 100644
--- a/libio/tst-popen1.c
+++ b/libio/tst-popen1.c
@@ -21,7 +21,7 @@ do_test (void)
 	  res = 1;
 	}
 
-      fclose (fp);
+      pclose (fp);
     }
 
   fp = popen ("echo hello", "re");
@@ -39,7 +39,7 @@ do_test (void)
 	  res = 1;
 	}
 
-      fclose (fp);
+      pclose (fp);
     }
 
   return res;
diff --git a/libio/tst-wmemstream1.c b/libio/tst-wmemstream1.c
index f8b308bc6c..8a0e253862 100644
--- a/libio/tst-wmemstream1.c
+++ b/libio/tst-wmemstream1.c
@@ -1,5 +1,40 @@
 #include <wchar.h>
 
+/* Verify that calling fclose on the result of open_wmemstream doesn't
+   trigger GCC -Wmismatched-dealloc with fclose forward-declared and
+   without <stdio.h> included first (it is included later, in.
+   "tst-memstream1.c").  */
+
+extern int fclose (FILE*);
+
+#if defined __GNUC__ && __GNUC__ >= 11
+#pragma GCC diagnostic push
+#pragma GCC diagnostic error "-Wmismatched-dealloc"
+#endif
+
+int test_open_wmemstream_no_stdio (void)
+{
+  {
+    wchar_t *buf;
+    size_t size;
+    FILE *f = open_wmemstream (&buf, &size);
+    fclose (f);
+  }
+
+  {
+    FILE* (*pf)(wchar_t**, size_t*) = open_wmemstream;
+    wchar_t *buf;
+    size_t size;
+    FILE *f = pf (&buf, &size);
+    fclose (f);
+  }
+  return 0;
+}
+
+#if defined __GNUC__ && __GNUC__ >= 11
+# pragma GCC diagnostic pop
+#endif
+
 #define CHAR_T wchar_t
 #define W(o) L##o
 #define OPEN_MEMSTREAM open_wmemstream
diff --git a/libio/tst-wmemstream5.c b/libio/tst-wmemstream5.c
new file mode 100644
index 0000000000..64461dbe48
--- /dev/null
+++ b/libio/tst-wmemstream5.c
@@ -0,0 +1,40 @@
+#include <wchar.h>
+
+/* Verify that calling fclose on the result of open_wmemstream doesn't
+   trigger GCC -Wmismatched-dealloc with fclose forward-declared and
+   without <stdio.h> included in the same translation unit.  */
+
+extern int fclose (FILE*);
+
+#if defined __GNUC__ && __GNUC__ >= 11
+#pragma GCC diagnostic push
+#pragma GCC diagnostic error "-Wmismatched-dealloc"
+#endif
+
+static int
+do_test (void)
+{
+  {
+    wchar_t *buf;
+    size_t size;
+    FILE *f = open_wmemstream (&buf, &size);
+    fclose (f);
+  }
+
+  {
+    FILE* (*pf)(wchar_t**, size_t*) = open_wmemstream;
+    wchar_t *buf;
+    size_t size;
+    FILE *f = pf (&buf, &size);
+    fclose (f);
+  }
+
+  return 0;
+}
+
+#if defined __GNUC__ && __GNUC__ >= 11
+# pragma GCC diagnostic pop
+#endif
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/malloc/malloc.h b/malloc/malloc.h
index b2371f7704..d9dc359559 100644
--- a/malloc/malloc.h
+++ b/malloc/malloc.h
@@ -56,22 +56,25 @@ __THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2));
    the same pointer that was passed to it, aliasing needs to be allowed
    between objects pointed by the old and new pointers.  */
 extern void *reallocarray (void *__ptr, size_t __nmemb, size_t __size)
-__THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2, 3));
+  __THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2, 3))
+  __attr_dealloc_free;
 
 /* Free a block allocated by `malloc', `realloc' or `calloc'.  */
 extern void free (void *__ptr) __THROW;
 
 /* Allocate SIZE bytes allocated to ALIGNMENT bytes.  */
 extern void *memalign (size_t __alignment, size_t __size)
-__THROW __attribute_malloc__ __attribute_alloc_size__ ((2)) __wur;
+  __THROW __attribute_malloc__ __attribute_alloc_size__ ((2)) __wur
+  __attr_dealloc_free;
 
 /* Allocate SIZE bytes on a page boundary.  */
 extern void *valloc (size_t __size) __THROW __attribute_malloc__
-     __attribute_alloc_size__ ((1)) __wur;
+     __attribute_alloc_size__ ((1)) __wur __attr_dealloc_free;
 
 /* Equivalent to valloc(minimum-page-that-holds(n)), that is, round up
    __size to nearest pagesize. */
-extern void *pvalloc (size_t __size) __THROW __attribute_malloc__ __wur;
+extern void *pvalloc (size_t __size) __THROW __attribute_malloc__
+  __wur __attr_dealloc_free;
 
 /* Underlying allocation function; successive calls should return
    contiguous pieces of memory.  */
diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
index 5fb6e309be..21e6177fac 100644
--- a/misc/sys/cdefs.h
+++ b/misc/sys/cdefs.h
@@ -569,6 +569,17 @@ _Static_assert (0, "IEEE 128-bits long double requires redirection on this platf
 #  define __attr_access(x)
 #endif
 
+#if __GNUC_PREREQ (11, 0)
+/* Designates dealloc as a function to call to deallocate objects
+   allocated by the declared function.  */
+# define __attr_dealloc(dealloc, argno) \
+    __attribute__ ((__malloc__ (dealloc, argno)))
+# define __attr_dealloc_free __attr_dealloc (__builtin_free, 1)
+#else
+# define __attr_dealloc(x)
+# define __attr_dealloc_free
+#endif
+
 /* Specify that a function such as setjmp or vfork may return
    twice.  */
 #if __GNUC_PREREQ (4, 1)
diff --git a/stdlib/stdlib.h b/stdlib/stdlib.h
index 3aa27a9d25..f88f8e55b3 100644
--- a/stdlib/stdlib.h
+++ b/stdlib/stdlib.h
@@ -550,6 +550,9 @@ extern void *calloc (size_t __nmemb, size_t __size)
 extern void *realloc (void *__ptr, size_t __size)
      __THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2));
 
+/* Free a block allocated by `malloc', `realloc' or `calloc'.  */
+extern void free (void *__ptr) __THROW;
+
 #ifdef __USE_MISC
 /* Re-allocate the previously allocated block in PTR, making the new
    block large enough for NMEMB elements of SIZE bytes each.  */
@@ -558,11 +561,13 @@ extern void *realloc (void *__ptr, size_t __size)
    between objects pointed by the old and new pointers.  */
 extern void *reallocarray (void *__ptr, size_t __nmemb, size_t __size)
      __THROW __attribute_warn_unused_result__
-     __attribute_alloc_size__ ((2, 3));
-#endif
+     __attribute_alloc_size__ ((2, 3))
+    __attr_dealloc_free;
 
-/* Free a block allocated by `malloc', `realloc' or `calloc'.  */
-extern void free (void *__ptr) __THROW;
+/* Add reallocarray as its own deallocator.  */
+extern void *reallocarray (void *__ptr, size_t __nmemb, size_t __size)
+     __attr_dealloc (reallocarray, 1);
+#endif
 
 #ifdef __USE_MISC
 # include <alloca.h>
@@ -788,7 +793,8 @@ extern int system (const char *__command) __wur;
 /* Return a malloc'd string containing the canonical absolute name of the
    existing named file.  */
 extern char *canonicalize_file_name (const char *__name)
-     __THROW __nonnull ((1)) __wur;
+     __THROW __nonnull ((1)) __attribute_malloc__
+     __attr_dealloc_free __wur;
 #endif
 
 #if defined __USE_MISC || defined __USE_XOPEN_EXTENDED
@@ -798,7 +804,8 @@ extern char *canonicalize_file_name (const char *__name)
    ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars,
    returns the name in RESOLVED.  */
 extern char *realpath (const char *__restrict __name,
-		       char *__restrict __resolved) __THROW __wur;
+		       char *__restrict __resolved) __THROW
+     __attr_dealloc_free __wur;
 #endif
 
 
diff --git a/wcsmbs/wchar.h b/wcsmbs/wchar.h
index 9cf8b05a87..e31734158c 100644
--- a/wcsmbs/wchar.h
+++ b/wcsmbs/wchar.h
@@ -151,7 +151,8 @@ extern size_t wcsxfrm_l (wchar_t *__s1, const wchar_t *__s2,
 			 size_t __n, locale_t __loc) __THROW;
 
 /* Duplicate S, returning an identical malloc'd string.  */
-extern wchar_t *wcsdup (const wchar_t *__s) __THROW __attribute_malloc__;
+extern wchar_t *wcsdup (const wchar_t *__s) __THROW
+  __attribute_malloc__ __attr_dealloc_free;
 #endif
 
 /* Find the first occurrence of WC in WCS.  */
@@ -562,9 +563,23 @@ extern wchar_t *wcpncpy (wchar_t *__restrict __dest,
 /* Wide character I/O functions.  */
 
 #if defined __USE_XOPEN2K8 || __GLIBC_USE (LIB_EXT2)
+# ifndef __attr_dealloc_fclose
+#   if defined __has_builtin
+#     if __has_builtin (__builtin_fclose)
+/* If the attribute macro hasn't been defined yet (by <stdio.h>) and
+   fclose is a built-in, use it.  */
+#      define __attr_dealloc_fclose __attr_dealloc (__builtin_fclose, 1)
+#     endif
+#   endif
+# endif
+# ifndef __attr_dealloc_fclose
+#  define __attr_dealloc_fclose /* empty */
+# endif
+
 /* Like OPEN_MEMSTREAM, but the stream is wide oriented and produces
    a wide character string.  */
-extern __FILE *open_wmemstream (wchar_t **__bufloc, size_t *__sizeloc) __THROW;
+extern __FILE *open_wmemstream (wchar_t **__bufloc, size_t *__sizeloc) __THROW
+  __attribute_malloc__ __attr_dealloc_fclose;
 #endif
 
 #if defined __USE_ISOC95 || defined __USE_UNIX98

  reply	other threads:[~2021-01-04 23:18 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-08 22:52 Martin Sebor
2020-12-09  0:07 ` Joseph Myers
2020-12-12  2:25   ` Martin Sebor
2020-12-14 21:39     ` Martin Sebor
2020-12-14 22:16       ` Florian Weimer
2020-12-15  1:01       ` Joseph Myers
2020-12-15 16:52         ` Martin Sebor
2020-12-27 23:13           ` Martin Sebor
2021-01-04 15:56             ` Ping: " Martin Sebor
2021-01-04 16:07             ` Florian Weimer
2021-01-04 16:18               ` Martin Sebor
2021-01-04 16:57                 ` Florian Weimer
2021-01-04 23:18                   ` Martin Sebor [this message]
2021-01-10 20:42                     ` Ping: " Martin Sebor
2021-01-11  9:13                     ` Florian Weimer
2021-01-12  0:00                       ` Martin Sebor
2021-01-12  0:01                       ` Martin Sebor
2021-01-12  8:59                         ` Florian Weimer
2021-01-19  1:08                           ` Martin Sebor
2021-01-19 16:54                           ` David Malcolm
2021-01-22 21:26                         ` DJ Delorie
2021-01-25 10:56                         ` Florian Weimer
2021-01-25 11:31                         ` Florian Weimer
2021-04-23  0:00                         ` Martin Sebor
2021-05-06 23:54                           ` Martin Sebor
2021-05-13 21:49                             ` Martin Sebor
2021-05-16 21:25                               ` Martin Sebor

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ee04d104-e46e-1501-ce96-ee7f9b3fd322@gmail.com \
    --to=msebor@gmail.com \
    --cc=fweimer@redhat.com \
    --cc=joseph@codesourcery.com \
    --cc=libc-alpha@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).